summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author(no author) <(no author)@unknown>2001-05-04 21:54:26 +0000
committer(no author) <(no author)@unknown>2001-05-04 21:54:26 +0000
commit2bab27f55b4766eadef25eb7b526ab463620b96d (patch)
tree7869dca81a6d93214c84f1d7bee2c8a8e7d89d88
parent9003fde19f9d9aefafa4dde58077596b5b807507 (diff)
downloadhttpd-2bab27f55b4766eadef25eb7b526ab463620b96d.tar.gz
This commit was manufactured by cvs2svn to create branch 'avendor'.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/avendor@88990 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--.cvsignore8
-rw-r--r--ABOUT_APACHE266
-rw-r--r--CHANGES8722
-rw-r--r--INSTALL449
-rw-r--r--README72
-rwxr-xr-xbuild/bsd_makefile71
-rwxr-xr-xbuild/buildinfo.sh160
-rw-r--r--build/default.pl496
-rwxr-xr-xbuild/httpd_roll_release115
-rwxr-xr-xbuild/sysv_makefile71
-rw-r--r--config.layout251
-rw-r--r--docs/STATUS76
-rwxr-xr-xdocs/conf/highperformance-std.conf63
-rw-r--r--docs/docroot/apache_pb2.gifbin0 -> 2414 bytes
-rw-r--r--docs/docroot/apache_pb2_ani.gifbin0 -> 2160 bytes
-rw-r--r--docs/docroot/index.html.el37
-rw-r--r--docs/doxygen.conf13
-rw-r--r--docs/icons/apache_pb2.gifbin0 -> 2414 bytes
-rw-r--r--docs/icons/apache_pb2_ani.gifbin0 -> 2160 bytes
-rw-r--r--docs/manual/configuring.html169
-rw-r--r--docs/manual/configuring.html.en169
-rw-r--r--docs/manual/configuring.html.ja.jis247
-rw-r--r--docs/manual/developer/debugging.html175
-rw-r--r--docs/manual/filter.html61
-rw-r--r--docs/manual/filter.html.en61
-rw-r--r--docs/manual/handler.html.ja.jis146
-rw-r--r--docs/manual/howto/cgi.html499
-rw-r--r--docs/manual/howto/cgi.html.en499
-rw-r--r--docs/manual/howto/cgi.html.ja.jis495
-rw-r--r--docs/manual/howto/ssi.html519
-rw-r--r--docs/manual/howto/ssi.html.en519
-rw-r--r--docs/manual/howto/ssi.html.ja.jis501
-rw-r--r--docs/manual/images/apache_header.gifbin0 -> 4084 bytes
-rw-r--r--docs/manual/images/pixel.gifbin0 -> 61 bytes
-rw-r--r--docs/manual/index.html.ja.jis179
-rw-r--r--docs/manual/mod/mod_charset_lite.html227
-rw-r--r--docs/manual/mod/mod_dav.html272
-rw-r--r--docs/manual/mod/mod_ext_filter.html286
-rw-r--r--docs/manual/mod/mod_file_cache.html267
-rw-r--r--docs/manual/mod/module-dict.html135
-rw-r--r--docs/manual/mod/module-dict.html.en135
-rw-r--r--docs/manual/mod/mpm_common.html409
-rw-r--r--docs/manual/mod/mpm_winnt.html59
-rw-r--r--docs/manual/mod/perchild.html123
-rw-r--r--docs/manual/mod/prefork.html130
-rw-r--r--docs/manual/new_features_2_0.html.fr88
-rw-r--r--docs/manual/programs/ab.html121
-rw-r--r--docs/manual/programs/apachectl.html82
-rw-r--r--docs/manual/programs/apxs.html269
-rw-r--r--docs/manual/programs/dbmmanage.html103
-rw-r--r--docs/manual/programs/htdigest.html50
-rw-r--r--docs/manual/programs/htpasswd.html158
-rw-r--r--docs/manual/programs/httpd.html117
-rwxr-xr-xdocs/manual/programs/index.html60
-rw-r--r--docs/manual/programs/logresolve.html38
-rwxr-xr-xdocs/manual/programs/other.html48
-rw-r--r--docs/manual/programs/rotatelogs.html42
-rw-r--r--docs/manual/programs/suexec.html29
-rw-r--r--docs/manual/server-wide.html215
-rw-r--r--docs/manual/server-wide.html.en215
-rw-r--r--docs/manual/server-wide.html.ja.jis113
-rw-r--r--docs/manual/upgrading.html94
-rw-r--r--docs/manual/upgrading.html.en94
-rw-r--r--docs/manual/upgrading.html.fr131
-rwxr-xr-xdocs/manual/urlmapping.html253
-rwxr-xr-xdocs/manual/urlmapping.html.en253
-rw-r--r--include/ap_compat.h11
-rw-r--r--include/ap_release.h74
-rw-r--r--include/mpm_common.h84
-rw-r--r--include/scoreboard.h242
-rw-r--r--include/util_charset.h109
-rw-r--r--include/util_ebcdic.h81
-rw-r--r--include/util_filter.h220
-rw-r--r--include/util_xml.h220
-rw-r--r--libhttpd.dsp100
-rw-r--r--modules/cache/.cvsignore10
-rw-r--r--modules/cache/Makefile.in3
-rw-r--r--modules/cache/config.m417
-rw-r--r--modules/config5.m443
-rw-r--r--modules/dav/fs/.cvsignore10
-rw-r--r--modules/dav/fs/Makefile.in5
-rw-r--r--modules/dav/fs/config6.m411
-rw-r--r--modules/dav/fs/dbm.c315
-rw-r--r--modules/dav/fs/lock.c1486
-rw-r--r--modules/dav/fs/mod_dav_fs.c137
-rw-r--r--modules/dav/fs/mod_dav_fs.dsp120
-rw-r--r--modules/dav/fs/repos.c2007
-rw-r--r--modules/dav/fs/repos.h75
-rw-r--r--modules/dav/main/.cvsignore10
-rw-r--r--modules/dav/main/Makefile.in6
-rw-r--r--modules/dav/main/config5.m422
-rw-r--r--modules/dav/main/liveprop.c122
-rw-r--r--modules/dav/main/mod_dav.c3313
-rw-r--r--modules/dav/main/mod_dav.dsp128
-rw-r--r--modules/dav/main/mod_dav.h1809
-rw-r--r--modules/dav/main/props.c1525
-rw-r--r--modules/dav/main/providers.c90
-rw-r--r--modules/dav/main/std_liveprop.c289
-rw-r--r--modules/dav/main/util.c2087
-rw-r--r--modules/dav/main/util_lock.c764
-rw-r--r--modules/echo/.cvsignore10
-rw-r--r--modules/echo/Makefile.in3
-rw-r--r--modules/echo/config.m418
-rw-r--r--modules/experimental/Makefile.in3
-rw-r--r--modules/experimental/config.m47
-rw-r--r--modules/experimental/mod_cache.c130
-rw-r--r--modules/experimental/mod_cache.h67
-rw-r--r--modules/experimental/mod_case_filter.c113
-rw-r--r--modules/experimental/mod_charset_lite.c296
-rw-r--r--modules/experimental/mod_disk_cache.c171
-rw-r--r--modules/filters/.cvsignore10
-rw-r--r--modules/filters/Makefile.in3
-rw-r--r--modules/filters/config.m418
-rw-r--r--modules/filters/mod_include.h206
-rw-r--r--modules/generators/.cvsignore10
-rw-r--r--modules/generators/Makefile.in3
-rw-r--r--modules/generators/config5.m428
-rw-r--r--modules/generators/mod_suexec.c160
-rw-r--r--modules/generators/mod_suexec.h64
-rw-r--r--modules/http/.cvsignore10
-rw-r--r--modules/http/Makefile.in3
-rw-r--r--modules/http/config2.m410
-rw-r--r--modules/http/mod_core.h92
-rw-r--r--modules/loggers/.cvsignore10
-rw-r--r--modules/loggers/Makefile.in3
-rw-r--r--modules/loggers/config.m410
-rw-r--r--modules/loggers/mod_log_config.h75
-rw-r--r--modules/mappers/.cvsignore10
-rw-r--r--modules/mappers/Makefile.in3
-rw-r--r--modules/mappers/config9.m433
-rw-r--r--modules/metadata/.cvsignore10
-rw-r--r--modules/metadata/Makefile.in3
-rw-r--r--modules/metadata/config.m430
-rw-r--r--modules/proxy/Makefile.in5
-rw-r--r--modules/proxy/config.m432
-rw-r--r--modules/ssl/.cvsignore10
-rw-r--r--modules/ssl/README163
-rw-r--r--modules/ssl/README.dsov.fig346
-rw-r--r--modules/ssl/README.dsov.ps1138
-rw-r--r--modules/ssl/mod_ssl.c248
-rw-r--r--modules/ssl/mod_ssl.h854
-rw-r--r--modules/ssl/ssl_engine_config.c1093
-rw-r--r--modules/ssl/ssl_engine_dh.c255
-rw-r--r--modules/ssl/ssl_engine_init.c1090
-rw-r--r--modules/ssl/ssl_engine_io.c728
-rw-r--r--modules/ssl/ssl_engine_kernel.c1905
-rw-r--r--modules/ssl/ssl_engine_log.c326
-rw-r--r--modules/ssl/ssl_engine_mutex.c397
-rw-r--r--modules/ssl/ssl_engine_pphrase.c545
-rw-r--r--modules/ssl/ssl_engine_rand.c215
-rw-r--r--modules/ssl/ssl_engine_vars.c615
-rw-r--r--modules/ssl/ssl_expr.c119
-rw-r--r--modules/ssl/ssl_expr.h139
-rw-r--r--modules/ssl/ssl_expr_eval.c282
-rw-r--r--modules/ssl/ssl_expr_parse.c605
-rw-r--r--modules/ssl/ssl_expr_parse.h29
-rw-r--r--modules/ssl/ssl_expr_parse.y186
-rw-r--r--modules/ssl/ssl_expr_scan.c2002
-rw-r--r--modules/ssl/ssl_expr_scan.l261
-rw-r--r--modules/ssl/ssl_scache.c204
-rw-r--r--modules/ssl/ssl_scache_dbm.c440
-rw-r--r--modules/ssl/ssl_scache_shmcb.c1343
-rw-r--r--modules/ssl/ssl_util.c437
-rw-r--r--modules/ssl/ssl_util_ssl.c544
-rw-r--r--modules/ssl/ssl_util_ssl.h115
-rw-r--r--modules/test/mod_optional_fn_export.c86
-rw-r--r--modules/test/mod_optional_fn_export.h3
-rw-r--r--modules/test/mod_optional_fn_import.c93
-rw-r--r--os/beos/.cvsignore5
-rw-r--r--os/os2/core.mk7
-rw-r--r--os/os2/core_header.def11
-rw-r--r--os/tpf/.cvsignore2
-rw-r--r--server/core.c3267
-rw-r--r--server/error_bucket.c104
-rw-r--r--server/mpm/beos/.cvsignore5
-rw-r--r--server/mpm/beos/Makefile.in5
-rw-r--r--server/mpm/beos/beos.c1134
-rw-r--r--server/mpm/beos/beos.h69
-rw-r--r--server/mpm/beos/config5.m47
-rw-r--r--server/mpm/beos/mpm.h79
-rw-r--r--server/mpm/beos/mpm_default.h143
-rw-r--r--server/mpm/experimental/perchild/.cvsignore5
-rw-r--r--server/mpm/experimental/perchild/Makefile.in5
-rw-r--r--server/mpm/experimental/perchild/config5.m46
-rw-r--r--server/mpm/experimental/perchild/mpm.h83
-rw-r--r--server/mpm/experimental/perchild/mpm_default.h144
-rw-r--r--server/mpm/experimental/perchild/perchild.c1430
-rw-r--r--server/mpm/monitoring-services.txt94
-rw-r--r--server/mpm/prefork/mpm.h72
-rw-r--r--server/mpm/winnt/Win9xConHook.c517
-rw-r--r--server/mpm/winnt/Win9xConHook.def9
-rw-r--r--server/mpm/winnt/Win9xConHook.dsp103
-rw-r--r--server/mpm/winnt/Win9xConHook.h24
-rw-r--r--server/mpm/winnt/mpm.h63
-rw-r--r--server/mpm_common.c170
-rw-r--r--server/protocol.c1594
-rw-r--r--server/request.c1124
-rw-r--r--server/scoreboard.c283
-rw-r--r--server/util_charset.c122
-rw-r--r--server/util_debug.c78
-rw-r--r--server/util_ebcdic.c103
-rw-r--r--server/util_filter.c146
-rw-r--r--server/util_xml.c421
-rw-r--r--srclib/pcre/.cvsignore14
-rw-r--r--srclib/pcre/config.hw28
-rw-r--r--srclib/pcre/dftables.dsp160
-rw-r--r--srclib/pcre/pcre.dsp193
-rw-r--r--srclib/pcre/pcre.hw105
-rw-r--r--srclib/pcre/pcreposix.dsp152
-rw-r--r--support/apachectl.in234
-rw-r--r--support/dbmmanage.in350
-rw-r--r--support/log_server_status.in114
-rw-r--r--support/logresolve.pl.in261
-rw-r--r--support/phf_abuse_log.cgi.in22
-rw-r--r--support/split-logfile.in98
-rw-r--r--test/Makefile.in15
216 files changed, 66925 insertions, 0 deletions
diff --git a/.cvsignore b/.cvsignore
new file mode 100644
index 0000000000..1e493134b1
--- /dev/null
+++ b/.cvsignore
@@ -0,0 +1,8 @@
+configure
+missing
+install-sh
+mkinstalldirs
+aclocal.m4
+.deps
+generated_lists
+buildmk.stamp
diff --git a/ABOUT_APACHE b/ABOUT_APACHE
new file mode 100644
index 0000000000..696a87bd54
--- /dev/null
+++ b/ABOUT_APACHE
@@ -0,0 +1,266 @@
+
+ The Apache HTTP Server Project
+
+ http://www.apache.org/httpd
+
+ July 2000
+
+The Apache Project is a collaborative software development effort aimed
+at creating a robust, commercial-grade, featureful, and freely-available
+source code implementation of an HTTP (Web) server. The project is
+jointly managed by a group of volunteers located around the world, using
+the Internet and the Web to communicate, plan, and develop the server and
+its related documentation. These volunteers are known as the Apache Group.
+In addition, hundreds of users have contributed ideas, code, and
+documentation to the project. This file is intended to briefly describe
+the history of the Apache Group, recognize the many contributors, and
+explain how you can join the fun too.
+
+In February of 1995, the most popular server software on the Web was the
+public domain HTTP daemon developed by Rob McCool at the National Center
+for Supercomputing Applications, University of Illinois, Urbana-Champaign.
+However, development of that httpd had stalled after Rob left NCSA in
+mid-1994, and many webmasters had developed their own extensions and bug
+fixes that were in need of a common distribution. A small group of these
+webmasters, contacted via private e-mail, gathered together for the purpose
+of coordinating their changes (in the form of "patches"). Brian Behlendorf
+and Cliff Skolnick put together a mailing list, shared information space,
+and logins for the core developers on a machine in the California Bay Area,
+with bandwidth and diskspace donated by HotWired and Organic Online.
+By the end of February, eight core contributors formed the foundation
+of the original Apache Group:
+
+ Brian Behlendorf Roy T. Fielding Rob Hartill
+ David Robinson Cliff Skolnick Randy Terbush
+ Robert S. Thau Andrew Wilson
+
+with additional contributions from
+
+ Eric Hagberg Frank Peters Nicolas Pioch
+
+Using NCSA httpd 1.3 as a base, we added all of the published bug fixes
+and worthwhile enhancements we could find, tested the result on our own
+servers, and made the first official public release (0.6.2) of the Apache
+server in April 1995. By coincidence, NCSA restarted their own development
+during the same period, and Brandon Long and Beth Frank of the NCSA Server
+Development Team joined the list in March as honorary members so that the
+two projects could share ideas and fixes.
+
+The early Apache server was a big hit, but we all knew that the codebase
+needed a general overhaul and redesign. During May-June 1995, while
+Rob Hartill and the rest of the group focused on implementing new features
+for 0.7.x (like pre-forked child processes) and supporting the rapidly growing
+Apache user community, Robert Thau designed a new server architecture
+(code-named Shambhala) which included a modular structure and API for better
+extensibility, pool-based memory allocation, and an adaptive pre-forking
+process model. The group switched to this new server base in July and added
+the features from 0.7.x, resulting in Apache 0.8.8 (and its brethren)
+in August.
+
+After extensive beta testing, many ports to obscure platforms, a new set
+of documentation (by David Robinson), and the addition of many features
+in the form of our standard modules, Apache 1.0 was released on
+December 1, 1995.
+
+Less than a year after the group was formed, the Apache server passed
+NCSA's httpd as the #1 server on the Internet.
+
+The survey by Netcraft (http://www.netcraft.com/survey/) shows that Apache
+is today more widely used than all other web servers combined.
+
+ ============================================================================
+
+Current Apache Group in alphabetical order as of 27 July 2000:
+
+ Brian Behlendorf Collab.Net, California
+ Ryan Bloom Covalent Technologies, California
+ Ken Coar IBM Corporation, Research Triangle Park, NC, USA
+ Mark J. Cox Red Hat, England
+ Lars Eilebrecht CyberSolutions, Munich, Germany
+ Ralf S. Engelschall Munich, Germany.
+ Roy T. Fielding eBuilt, California
+ Tony Finch Covalent Technologies, California
+ Dean Gaudet Transmeta Corporation, California
+ Dirk-Willem van Gulik Covalent Technologies, California
+ Brian Havard Australia
+ Ben Hyde Gensym, Massachusetts
+ Jim Jagielski jaguNET Access Services, Maryland
+ Manoj Kasichainula Collab.Net, California
+ Alexei Kosut Stanford University, California
+ Martin Kraemer Munich, Germany
+ Ben Laurie Freelance Consultant, UK
+ Rasmus Lerdorf Linuxcare, California
+ Daniel Lopez Ridruejo Covalent Technologies, California
+ Doug MacEachern Covalent Technologies, California
+ Aram W. Mirzadeh CableVision, New York
+ Chuck Murcko The Topsail Group, Pennsylvania
+ Sameer Parekh California
+ David Reid UK
+ William A. Rowe, Jr. Freelance Consultant, Chicago area
+ Wilfredo Sanchez Apple Computer, California
+ Cliff Skolnick California
+ Marc Slemko Canada
+ Greg Stein California
+ Bill Stoddard IBM Corporation, Research Triangle Park, NC
+ Paul Sutton Seattle
+ Randy Terbush Covalent Technologies, California
+
+Apache Emeritus (old group members now off doing other things)
+
+ Rob Hartill Internet Movie DB, UK
+ David Robinson Cambridge University, UK
+ Robert S. Thau MIT, Massachusetts
+ Andrew Wilson Freelance Consultant, UK
+
+Other major contributors
+
+ Howard Fear (mod_include), Florent Guillaume (language negotiation),
+ Koen Holtman (rewrite of mod_negotiation),
+ Kevin Hughes (creator of all those nifty icons),
+ Brandon Long and Beth Frank (NCSA Server Development Team, post-1.3),
+ Ambarish Malpani (Beginning of the NT port),
+ Rob McCool (original author of the NCSA httpd 1.3),
+ Paul Richards (convinced the group to use remote CVS after 1.0),
+ Garey Smiley (OS/2 port), Henry Spencer (author of the regex library).
+
+Many 3rd-party modules, frequently used and recommended, are also
+freely-available and linked from the related projects page:
+<http://modules.apache.org/>, and their authors frequently
+contribute ideas, patches, and testing.
+
+Hundreds of people have made individual contributions to the Apache
+project. Patch contributors are listed in the src/CHANGES file.
+Frequent contributors have included Petr Lampa, Tom Tromey, James H.
+Cloos Jr., Ed Korthof, Nathan Neulinger, Jason S. Clary, Jason A. Dour,
+Michael Douglass, Tony Sanders, Brian Tao, Michael Smith, Adam Sussman,
+Nathan Schrenk, Matthew Gray, and John Heidemann.
+
+ ============================================================================
+
+How to become involved in the Apache project
+
+There are several levels of contributing. If you just want to send
+in an occasional suggestion/fix, then you can just use the bug reporting
+form at <http://www.apache.org/bug_report.html>. You can also subscribe
+to the announcements mailing list (apache-announce@apache.org) which we
+use to broadcast information about new releases, bugfixes, and upcoming
+events. There's a lot of information about the development process (much
+of it in serious need of updating) to be found at <http://dev.apache.org/>.
+
+If you'd like to become an active contributor to the Apache project (the
+group of volunteers who vote on changes to the distributed server), then
+you need to start by subscribing to the new-httpd@apache.org mailing list.
+One warning though: traffic is high, 1000 to 1500 messages/month.
+To subscribe to the list, send "subscribe new-httpd" in the body of
+a message to <majordomo@apache.org>. We recommend reading the list for
+a while before trying to jump in to development.
+
+ NOTE: The developer mailing list (new-httpd@apache.org) is not
+ a user support forum; it is for people actively working on development
+ of the server code and documentation, and for planning future
+ directions. If you have user/configuration questions, send them
+ to the USENET newsgroup "comp.infosystems.www.servers.unix".
+
+There is a core group of contributors (informally called the "core")
+which was formed from the project founders and is augmented from time
+to time when core members nominate outstanding contributors and the
+rest of the core members agree. The core group focus is more on
+"business" issues and limited-circulation things like security problems
+than on mainstream code development. The term "The Apache Group"
+technically refers to this core of project contributors.
+
+The Apache project is a meritocracy -- the more work you have done, the more
+you are allowed to do. The group founders set the original rules, but
+they can be changed by vote of the active members. There is a group
+of people who have logins on our server (apache.org) and access to the
+CVS repository. Everyone has access to the CVS snapshots. Changes to
+the code are proposed on the mailing list and usually voted on by active
+members -- three +1 (yes votes) and no -1 (no votes, or vetoes) are needed
+to commit a code change during a release cycle; docs are usually committed
+first and then changed as needed, with conflicts resolved by majority vote.
+
+Our primary method of communication is our mailing list. Approximately 40
+messages a day flow over the list, and are typically very conversational in
+tone. We discuss new features to add, bug fixes, user problems, developments
+in the web server community, release dates, etc. The actual code development
+takes place on the developers' local machines, with proposed changes
+communicated using a patch (output of a unified "diff -u oldfile newfile"
+command), and committed to the source repository by one of the core
+developers using remote CVS. Anyone on the mailing list can vote on a
+particular issue, but we only count those made by active members or people
+who are known to be experts on that part of the server. Vetoes must be
+accompanied by a convincing explanation.
+
+New members of the Apache Group are added when a frequent contributor is
+nominated by one member and unanimously approved by the voting members.
+In most cases, this "new" member has been actively contributing to the
+group's work for over six months, so it's usually an easy decision.
+
+The above describes our past and current (as of July 2000) guidelines,
+which will probably change over time as the membership of the group
+changes and our development/coordination tools improve.
+
+ ============================================================================
+
+The Apache Software Foundation (www.apache.org)
+
+The Apache Software Foundation exists to provide organizational, legal,
+and financial support for the Apache open-source software projects.
+Founded in June 1999 by the Apache Group, the Foundation has been
+incorporated as a membership-based, not-for-profit corporation in order
+to ensure that the Apache projects continue to exist beyond the participation
+of individual volunteers, to enable contributions of intellectual property
+and funds on a sound basis, and to provide a vehicle for limiting legal
+exposure while participating in open-source software projects.
+
+You are invited to participate in The Apache Software Foundation. We welcome
+contributions in many forms. Our membership consists of those individuals
+who have demonstrated a commitment to collaborative open-source software
+development through sustained participation and contributions within the
+Foundation's projects. Many people and companies have contributed towards
+the success of the Apache projects.
+
+ ============================================================================
+
+Why Apache Is Free
+
+Apache exists to provide a robust and commercial-grade reference
+implementation of the HTTP protocol. It must remain a platform upon which
+individuals and institutions can build reliable systems, both for
+experimental purposes and for mission-critical purposes. We believe the
+tools of online publishing should be in the hands of everyone, and
+software companies should make their money providing value-added services
+such as specialized modules and support, amongst other things. We realize
+that it is often seen as an economic advantage for one company to "own" a
+market - in the software industry that means to control tightly a
+particular conduit such that all others must pay. This is typically done
+by "owning" the protocols through which companies conduct business, at the
+expense of all those other companies. To the extent that the protocols of
+the World Wide Web remain "unowned" by a single company, the Web will
+remain a level playing field for companies large and small. Thus,
+"ownership" of the protocol must be prevented, and the existence of a
+robust reference implementation of the protocol, available absolutely for
+free to all companies, is a tremendously good thing.
+
+Furthermore, Apache is an organic entity; those who benefit from it
+by using it often contribute back to it by providing feature enhancements,
+bug fixes, and support for others in public newsgroups. The amount of
+effort expended by any particular individual is usually fairly light, but
+the resulting product is made very strong. This kind of community can
+only happen with freeware -- when someone pays for software, they usually
+aren't willing to fix its bugs. One can argue, then, that Apache's
+strength comes from the fact that it's free, and if it were made "not
+free" it would suffer tremendously, even if that money were spent on a
+real development team.
+
+We want to see Apache used very widely -- by large companies, small
+companies, research institutions, schools, individuals, in the intranet
+environment, everywhere -- even though this may mean that companies who
+could afford commercial software, and would pay for it without blinking,
+might get a "free ride" by using Apache. We would even be happy if some
+commercial software companies completely dropped their own HTTP server
+development plans and used Apache as a base, with the proper attributions
+as described in the LICENSE file.
+
+Thanks for using Apache!
+
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000000..e27eab039b
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,8722 @@
+Changes with Apache 2.0a9
+
+ *) Distribution directory structure reorganized to reflect a
+ normal source distribution with external install targets.
+ [Roy Fielding]
+
+ *) The MPMs that need multiple segments of shared memory now create
+ two apr_shmem_t variables, one for each shared memory allocation.
+ the problem is that we can't determine how much memory will be required
+ for shared memory allocations once we try to allocate more than one
+ variable. The MM code automatically aligns the shared memory allocations,
+ so we end up needing to pad the amount of shared memory we want based
+ on how many variables will be allocated out of the shared memory segment.
+ It is just easier to create a second apr_shmem_t variable, and two
+ shmem memory blocks.
+ [Ryan Bloom]
+
+ *) Cleanup the export list a bit. This creates a single unified list of
+ functions exported by APR. The export list is generated at configure
+ time, and that list is then used to generate the exports.c file.
+ Because of the way the export list is generated, we only export those
+ functions that are valid on the platform we are building on.
+ [Ryan Bloom]
+
+ *) Enable logging the cookie with mod_log_config
+ [Sander van Zoest <sander@covalent.net>]
+
+ *) Fix a segfault in mod_info when it reaches the end of the configuration.
+ [Jeff Trawick]
+
+ *) Added lib/aputil/ as a placeholder for utility functions which are not
+ specific to the Apache HTTP Server (but do not make sense with APR).
+ The first utility is "apu_dbm": a set of functions to work with DBM
+ files. This first version can be compiled for SDBM or GDBM databases.
+ [Greg Stein]
+
+ *) Complete re-write of mod_include. This makes mod_include a filter that
+ uses buckets directly. This has now served the FAQ correctly.
+ [Paul Reder <rederpj@raleigh.ibm.com>]
+
+ *) Allow modules to specify the first filter in a sub_request when
+ making the sub_request. This keeps modules from having to change the
+ output_filter immediately after creating the sub-request, and therefore
+ skip the sub_req_output_filter. [Ryan Bloom]
+
+ *) Update ab to accept URLs with IPv6 literal address strings (in the
+ format described in RFC 2732), and to build Host header fields in
+ the same format. This allows IPv6 literal address strings to be
+ used with ab. This support has been tested against Apache 1.3 with
+ the KAME patch, but Apache 2.0 does not yet work with this format
+ of the Host header field. [Jeff Trawick]
+
+ *) Accomodate an out-of-space condition in the piped logs and the
+ rotatelogs.c code, and no longer churn log processes for this
+ condition. [Victor J. Orlikowski]
+
+ *) Add support for partial writes with apr_sendfile() to core_output_filter.
+ [Greg Ames]
+
+Changes with Apache 2.0a8
+
+ *) Add a directive to mod_mime so that filters can be associated with
+ a given mime-type.
+ [Ryan Bloom]
+
+ *) Get multi-views working again. We were setting the path_info
+ field incorrectly if we couldn't find the specified file.
+ [Ryan Bloom]
+
+ *) Fix 304 processing. The core should never try to send the headers
+ down the filter stack. Always, just setup the table in the request
+ record, and let the header filter convert it to data that is ready
+ for the network.
+ [Ryan Bloom]
+
+ *) More fixes for the proxy. There are still bugs in the proxy code,
+ but this has now proxied www.yahoo.com and www.ntrnet.net (my ISP)
+ successfully.
+ [Ryan Bloom]
+
+ *) Fix params for apr_getaddrinfo() call in connect proxy handler.
+ [Chuck Murcko]
+
+ *) APR: Add new apr_getopt_long function to handle long options.
+ [B. W. Fitzpatrick <fitz@red-bean.com>]
+
+ *) APR: Change apr_connect() to take apr_sockaddr_t instead of hostname.
+ Add generic apr_create_socket(). Add apr_getaddrinfo() for doing
+ hostname resolution/address string parsing and building
+ apr_sockaddr_t. Add apr_get_sockaddr() for getting the address
+ of one of the apr_sockaddr_t structures for a socket. Change
+ apr_bind() to take apr_sockaddr_t. [David Reid and Jeff Trawick]
+
+ *) Remove the BUFF from the HTTP proxy. This is still a bit ugly, but
+ I have proxied pages with it, cleanup will commence soon.
+ [Ryan Bloom]
+
+ *) Make the proxy work with filters. This isn't perfect, because we
+ aren't dealing with the headers properly. [Ryan Bloom]
+
+ *) Do not send a content-length iff the C-L is 0 and this is a head
+ request. [Ryan Bloom]
+
+ *) Make cgi-bin work as a regular directory when using mod_vhost_alias
+ with no VirtualScriptAlias directives. PR#6829 [Tony Finch]
+
+ *) Remove BUFF from the PROXY connect handling. [Ryan Bloom]
+
+ *) Get the default_handler to stop trying to deal with HEAD requests.
+ The idea is to let the content-length filter compute the C-L before
+ we try to send the data. If we can get the C-L correctly, then we
+ should send it in the HEAD response.
+ [Ryan Bloom]
+
+ *) The Header filter can now determine if a body should be sent based
+ on r->header_only. The general idea of this is that if we delay
+ deciding to send the body, then we might be able to compute the
+ content-length correctly, which will help caching proxies to cache
+ our data better. Any handler that doesn't want to try to compute
+ the content-length can just send an EOS bucket without data and
+ everything will just work.
+ [Ryan Bloom]
+
+ *) Add the referer to the error log if one is available.
+ [Markus Gyger <mgyger@itr.ch>]
+
+ *) Mod_info.c has now been ported to Apache 2.0. As a part of this
+ change, the root of the configuration tree has been exposed to modules
+ as ap_conftree.
+ [Ryan Morgan <rmorgan@covalent.net>]
+
+ *) Get the core_output_filter to use the bucket interface directly.
+ This keeps us from calling the content-length filter multiple times
+ for a simple static request.
+ [Ryan Bloom]
+
+ *) We are sending the content-type correctly now.
+ [Ryan Bloom and Will Rowe]
+
+ *) APR on FreeBSD: Fix a bug in apr_sendfile() which caused us to report
+ a bogus bytes-sent value when the only thing being sent was trailers
+ and writev() returned an error (or EAGAIN). [Jeff Trawick]
+
+ *) Get SINGLE_LISTEN_UNSERIALIZED_ACCEPT working again. This uses the
+ hints file to determine which platforms define
+ SINGLE_LISTEN_UNSERIALIZED_ACCEPT.
+ [Ryan Bloom]
+
+ *) APR: add apr_get_home_directory() [Jeff Trawick]
+
+ *) Initial import of 1.3-current mod_proxy. [Chuck Murcko]
+
+ *) Not all platforms have INADDR_NONE defined by default. Apache
+ used to make this check and define INADDR_NONE if appropriate,
+ but APR needs the check too, and I suspect other applications will
+ as well. APR now defines APR_INADDR_NONE, which is always a valid
+ value on all platforms.
+ [Branko Èibej <brane@xbc.nu>]
+
+ *) Destroy the pthread mutex in lock_intra_cleanup() for PR#6824.
+ [Shuichi Kitaguchi <ki@hh.iij4u.or.jp>]
+
+ *) Relax the syntax checking of Host: headers in order to support
+ iDNS. PR#6635 [Tony Finch]
+
+ *) When reading from file buckets we convert to an MMAP if it makes
+ sense. This also simplifies the default handler because the
+ default handler no longer needs to try to create MMAPs.
+ [Ryan Bloom]
+
+ *) BUFF has been removed from the main server. The BUFF code will remain
+ in the code until it has been purged from the proxy module as well.
+ [Ryan Bloom]
+
+ *) Byteranges have been completely re-written to be a filter. This
+ has been tested, and I believe it is working correctly, but it could
+ doesn't work for the Adobe Acrobat plug-in. The output almost matches
+ the output from 1.3, the only difference being that 1.3 includes
+ a content-length in the response, and this does not.
+ [Ryan Bloom]
+
+ *) APR read/write functions and bucket read functions now operate
+ on unsigned integers, instead of signed ones. It doesn't make
+ any sense to use signed ints, because we return the error codes,
+ so if we have an error we should report 0 bytes read or written.
+ [Ryan Bloom]
+
+ *) Always compute the content length, whether it is sent or not.
+ The reason for this, is that it allows us to correctly report
+ the bytes_sent when logging the request. This also simplifies
+ content-length filter a bit, and fixes the actual byte-reporing
+ code in mod_log_config.c
+ [Ryan Bloom]
+
+ *) Remove AP_END_OF_BRIGADE definition. This does not signify what
+ it says, because it was only used by EOS and FLUSH buckets. Since
+ neither of those are required at the end of a brigade, this was
+ really signifying FLUSH_THE_DATA, but that can be determined better
+ by checking AP_BUCKET_IS_EOS() or AP_BUCKET_IS_FLUSH. EOS and FLUSH
+ buckets now return a length of 0, which is actually the amount of data
+ read, so they make more sense.
+ [Ryan Bloom]
+
+ *) Allow the core_output_filter to save some data past the end of a
+ request. If we get an EOS bucket, we only send the data if it
+ makes sense to send it. This allows us to pipeline request
+ responses. As a part of this, we also need to allocate mmap
+ buckets out of the connection pool, not the request pool. This
+ allows the mmap to outlive the request.
+ [Ryan Bloom]
+
+ *) Make blocking and non-blocking bucket reads work correctly for
+ sockets and pipes. These are the only bucket types that should
+ have non-blocking reads, because the other bucket types should
+ ALWAYS be able to return something immediately.
+ [Ryan Bloom]
+
+ *) In the Apache/Win32 console window, accept Ctrl+C to stop the
+ server, but use Ctrl+Break to initiate a graceful restart
+ instead of duplicating behavior. [John Sterling]
+
+ *) Patch mod_autoindex to set the Last-Modified header based on
+ the directory's mtime, and add the ETag header. [William Rowe]
+
+ *) Merge the 1.3 patch to add support for logging query string in
+ such a way that "%m %U%q %H" is the same as "%r".
+ [Bill Stoddard]
+
+ *) Port three log methods from mod_log_config 1.3 to 2.0:
+ CLF compliant '-' byte count, method and protocol.
+ [Bill Stoddard]
+
+ *) Add a new LogFormat directive, %c, that will log connection
+ status at the end of the response as follows:
+ 'X' - connection aborted before the response completed.
+ '+' - connection may be kept-alive by the server.
+ '-' - connection will be closed by the server.
+ [Bill Stoddard]
+
+ *) Expand APR for WinNT to fully accept and return utf-8 encoded
+ Unicode file names and paths for Win32, and tag the Content-Type
+ from mod_autoindex to reflect that charset if the the feature
+ macro APR_HAS_UNICODE_FS is true. [William Rowe]
+
+ *) Compute the content length (and add appropriate header field) for
+ the response when no content length is available and we can't use
+ chunked encoding. [Jeff Trawick]
+
+ *) Changed ap_discard_request_body() to use REQUEST_CHUNKED_DECHUNK,
+ so that content input filters get dechunked data when using
+ the default handler. Also removed REQUEST_CHUNKED_PASS.
+ [Sascha Schumann]
+
+ *) Add mod_ext_filter as an experimental module. This module allows
+ the administrator to use external programs as filters. Currently,
+ only filtering of output is supported. [Jeff Trawick]
+
+ *) Most Apache functions work on EBCDIC machines again, as protocol
+ data is now translated (again). [Jeff Trawick]
+
+ *) Introduce ap_xlate_proto_{to|from}_ascii() to clean up some of
+ the EBCDIC support. They are noops on ASCII machines, so this
+ type of translation doesn't have to be surrounded by #ifdef
+ CHARSET_EBCDIC. [Jeff Trawick]
+
+ *) Fix mod_include. tag commands work again, and the server will
+ send the FAQ again. This also allows mod_include to set aside
+ buckets that include partial buckets.
+ [Ryan Bloom and David Reid]
+
+ *) Add suexec support back. [Manoj Kasichainula]
+
+ *) Lingering close now uses the socket directly instead of using
+ BUFF. This has been tested, but since all we can tell is that it
+ doesn't fail, this needs to be really hacked on.
+ [Ryan Bloom]
+
+ *) Allow filters to modify headers and have those headers be sent to
+ the client. The idea is that we have an http_header filter that
+ actually sends the headers to the network. This removes the need
+ for the BUFF to send headers.
+ [Ryan Bloom]
+
+ *) Charset translation: mod_charset_lite handles translation of
+ request bodies. Get rid of the xlate version of ap_md5_digest()
+ since we don't compute digests of filtered (e.g., translated)
+ response bodies this way anymore. (Note that we don't do it at
+ all at the present; somebody needs to write a filter to do so.)
+ [Jeff Trawick]
+
+ *) Input filters and ap_get_brigade() now have a input mode parameter
+ (blocking, non-blocking, peek) instead of a length parameter.
+ [hackathon]
+
+ *) Update the mime.types file to the registered media types as
+ of 2000-10-19. PR#6613 [Carsten Klapp <carsten.klapp@home.net>,
+ Tony Finch]
+
+ *) Namespace protect some macros declared in ap_config.h
+ [Ryan Bloom]
+
+ *) Support HTTP header line folding with input filtering.
+ [Greg Ames]
+
+ *) Mod_include works again. This should still be re-written, but at
+ least now we can serve an SHTML page again.
+ [Ryan Bloom]
+
+ *) Begin to remove BUFF from the core. Currently, we keep a pointer
+ to both the BUFF and the socket in the conn_rec. Functions that
+ want to use the BUFF can, functions that want to use the socket,
+ can. They point to the same place.
+ [Ryan Bloom]
+
+ *) apr_psprintf doesn't understand %lld as a format. Make it %ld.
+ [Tomas "Ögren" <stric@ing.umu.se>]
+
+ *) APR pipes on Unix and Win32 are now cleaned up automatically when the
+ associated pool goes away. (APR pipes on OS/2 were already had this
+ logic.) This resolvs a fatal file descriptor leak with CGIs.
+ [Jeff Trawick]
+
+ *) The final line of the config file was not being read if there was
+ no \n at the end of it. This was caused by apr_fgets returning
+ APR_EOF even though we had read valid data. This is solved by
+ making cfg_getline check the buff that was returned from apr_fgets.
+ If apr_fgets return APR_EOF, but there was data in the buf, then we
+ return the buf, otherwise we return NULL.
+ [Ryan Bloom]
+
+ *) Piped logs work again in the 2.0 series.
+ [Ryan Bloom]
+
+ *) Restore functionality broken by the mod_rewrite security fix:
+ rewrite map lookup keys and default values are now expanded
+ so that the lookup can depend on the requested URI etc.
+ PR #6671 [Tony Finch]
+
+ *) Tighten up the syntax checking of Host: headers to fix a
+ security bug in some mass virtual hosting configurations
+ that can allow a remote attacker to retrieve some files
+ on the system that should be inaccessible. [Tony Finch]
+
+ *) Add a pool bucket type. This bucket is used for data allocated out
+ of a pool. If the pool is cleaned before the bucket is destroyed, then
+ the data is converted to a heap bucket, allowing it to survive the
+ death of the pool.
+ [Ryan Bloom]
+
+ *) Add a flush bucket. This allows modules to signal that the filters
+ should all flush whatever data they currently have. There is no way
+ to actually force them to do this, so if a filter ignores this bucket,
+ that's life, but at least we can try with this.
+ [Ryan Bloom]
+
+ *) Add an output filter for sub-requests. This filter just strips the
+ EOS bucket so that we don't confuse the main request's core output
+ filter by sending multiple EOS buckets. This change also makes sub
+ requests start to send EOS buckets when they are finished.
+ [Ryan Bloom]
+
+ *) Make ap_bucket_(read|destroy|split|setaside) into macros. Also
+ makes ap_bucket_destroy a return void, which is okay because it
+ used to always return APR_SUCCESS, and nobody ever checked its
+ return value anyway.
+ [Cliff Woolley <cliffwoolley@yahoo.com>]
+
+ *) Remove the index into the bucket-type table from the buckets
+ structure. This has now been replaced with a pointer to the
+ bucket_type. Also add some macros to test the bucket-type.
+ [Ryan Bloom]
+
+ *) Renamed all MODULE_EXPORT symbols to AP_MODULE_DECLARE and all symbols
+ for CORE_EXPORT to AP_CORE_DECLARE (namespace protecting the wrapper)
+ and retitled API_EXPORT as AP_DECLARE and APR_EXPORT as APR_DECLARE.
+ All _VAR_ flavors changes to _DATA to be absolutely clear.
+ [William Rowe]
+
+ *) Add support for /, //, //servername and //server/sharename
+ parsing of <Directory> blocks under Win32 and OS2.
+ [Tim Costello, William Rowe, Brian Harvard]
+
+ *) Remove the function pointers from the ap_bucket type. They have been
+ replaced with a global table. Modules are allowed to register bucket
+ types and use then use those buckets.
+ [Ryan Bloom]
+
+ *) mod_cgid: In the handler, shut down the Unix socket (only for write)
+ once we finish writing the request body to the cgi child process;
+ otherwise, the client doesn't hit EOF on stdin. Small request bodies
+ worked without this change (for reasons I don't understand), but large
+ ones didn't. [Jeff Trawick]
+
+ *) Remove file bucket specific information from the ap_bucket type.
+ This has been moved to a file_bucket specific type that hangs off
+ the data pointer in the ap_bucket type.
+ [Ryan Bloom]
+
+ *) Input filtering now has a third argument. This is the amount of data
+ to read from lower filters. This argument can be -1, 0, or a positive
+ number. -1 means give me all the data you have, I'll deal with it and
+ let you know if I need more. 0 means give me one line and one line
+ only. A positive number means I want no more than this much data.
+
+ Currently, only 0 and a positive number are implemented. This allows
+ us to remove the remaining field from the conn_rec structure, which
+ has also been done.
+ [Ryan Bloom]
+
+ *) Big cleanup of the input filtering. The goal is that http_filter
+ understands two conditions, headers and body. It knows where it is
+ based on c->remaining. If c->remaining is 0, then we are in headers,
+ and http_filter returns a line at a time. If it is not 0, then we are
+ in body, and http_filter returns raw data, but only up to c->remaining
+ bytes. It can return less, but never more.
+ [Greg Ames, Ryan Bloom, Jeff Trawick]
+
+ *) mod_cgi: Write all of the request body to the child, not just what
+ the kernel would accept on the first write. [Jeff Trawick]
+
+ *) Back out the change that moved the brigade from the core_output_filters
+ ctx to the conn_rec. Since all requests over a given connection
+ go through the same core_output_filter, the ctx pointer has the
+ correct lifetime.
+ [Ryan Bloom]
+
+ *) Fix another bug in the send_the_file() read/write loop. A partial
+ send by apr_send would cause unsent data in the read buffer to
+ get clobbered. Complete making send_the_file handle partial
+ writes to the network.
+ [Bill Stoddard]
+
+ *) Fix a couple of type fixes to allow compilation on AIX again
+ [Victor J. Orlikowski <v.j.orlikowski@gte.net>]
+
+ *) Fix bug in send_the_file() which causes offset to be ignored
+ if there are no headers to send.
+ [Bill Stoddard]
+
+ *) Handle APR_ENOTIMPL returned from apr_sendfile in the core
+ filter. Useful for supporting Windows 9* with a binary
+ compiled on Windows NT.
+ [Bill Stoddard]
+
+Changes with Apache 2.0a7
+
+ *) Reimplement core_output_filter to buffer/save bucket brigades
+ across multiple calls to the core_filter. The brigade will be
+ sent when either MIN_BYTES_TO_SEND or MAX_IOVEC_TO_WRITE
+ thresholds are hit or the EOS bucket is received.
+ [Bill Stoddard]
+
+ *) Create experimental filter (buffer_filter) that coalesces bytes
+ into one large buffer before invoking the next filter in the
+ chain. This filter is particularly useful with the current
+ implementation of mod_autoindex when it inserted above the
+ chunk_filter. mod_autoindex generates a lot of brigades that
+ containing buckets holding just a few bytes each. The
+ buffer_filter coalesces these buckets into a single large bucket.
+ [Bill Stoddard]
+
+ *) Add apr_sendfile() support into the core_output_filter.
+ [Bill Stoddard]
+
+ *) Add apr_sendv() support into the core_output_filter.
+ [Bill Stoddard]
+
+ *) Fix mod_log_config so that it compiles cleanly with BUFFERED_LOGS
+ [Mike Abbott <mja@sgi.com>]
+
+ *) Remove ap_send_fb. This is no longer used in Apache, and it doesn't
+ make much sense, because Apache uses buckets instead of BUFFs now.
+ [Ryan Bloom]
+
+ *) send_the_file now falls back to a read/write loop on platforms that
+ do not have sendfile.
+ [Ryan Bloom and Brian Havard]
+
+ *) Install apachectl correctly, and substitute the proper values so
+ that it works again. [Ryan Bloom]
+
+ *) Better(??) handle platforms that lack sendfile().
+ [Jim Jagielski]
+
+ *) APR now has UUID generation/formatting/parsing support.
+ [Greg Stein]
+
+ *) Begin the http_filter. This is an input filter that understands
+ the absolute basic amount required to parse an HTTP Request. The
+ goal is to be able to split headers from request body before passing
+ the data back to the other filters.
+ [Ryan Bloom]
+
+ *) Bring forward from 1.3.13 the config directory implementation
+ [Jim Jagielski]
+
+ *) install apxs if it is created
+ [Ryan Bloom]
+
+ *) Added APR_IS_STATUS_condition test macros to eliminate canonical error
+ conversions. [William Rowe]
+
+ *) Now that we have ap_add_input_filter(), rename ap_add_filter() to
+ ap_add_output_filter(). [Jeff Trawick]
+
+ *) Multiple build and configuration fixes
+ Build process:
+
+ -add datadir and localstatedir substitutions
+ -fix layout name
+ -fix logfilename misspelling
+ -fix evaluation of installation dir variables and
+ -replace $foobar by $(foobar) to be usefull in the makefile
+
+ Cross compile:
+
+ -add rules for cross-compiling in rules.mk. Okay, rule to check for
+ $CC_FOR_BUILD is still missing
+ -use CHECK_TOOL instead of CHECK_PROG for ranlib
+ -add missing "AR=@AR@" to severaly Makefile.in's
+ -cache result for "struct rlimit"
+ -compile all helper programs with native and cross compiler
+ and use the native version to generate header file
+ ["Rüdiger" Kuhlmann <Tadu@gmx.de>]
+
+ *) Prepare our autoconf setup for autoconf 2.14a and for cross-
+ compiling.
+ ["Rüdiger" Kuhlmann <Tadu@gmx.de>]
+
+ *) Fix a bug where a client which only sends \n to delimit header
+ lines (netcat) gets a strange looking HTTP_NOT_IMPLEMENTED
+ message. Start working on ebcdic co-existance with input
+ filtering.
+ [William Rowe, Greg Ames]
+
+ *) If mod_so is enabled in the server always create libexec, even
+ if there are no modules installed in this directory. This is a
+ requirement for APXS to work correctly.
+ [Ryan Bloom]
+
+ *) Connection oriented output filters are now stored in the
+ conn_rec instead of the request_rec. This allows us to add the
+ output filter in the pre-connection phase instead of the
+ post_read_request phase, which keeps us from trying to write an
+ error page before we have a filter to write to the network.
+ [Ryan Bloom, Jeff Trawick, and Greg Ames]
+
+ *) Cleaning up an mmap bucket no longer deletes the mmap. An
+ mmap can be used across multiple buckets (default_handler with
+ byte ranges, mod_file_cache, mod_mmap_static), so cleanup of
+ the mmap itself can't be associated with the bucket.
+ [Jeff Trawick]
+
+ *) Add .dll caching directive ISAPICacheFile to mod_isapi.
+ [William Rowe]
+
+ *) Radical surgery to improve mod_isapi support under Win32.
+ Includes a number of newer ServerSupportFunction calls, support
+ for ReadClient (in order to retrieve POSTs greater than 48KB),
+ and general bug fixes to more reliably load ISAPI .dll's and
+ prevent leaking handle resources. Note: There are still
+ discrepancies between IIS's and Apache's ServerVariables, and
+ async calls are still not supported. Additional warnings are
+ logged to facilitate debugging of unsupported ISAPI calls.
+ [William Rowe]
+
+ *) Add input filtering to Apache. The basic idea for the input
+ filters is the same as the ideas for output filters. The biggest
+ difference is that instead of calling ap_pass_brigade, ap_get_brigade
+ should be called, and the order of execution for the filter itself is
+ different. When writing an output filter, a brigade is passed in,
+ and filters operate directly on that brigade, when done, they call
+ ap_pass_brigade. Input filters are the exact opposite. Because input
+ is not a push operation, filters first call ap_get_brigade. When this
+ function returns, the input filter will be left with a valid brigade.
+ The input filter should then operate on the brigade, and return.
+ [Ryan Bloom]
+
+ *) Fix building on BSD/OS using its native make. The build system
+ falls back to the BSD .include directive on that host platform.
+ [Sascha Schumann]
+
+ *) Expand dbmmanage to allow -d -m -s -p options for Crypt, MD5,
+ SHA1 and plaintext password encodings. Make feature tests a
+ bit more flexible. [William Rowe]
+
+ *) Charset translation: mod_charset_lite handles output content
+ translation in a filter. mod_charset_lite no longer ignores
+ subrequests. A bunch of cruft related to BUFF's support for
+ translating request and response bodies was removed.
+ [Jeff Trawick]
+
+ *) Move the addition of the CORE filter to the post_read_request
+ hook in http_core.c. This removes the need to add the filter in
+ multiple places and allows for an SSL module to be added much
+ simpler. [Ryan Bloom]
+
+ *) Fix a security problem that affects certain configurations of
+ mod_rewrite. If the result of a RewriteRule is a filename that
+ contains expansion specifiers, especially regexp backreferences
+ $0..$9 and %0..%9, then it may be possible for an attacker to
+ access any file on the web server. [Tony Finch]
+
+ *) Fix a bug where errors that are detected during early request parsing
+ don't produce visible HTTP error messages at the browser, because
+ the core_filter wasn't present. [Greg Ames]
+
+ *) Provide apr_socklen_t as a portability aid.
+ [Victor J. Orlikowski]
+
+ *) Overhaul of dbmmanage to allow a groups arg (as in Apache 1.2)
+ as well as a comment arg to the add, adduser and update cmds.
+ update allows the user to clear or preserve pw/groups/comment.
+ Fixed a bug in dbmmanage that prevented the check option from
+ parsing a password followed by :group... text. Corrected the
+ seed calcualation for Win32 systems, and added -lsdbm support.
+ [William Rowe]
+
+ *) Configured mod_auth_dbm to compile with sdbmlib under Win32.
+ [William Rowe]
+
+ *) Avoid a segfault when parsing .htaccess files. An
+ uninitialized tree pointer was passed to ap_build_config().
+ [Jeff Trawick]
+
+ *) Change the way that inet_addr & inet_network are checked for
+ in APR's configure process to allow BeOS BONE to correctly
+ find them. With this change BeOS BONE now builds from source
+ with no problems. [David Reid]
+
+ *) Fix a bug in apr_create_process() for Unix. The NULL signifying
+ the end of the parameters to execve() was stored in the wrong
+ location, overlaying the storage beyond the newargs[] array and
+ also passing uninitialized storage to execve(), which would
+ sometimes fail with EFAULT. [Jeff Trawick]
+
+ *) Fix a bug parsing configuration file containers. With a sequence
+ like this in the config file
+
+ <IfModule mod_kilroy.c>
+ any stuff
+ </IfModule>
+ <IfModule mod_lovejoy.c>
+ (blank line)
+ any stuff
+ </IfModule>
+
+ the second container would be terminated at the blank line due to
+ sediment in the buffer from reading the prior </IfModule> and an
+ error message would be generated for the real </IfModule> for the
+ second container. Also due to this problem, any two characters
+ could be used for "</" in the close of a container.
+ [Jeff Trawick]
+
+ *) ap_add_filter prototype changed to remove the ctx pointer. The
+ pointer still remains in the filter structure, but it can not be
+ a part of the ap_add_filter prototype. The reason is that when
+ the core uses AddFilter to add a filter to the stack it doesn't
+ know how to allocate the ctx pointer, or even how much memory should
+ be allocated. The filters will have to be responsible for allocating
+ the ctx memory when they need it.
+ [Ryan Bloom]
+
+ *) Add an AddFilter directive. This directive takes a list of filters
+ that should be activated for the requested resource.
+ [Ryan Bloom]
+
+ *) apr_snprintf(): Get quad format strings working on OS/390 (and perhaps
+ some other platforms). [Jeff Trawick]
+
+ *) Modify mod_include to be a filter. Currently, it has only been tested
+ on actual files, but it should work for CGI scripts too.
+ [Ryan Bloom]
+
+ *) apr_putc(), apr_puts() for Unix: handle buffered files and interrupted
+ writes. apr_flush() for Unix: handle interrupted writes.
+ [Jeff Trawick]
+
+ *) NameVirtualHost can now take "*" as an argument instead of
+ an IP address. This allows you to create a purely name-based
+ virtual hosting server that does not have any IP addresses in
+ the configuration file and which ignores the local address
+ of any connections. PR #5595, PR #4455 [Tony Finch]
+
+ *) Fix some compile warnings in mod_mmap_static.c
+ [Mike Abbott <mja@sgi.com>]
+
+ *) Fix chunking problem with CGI scripts. The general problem was that
+ the CGI modules were adding an EOS bucket and then the core added an
+ EOS bucket. The chunking filter finalizes the chunked response when it
+ encounters an EOS bucket. Because two EOS buckets were sent, we
+ finalized the response twice. The fix is to make sure we only send one
+ EOS, by utilizing a flag in the request_rec.
+ [Ryan Bloom]
+
+ *) apr_put_os_file() now sets up the unget byte appropriately on Unix
+ and Win32. Previously, the first read from an apr_file_t set up via
+ apr_put_os_file() would return a '\0'. [Jeff Trawick]
+
+ *) Mod_cgid now creates a single element bucket brigade, with a pipe
+ bucket, instead of using BUFF's and ap_r*.
+ [Ryan Bloom]
+
+ *) APRVARS.in no longer overwrites the EXTRA_LIBS variable.
+ [Mike Abbott <mja@sgi.com>]
+
+ *) Remove ap_bopenf from buff code. This required modifying the file_cache
+ code to use APR file's directly instead of going through BUFFs.
+ [Ryan Bloom]
+
+ *) Fix compile break on some platforms for mod_mime_magic.c
+ [John K. Sterling <sterling@covalent.net>]
+
+ *) Fix merging of AddDefaultCharset directive.
+ PR #5872 (1.3) [Jun Kuriyama <kuriyama@imgsrc.co.jp>]
+
+ *) Minor revamp of the rlimit sections of code. We now test
+ explicitly for setrlimit and getrlimit. Also, unixd_set_rlimit()
+ is now "available" even if the platform doesn't support
+ the rlimit family (it's just a noop though). [Jim Jagielski]
+
+ *) Migrate the pre-selection of which MPM to use for specific
+ platforms to hints.m4, which contains (or should contain)
+ all platform specific "hints". [Jim Jagielski]
+
+ *) Remove IOLs from Apache. With filtering, IOLs are no longer necessary
+ [Ryan Bloom]
+
+ *) Add tables with non-string/binary values to APR.
+ [Ken Coar]
+
+ *) Fix some bad calls to ap_log_rerror() in mod_rewrite.
+ [Jeff Trawick]
+
+ *) Update PCRE to version 3.2. [Ryan Bloom]
+
+ *) Change the way buckets' destroy functions are called so that
+ they can be more directly used when changing the type of a
+ bucket in place. [Tony Finch]
+
+ *) Add generic support for reference-counting the resources used by
+ buckets, and alter the HEAP and MMAP buckets to use it. Change
+ the way buckets are initialised to support changing the type of
+ buckets in place, and use it when setting aside TRANSIENT buckets.
+ Change the implementation of TRANSIENT buckets so that it can be
+ mostly shared with IMMORTAL buckets, which are now implemented.
+ [Tony Finch]
+
+Changes with Apache 2.0a6
+
+ *) Add support to Apache and APR for dsos on OS/390. [Greg Ames]
+
+ *) Add a chunking filter to Apache. This brings us one step closer
+ to removing BUFF. [Ryan Bloom]
+
+ *) ap_add_filter now adds filters in a LIFO fashion. The first filter
+ added to the stack is the last filter to be called. [Ryan Bloom]
+
+ *) Apache 2.0 has been completely documented using Scandoc. The
+ docs can be generated by running 'make docs'. [Ryan Bloom]
+
+ *) Add filtered I/O to Apache. This is based on bucket brigades,
+ Currently the buckets still use BUFF under the covers, but that
+ should change quickly. The only currently written filter is the
+ core filter which just calls ap_bwrite. [The Apache Group]
+
+ *) APR locks on Unix: Let APR_LOCKALL locks work when APR isn't
+ built with thread support. [Jeff Trawick]
+
+ *) Abort configuration if --with-layout was specified and there's
+ no layout definition file. [Ken Coar]
+
+ *) Add support for '--with-port=n' option to configure. [Ken Coar]
+
+ *) Add support for extension methods for the Allow response header
+ field, and an API routine for accessing r->allowed and the
+ list of extension methods in a unified manner. [Ken Coar]
+
+ *) mod_cern_meta: fix broken file reading loop in scan_meta_file().
+ [Rob Simonson <simo@us.ibm.com>]
+
+ *) Get xlate builds working again. The apr renaming in 2.0a5 broke
+ APACHE_XLATE builds. [Jeff Trawick]
+
+ *) A configuration file parsing problem was fixed. When the
+ configuration file started with an IfModule/IfDefine container,
+ only the last statement in the container would be retained.
+ [Jeff Trawick]
+
+Changes with Apache 2.0a5
+
+ *) Perchild is serving pages after passing them to different child
+ processes. There are still a lot of bugs, but this does work. I
+ have made requests against the same installation of Apache, and had
+ different servers use different user IDs to serve the responses.
+ This change moves to using socketpair instead of an AF_UNIX socket.
+ [Ryan Bloom]
+
+ *) Perchild MPM still doesn't work perfectly, but it is serving pages.
+ It can't seem to pass between child processes yet, but I think we
+ are closer now than before. This moves us back to using Unix
+ Domain Sockets. [Ryan Bloom]
+
+ *) libapr functions and types renamed with apr_ prefix.
+ #include "apr_compat.h" for 1.3.x backwards compat
+ [Perl]
+
+ *) Fix problems with APR sockaddr handling on Win32. It didn't always
+ return the right information on the local socket address.
+ [Gregory Nicholls <gnicholls@level8.com>]
+
+ *) ap_recv() on Win32: Set bytes-read to 0 on error.
+ [Gregory Nicholls <gnicholls@level8.com>]
+
+ *) Add an option to not detach from the controlling terminal without
+ going into single process mode. This allows for much easier
+ debugging of the process startup code. [Ryan Bloom]
+
+ *) ab: don't use perror() to report the failure of an APR function.
+ [Jeff Trawick]
+
+ *) Make dexter, mpmt_pthread, and perchild MPMs not destroy the
+ scoreboard on graceful restarts.
+ [Ryan Bloom]
+
+ *) Fix segfault/SIGSEGV when running gzip from mod_mime_magic.c.
+ An invalid ap_proc_t was passed to ap_create_process().
+ [Jeff Trawick]
+
+ *) Allow modules to register filters. Those filters are still
+ never called, but this is a step in the right direction.
+ [Ryan Bloom and Greg Stein]
+
+ *) Register the mod_cgid daemon process for cleanup so that it is
+ killed at termination if it does not die when the parent gets
+ SIGTERM. This change is to fix occasional problems where the
+ process stays around. Bugs in similar logic in mod_rewrite and
+ mod_include were also fixed. [Jeff Trawick]
+
+ *) Fix a bug in the time handling. Basically, we were imploding a time
+ in ap_parseHTTPdate, but it had bogus data in the exploded time format.
+ Namely, tm_usec and tm_gmtoff were not filled out. ap_implode_time
+ uses those two fields to adjust the time value. Because of the HTTP
+ spec, both of those values can be zero'ed out safely. This fixes
+ the bug correctly. [Ryan Bloom]
+
+ *) Fix a couple of place in the Windows code where the wrong error
+ code was being returned. [Gregory Nicholls <gnicholls@level8.com>]
+
+ *) Fix POOL_DEBUG (at least for prefork mpm). [Dean Gaudet]
+
+ *) Added the APR_EOL_STR macro for platform dependent differences in
+ logfiles and other raw text (such as all APR files). Fixes logfiles
+ not terminated with cr/lf sequences in Win32. [William Rowe]
+
+ *) Move all strings functions in APR to src/lib/apr/strings and create
+ apr_strings.h for the prototypes. [Ryan Bloom]
+
+ *) APR lock fixes: when using SysV sems, flock(), or fcntl(), be sure
+ to repeat the syscall until we stop getting EINTR. I noticed a
+ related problem at termination (SIGTERM) on FreeBSD when using
+ fcntl(). Apache 1.3 had these new loops too. Also, make the flock()
+ implementation work properly with child init. Previously, ap_lock()
+ was essentially a no-op because all children were using different
+ locks and thus nobody ever blocked. [Jeff Trawick]
+
+ *) The htdocs/ tree has been moved out of the CVS source tree into
+ a separate area for easier development. This has NO EFFECT on
+ end-users or Apache installations. [Ken Coar]
+
+ *) Integrate the mod_dav module for WebDAV protocol handling. This
+ adds the dav and dav_fs modules, the SDBM library, and additional
+ XML handling utilities. [Greg Stein]
+
+ *) Clean out obsolete names (from httpd.h) for the HTTP Status Codes
+ [Greg Stein]
+
+ *) Update the lib/expat-lite/ library (bring forward changes from
+ the Apache 1.3 repository). [Greg Stein]
+
+ *) If sizeof(long long) == sizeof(long), then prefer long in APR
+ configure.in. [Dave Hill <ddhill@zk3.dec.com>]
+
+ *) Add ap_sendfile for Tru64 Unix. Also, add an error message for
+ machines where sendfile is detected, but nobody has written ap_sendfile.
+ [Dave Hill <ddhill@zk3.dec.com>]
+
+ *) Compile fixes in mod_mmap_static. [Victor J. Orlikowski]
+
+ *) ab would start up more connections than needed, then quit when the
+ desired number were finished. Also fixed a logic error involving
+ ab keepalives. [Victor J. Orlikowski]
+
+ *) WinNT: Implement non-blocking pipes with timeouts to communicate
+ with CGIs. Apache 2.0a4 had non-blocking pipes but without
+ timeouts (i.e, if a timeout was specified, the pipe reverted to
+ a full blocking pipe). Now the behaviour is more in line with
+ Unix non-blocking pipes.
+ [Bill Stoddard]
+
+ *) WinNT: Implement accept socket reuse. Using mod_file_cache to
+ cache open file handles along with accept socket reuse enables
+ Apache 2.0 to serve non-keepalive requests for static files at
+ 3x the rate of Apache 1.3.(e.g, Apache 1.3 will serve 400 rps
+ and Apache 2.0 will serve almost 1200 rps on my system).
+ [Bill Stoddard]
+
+ *) Merge mod_mmap_static function into mod_file_cache. mod_file_cache
+ supports two config directives, mmapfile (same behavious as
+ mod_mmap_static) and cachefile. Use the cachefile directive
+ to cache open file handles. This directive only works on systems
+ that have implemented the ap_sendfile API. cachefile works today
+ on Windows NT, but has not been tested on any flavors of Unix.
+ [Bill Stoddard]
+
+ *) Cleanup the configuration. With the last few changes the
+ configuration process automatically:
+ inherits information about how to build from APR. Allowing
+ APR to inform Apache that it should or should not use -ldl
+
+ Detects which mod_cgi should be used mod_cgi or mod_cgid,
+ based on the threading model
+
+ Apache calls APR's configure process before finishing it's
+ configuration processing, allowing for more information flow
+ between the two.
+ [Ryan Bloom]
+
+
+ *) Change Unix and Win32 ap_setsockopt() so that APR_SO_NONBLOCK
+ with non-zero argument makes the socket non-blocking. BeOS and
+ OS/2 already worked this way. [Jeff Trawick]
+
+ *) ap_close() now calls ap_flush() for buffered files, so write
+ operations work a whole lot better on buffered files.
+ [Jeff Trawick]
+
+ *) Fix error messages issued from MPMs which explain where to change
+ compiled-in limits (e.g., ThreadsPerChild, MaxClients, StartTreads).
+ [Greg Ames]
+
+ *) ap_create_pipe() now leaves pipes in blocking state. (This helps
+ reduce the number of syscalls on Unix.) ap_set_pipe_timeout() is
+ now the way that the blocking state of a pipe is manipulated.
+ ap_block_pipe() is gone. [Jeff Trawick]
+
+ *) Correct the problem where the only local host name that the IP stack
+ can discover are 'undotted' private names. If no fully qualified
+ domain name can be identified, the default ServerName will be set to
+ the machine's IP address string. A warning is always provided if the
+ ServerName not specified, but assumed. Solves PR6215 [William Rowe]
+
+ *) Repair problems with config file processing which caused segfault
+ at init when virtual hosts were defined and which caused ServerName to
+ be ignored when there was no valid DNS setup. [Jeff Trawick]
+
+ *) Removed pointless ap_is_aborted macro function. [Roy Fielding]
+
+ *) Add ap_sendfile implementation for AIX
+ [Victor J. Orlikowski]
+
+ *) Repair C++ compatibility in ap_config.h, apr_file_io.h,
+ apr_network_io.h, and apr_thread_proc.h.
+ [Tyler J. Brooks <tylerjbrooks@home.com>, Jeff Trawick]
+
+ *) Bring the allocation and pool debugging code back into a working
+ state. This will need to be tested as so far it's only been used on
+ BeOS. [David Reid]
+
+ *) Change configuration command setup to be properly typesafe when in
+ maintainer mode. Note that this requires a compiler that can initialise
+ unions. [Ben Laurie]
+
+ *) Turn on buffering for config file reads. Part of this was to
+ repair buffered I/O support in Unix and implement buffered
+ ap_fgets() for all platforms. [Brian Havard, Jeff Trawick]
+
+ *) Win32: Fix problem where UTC offset was not being set correctly
+ in the access log. Problem reported on news group by Jerry Baker.
+ [Bill Stoddard]
+
+ *) Fix segfault when reporting this type of syntax error:
+ "</container> without matching <container> section", where
+ container is VirtualHost or Directory or whatever.
+ [Jeff Trawick]
+
+ *) Prevent the source code for CGIs from being revealed when using
+ mod_vhost_alias and the CGI directory is under the document root
+ and a user makes a request like http://www.example.com//cgi-bin/cgi
+ as reported in <news:960999105.344321@ernani.logica.co.uk>
+ [Tony Finch]
+
+ *) Add support for the new Beos NetwOrking Environment (BONE)
+ [David Reid]
+
+ *) xlate: ap_xlate_conv_buffer() now tells the caller when the
+ final input char is incomplete; ap_bwrite_xlate() now handles
+ incomplete final input chars. [Jeff Trawick]
+
+ *) Yet another update to saferead/halfduplex stuff -- need to ensure
+ that a bhalfduplex call occurs before logging or else DNS and
+ such can delay the last packet of the response. [Dean Gaudet]
+
+ *) Some syscall reduction in APR on unix -- don't seek when setting
+ up an mmap; and don't fcntl() more than once per socket.
+ [Dean Gaudet]
+
+ *) When mod_cgid is started as root, the cgi daemon now switches
+ to the configured User/Group (like other httpd processes)
+ instead of continuing as root. [Jeff Trawick]
+
+ *) The prefork MPM now uses an APR lock for the accept() mutex.
+ It has not been getting a lock at all recently. httpd -V now
+ displays APR's selection of the lock mechanism instead of the
+ symbols previously respected by prefork. [Jeff Trawick]
+
+ *) Change the mmap() feature test to check only for existence.
+ The previous check required features not used by Apache.
+ [Greg Ames]
+
+ *) Fix a couple of bugs in mod_cgid: The cgi arguments were
+ sometimes mangled. The len parm to accept() was not
+ initialized, leading sometimes to an endless loop of failed
+ accept() calls on OS/390 and anywhere else that failed the call
+ if the len was negative. Use <sys/un.h> for struct sockaddr_un
+ instead of declaring it ourselves to fix a compilation problem
+ on Solaris. [Jeff Trawick]
+
+ *) Add Resource limiting code back into Apache 2.0. [Ryan Bloom]
+
+ *) Fix zombie process problem with mod_cgi. [Jeff Trawick]
+
+ *) Port mod_mmap_static to 2.0. Make it go faster. [Greg Ames]
+
+ *) Fix storage overlay when loading dsos. Symptom: Apache dies at
+ initialization if ALLOC_DEBUG is defined; no known symptom
+ otherwise. [Jeff Trawick]
+
+ *) Fix typo in configure script when checking for mod_so. bash
+ doesn't seem to have a problem but /bin/sh on Solaris does.
+ Symptom: "./configure: test: unknown operator =="
+ [Jeff Trawick]
+
+ *) Rebind the Win32 NT and 9x services control into the MPM.
+ All console, WinNT SCM and Win9x pseudo-service control code is
+ now wrapped within the WinNT MPM.
+ [William Rowe]
+
+ *) Make a copy of getenv("PATH") before storing for later use. Some
+ getenv() implementations use the same storage for successive calls.
+ CGIs on OS/390 had a bad PATH due to this. [Jeff Trawick]
+
+ *) Server Tokens work in 2.0 again. This also propogates the change
+ to allow just the product name in the server string using
+ PRODUCT_ONLY.
+ [Ryan Bloom]
+
+Changes with Apache 2.0a4
+
+ *) EBCDIC: Rearrange calls to ap_checkconv() so that most handlers
+ won't need to call it. [Greg Ames, Jeff Trawick]
+
+ *) Move pre_config hook call to between configuration read and config
+ tree walk. This allows all modules to implement pre_config hooks
+ and know that they will be called at an appropriate time.
+ [Ryan Bloom]
+
+ *) mod_cgi, mod_cgid: Make ScriptLog directive work again.
+ [Jeff Trawick]
+
+ *) Add pre-config hooks back to all modules.
+ [Ryan Bloom]
+
+ *) Fix a SIGSEGV in ap_md5digest(), which is used when you have
+ ContentDigest enabled and we can't/don't mmap the file.
+ [Jeff Trawick]
+
+ *) We now report the correct line number for syntax errors in config
+ files. [Ryan Bloom, Greg Stein, Jeff Trawick]
+
+ *) Brought mod_auth_digest up to synch with 1.3, fixed ap_time_t-
+ related bugs, and changed shmem/locking to use apr API. Shared-mem
+ is currently disabled, however, because of problems with graceful
+ restarts. [Ronald Tschalär]
+
+ *) Fix corruption of IFS variable in --with-module= handling.
+ Depending on the user's shell or customization thereof, there
+ would be errors generating ap_config_auto.h later in the configure
+ procedure. [Jeff Trawick]
+
+ *) mod_cgi: Restore logging of stderr from child process when ScriptLog
+ isn't used (as in 1.3), except that on Unix it is now logged via
+ ap_log_rerror() instead of by the child having STDERR_FILENO refer
+ to the error log. [Greg Ames, Jeff Trawick]
+
+ *) Add '-D' argument processing for run time configuration defines.
+ [William Rowe]
+
+ *) Organize http_main.c as independent code, such that no code or
+ global data is exported from it. WIN32 will dynamically link it
+ to the server core, so this will prevent mutual dependency.
+ [William Rowe]
+
+ *) Add separate dynamic linkage tags APR_EXPORT(), APR_EXPORT_NONSTD()
+ and APR_VAR_EXPORT to correctly resolve apr functions and globals.
+ [William Rowe]
+
+ *) Add Win9x service execution and Ctrl+C/Ctrl+Break/Shutdown handlers.
+ [William Rowe, Jan Just Keijser <KEIJSERJJ@logica.com>]
+
+ *) Add mod_charset_lite for configuring character set translation.
+ [Jeff Trawick]
+
+ *) Add '-n' option to htpasswd to make it print its user:pw record
+ on stdout rather than having to frob a text file. [Ken Coar]
+
+ *) Fix saferead. Basically, we flush the output buffer if a read on the
+ input will block.
+ [Ryan Bloom]
+
+ *) APR: Add ap_xlate_get_sb() so that an app can find out whether or not
+ a conversion is single-byte only. [Jeff Trawick]
+
+ *) BEOS: ap_shutdown should return APR_SUCCESS or errno. Note that
+ the BeOS 5.0 documentation says that shutdown doesn't work yet.
+ [Roy Fielding]
+
+ *) Fix some minor errors where pid was being manipulated as an int
+ instead of the portable pid_t. [Roy Fielding]
+
+ *) Fix some error log prints that were printing the pointer to a
+ structure rather than the pid within the structure.
+ [Jeff Trawick, Roy Fielding]
+
+ *) ab: Fix a command-line processing bug; track bad headers in
+ err_response; support reading headers up to 2K.
+ [Ask Bjoern Hansen <ask@valueclick.com>]
+
+ *) Fix ap_resolve_env() so that it handles new function added in a prior
+ alpha (see "Added the capability to do ${ENVVAR} constructs in the
+ config file.") as well as the constructs used by mod_rewrite.
+ [Paul Reder <rederpj@raleigh.ibm.com>]
+
+ *) Apache 2.0 builds and runs on OS/390. [Jeff Trawick, Greg Ames]
+
+ *) Change the EBCDIC support in functions for MD5, SHA1, and base 64 to use
+ APR to perform translation, instead of accessing the hard-coded tables
+ in 1.3's ebcdic.c. [Jeff Trawick]
+
+ *) Fix some bugs (mostly lost 1.3 code) in ab's command-line processing.
+ [Jeff Trawick]
+
+ *) Add the ability to hook into the config file reading phase. Basically
+ if a directive is specified EXEC_ON_READ, then when that directive is
+ read from the config file, the assocaited function is executed. This
+ should only be used for those directives that must muck with HOW the
+ server INTERPRETS the config. This should not be used for directives
+ that re-order or replace items in the config tree. Those changes should
+ be made in the pre-config step.
+ [Ryan Bloom]
+
+ *) Add mod_example to the build system.
+ [Tony Finch]
+
+ *) APR: Add ap_xlate_conv_byte() to convert one char between single-
+ byte character sets. [Jeff Trawick]
+
+ *) Pick up various EBCDIC fixes from 1.3 (from Martin
+ Kraemer and Oliver Reh originally according to the change log).
+ [Jeff Trawick]
+
+ *) Fix a couple of problems in RFC1413 support (controlled by the
+ IdentityCheck directive). Apache did not build the request string
+ properly and more importantly Apache would loop forever if the
+ would-be ident server dropped the connection before sending a
+ properly terminated response. [Jeff Trawick]
+
+ *) apxs works in 2.0.
+ [Ryan Bloom]
+
+ *) Reliable piped logs work in 2.0.
+ [Ryan Bloom]
+
+ *) Introduce a hash table implementation into APR to be used for
+ replacing tables and other random data structures in Apache.
+ [Tony Finch]
+
+ *) Add some more error reporting to htpasswd in the case of problems
+ generating or accessing the temporary file. Also, pass in a
+ buffer if the implementation knows how to use it (i.e., if L_tmpnam
+ is defined). [Ken Coar]
+
+ *) Configure creates config.nice now containing your configure
+ options. Syntax: ./config.nice [--more-options]
+ [Sascha Schumann]
+
+ *) Fix various return code problems in APR on Win32. For most of
+ these, APR was returning APR_EEXIST instead of GetLastError()/
+ WSAGetLastError(). [Jeff Trawick]
+
+ *) Make piped logs work again in version 2.0
+ [Ryan Bloom]
+
+ *) Add VPATH support to UNIX build system of Apache and APR.
+ [Sascha Schumann]
+
+ *) Fix ap_tokenize_to_argv to respect the const arguments that are
+ passed to it.
+ [Ryan Bloom]
+
+ *) Fix mm's memcpy/memset macros, pointer arithmetic was broken.
+ Patch submitted to author.
+ [Sascha Schumann]
+
+ *) Fix mm configuration on Solaris 8 x86 and OS/390. Don't require
+ /sbin in PATH on FreeBSD (all submitted to rse previously)
+ [Jeff Trawick]
+
+ *) Fix building Pthread-based MPMs on OpenBSD
+ [Sascha Schumann] PR#26
+
+ *) Fix ap_readdir() problem on systems where d_name[] field in
+ struct dirent is declared with only one byte. (This problem only
+ affected multithreaded builds.) This caused a segfault during
+ pool cleanup with mod_autoindex on Solaris (Solaris 8 x86, at
+ least). [Jeff Trawick]
+
+ *) Fix some make-portability problems on at least Tru64, Irix
+ and UnixWare.
+ [Sascha Schumann] PR#18, PR#39
+
+ *) Add ap_sigwait() to support old-style sigwait() on systems
+ like OS/390 and UnixWare.
+ [Sascha Schumann]
+
+ *) Add POSIX-thread flags for more platforms.
+ [Sascha Schumann]
+
+ *) Fix some minor bugs in ap_strerror(). Teach ap_strerror()
+ (on Unix, at least) to handle resolver errors. Fix a bug in
+ the definition of APR_ENOMEM so that ap_strerror() can spit
+ out the correct error message for it.
+ [Jeff Trawick]
+
+Changes with Apache 2.0a3
+
+ *) mod_so reports ap_os_dso_error() if ap_dso_load() fails
+ [Doug MacEachern]
+
+ *) API: *HOOK* macros now have an AP_ prefix
+ [Doug MacEachern]
+
+ *) Win32: Eliminate redundant calls to initialize winsock.
+ [Tim Costello <timcostello@ozemail.com.au>]
+
+ *) Fix bugs initializing ungetchar for pipes.
+ [Chia-liang Kao <clkao@CirX.ORG>]
+
+ *) The ab program in the src/support directory is now portable using
+ APR.
+ [Ryan Bloom]
+
+ *) Support directory is being compiled when the server is built
+ [Ryan Bloom]
+
+ *) The configure option --with-program-name has been added to allow
+ developers to rename the executable at configure time. This also
+ changes the name of the config files to match the executable's name.
+ [Ryan Bloom]
+
+ *) mod_autoindex: Add `IndexOptions +VersionSort', to nicely sort filenames
+ containing version numbers. [Martin Pool]
+
+ *) ap_open(..,APR_OS_DEFAULT,..) uses perms 0666 instead of 0777 on
+ Unix; access_log and error_log now created with these perms; non-
+ Unix is unaffected [Jeff Trawick]
+
+ *) Finished move of ap_md5 routines to apr_md5. Removed ap_md5.h.
+ Replaced more magic numbers with MD5_DIGESTSIZE.
+ [William Rowe, Roy Fielding]
+
+ *) Win32: Get mod_auth_digest compiling and added to the Windows
+ build environment. Not tested and I'd be suprised if it
+ actually works. [Bill Stoddard]
+
+ *) Revamp the Win32 make environment. Makefiles have been removed and
+ Apache.dsw created to bring together all the pieces. Create new file
+ os/win32/BaseAddr.ref to define module base addresses (to prevent
+ dll relocation at start-up).
+ [William Rowe, Greg Marr, Tim Costello, Bill Stoddard]
+
+ *) [EBCDIC] Port Paul Gilmartin's CRLF patch from 1.3. This replaces most
+ of the \015, \012, and \015\012 constants with macros.
+ [Greg Ames <gregames@us.ibm.com>]
+
+ *) Add ap_xlate_open() et al for translation of text between different
+ character sets. The initial implementation requires iconv().
+ [Jeff Trawick]
+
+ *) More FAQs and answers from comp.infosystems.www.servers.unix.
+ [Joshua Slive <slive@finance.commerce.ubc.ca>]
+
+ *) CGI output is being timed out now.
+ [Ryan Bloom]
+
+ *) Fix the problem with dieing quietly. dupfile now takes a pool which
+ is used by the new apr file. There is no reason to create a new file
+ with the same lifetime as the original file.
+ [Ryan Bloom]
+
+ *) Win32: Attempt to eliminate dll relocation at start-up by specifying
+ module base addresses. This will help shooting seg faults
+ in the field. [William Rowe <wrowe@lnd.com>]
+
+ *) Update Apache on Windows documentation. Add new document
+ describing how to compile Apache on Windows.
+ [William Rowe <wrowe@lnd.com>]
+
+ *) ap_set_pipe_timeout(), ap_poll(), and APR_SO_TIMEOUT now take
+ microseconds instead of seconds. Some storage leaks and other
+ minor bugs in related code were fixed. [Jeff Trawick]
+
+ *) Win32: First cut at getting mod_isapi working under 2.0
+ [William Rowe <wrowe@lnd.com>]
+
+ *) First stab at getting mod_auth_digest working under 2.0
+ quick change summary:
+ - moved the random byte generation (ap_generate_random_bytes) into APR
+ - now uses ap_time_t
+ - compiles and runs on linux
+ - tested with amaya
+ [Brian Martin <bmartin@penguincomputing.com>]
+
+ *) Win32: Move the space stripping of physical service names
+ fix up from Apache 1.3. #include'ing "ap_mpm.h" fixes up an
+ unresolved symbol. Add dependency checking to the
+ CreateService call to ensure TCPIP and AFP (winsock) is started
+ before Apache.
+ [William Rowe <wrowe@lnd.com>]
+
+ *) Win32: Add code to perform latebinding on functions that may
+ not exist on all levels of Windows where Apache runs. This
+ is needed to allow Apache to start-up on Win95/98. All calls
+ to non portable functions should be protected with
+ ap_oslevel checks to prevent runtime segfaults.
+ [William Rowe <wrowe@lnd.com>]
+
+ *) Fix fallback default values for SHM_R and SHM_W [Martin Kraemer]
+
+ *) Get lingering_close() working again. [Dean Gaudet, Jeff Trawick]
+
+ *) Win32: Get non-blocking CGI pipe reads working under Windows NT.
+ This addresses PR 1623. Still need to address timing out runaway
+ CGI scripts. [Bill Stoddard]
+
+ *) Win32: Make ap_stat Windows 95/98 friendly
+ [William Rowe <wrowe@lnd.com>]
+
+ *) Win32: Fix a bug in ap_get_oslevel which causes GetVersionEx() to
+ always fail. Need to initialise the dwOSVersionInfoSize member of the
+ OSVERSIONINFO struct before calling GetVersionEx, so GetVersionEx
+ always fails.
+
+ The patch also enhances ap_get_oslevel (and the associated enum) to
+ handle selected service packs for NT4, and adds recognition for
+ Windows 2000. This is useful, eg. if we can recognise NT4 SP2 then
+ we can use ReadFileScatter and WriteFileGather in readwrite.c.
+ [Tim Costello <Tim.Costello@BTFinancialgroup.com>]
+
+ *) Get mod_rewrite building and running, and mod_status building for Win NT
+ [Allan Edwards <ake@raleigh.ibm.com>]
+
+ *) Patch to port mod_auth_db to the 2.0 api and also to support
+ Berlekey DB 3.0. It works for me with both Berkeley DB 3.0.55 and
+ 2.7.7. It should work with version 1 as well but I haven't tested it.
+ [Brian Martin <bmartin@penguincomputing.com>]
+
+ *) Get APR DSO code working under Windows. Includes cross platform
+ fixes to mod_so.c.
+ [Tim.Costello@BTFinancialgroup.com]
+
+ *) Fix some of the Windows APR time functions.
+ [William Rowe]
+
+ *) FAQ changes related to tidying up historical documents on the web site.
+ [Joshua Slive <slive@finance.commerce.ubc.ca>]
+
+ *) Move Windows DSO code into APR.
+ [Bill Stoddard]
+
+ *) Eliminate apr_win.h and apr_winconfig.h (and the ugly #ifdefs they cause).
+ Now, apr.h and apr_config.h are generated from apr.hw and apr_config.hw
+ at build time. At this point, the server will not compile on Windows because
+ of the recent DSO commits. Fixing those next.
+ [Bill Rowe & Bill Stoddard]
+
+ *) Added error checking for file I/O APR routines.
+ [Jon Travis <jtravis@covalent.net>]
+
+ *) APR: Don't use the values of resolver error codes for the
+ corresponding APR error codes. On Unix and Win32, return the
+ proper APR error code after a resolver error. [Jeff Trawick]
+
+Changes with Apache 2.0a2
+
+ *) Renamed the executable back to httpd on all platforms other
+ than Win32
+ [Ryan Bloom]
+
+ *) Allow BeOS to survive restarts, log properly and a few
+ small things it had problems with due to the way it setup
+ users and groups. [David Reid]
+
+ *) Get mod_rewrite working with APR locks
+ [Paul Reder <rederpj@raleigh.ibm.com>]
+
+ *) Actually remove the sempahore when the lock cleanup routine
+ is called on BeOS. [David Reid]
+
+ *) Clear hook registrations between reads of the config file.
+ When DSOs are unloaded and re-loaded the old hook pointers may
+ no longer be valid. This fix eliminates potential segfaults.
+ [Allan Edwards <ake@raleigh.ibm.com>]
+
+ *) Fix a problem with Sigfunc not being defined or bypassed
+ if sigaction() wasn't found. [Jim Jagielski]
+
+ *) Fix the locking mechanism on BSD variants. They now use fcntl
+ locks. This allows the server to start and serve pages.
+ [Ryan Bloom]
+
+ *) First cut at getting the Win32 installer to work
+ [William Rowe <wrowe@lnd.com>]
+
+ *) Get htpasswd compiling under Windows
+ [William Rowe <wrowe@lnd.com>]
+
+ *) Change the log message for a bind() failure to show the
+ interface and port number. [Jeff Trawick]
+
+ *) Import the documentation from 1.3.12 and bring parts of it
+ up-to-date with respect to the changes that have occurred
+ in 2.0.
+ [Tony Finch]
+
+ *) BeOS MPM updated. CGI bug on BeOS fixed. IP addresses
+ now logged correctly on BeOS.
+ [David Reid]
+
+ *) Create one makefile for all Win32 distributions (NT/2000/95/98).
+ Makefile.win includes the same user interface as the old
+ Makefile.nt
+ [William Rowe <wrowe@lnd.com>, Jeff Trawick <trawick@us.ibm.com>]
+
+ *) Win32 exec now uses COMSPEC environment string for command
+ shell path resolution.
+ [William Rowe <wrowe@lnd.com>] PR#3715
+
+ *) Win32: ap_connect() was not returning correct error condition
+ PR5866
+ [Allen Prescott <allen@clanprescott.com>]
+
+ *) Win32: ap_open() was broken on Win9x because an NT-specific
+ flag was passed to CreateFile. ap_puts() added an unnecessary
+ '\n'.
+ [Jeff Trawick <trawick@us.ibm.com>]
+
+ *) Put in Korean and Norwegian index.html pages (2.0 and 1.3)
+ which where donated by Lee Kuk Hyun and Lorant Czaran. 'Fixed'
+ confusing ee/et name and made all extensions language/dialect
+ rather than country reflecting. Changed example files to
+ explicit reflect the ISO charset and added a few common
+ ones to the example config [dirkx]
+
+ *) Extend external module capability. To use this, you call
+ configure with --with-module=path/to/mod1,path/to/mod2,etc.
+ [Ryan Bloom]
+
+ *) Backported the various "default charset" fixes from 1.3.12,
+ including the AddDefaultCharset directive. [Jim Jagielski]
+
+ *) Added the capability to do ${ENVVAR} constructs in the
+ config file. E.g. 'ServerAdmin ${POSTMASTER}'. As commited
+ it does this on a line by line basis; i.e. if the envvar
+ expands to something with spaces you have to protect it
+ by adding quotes around it (Unless of course you expect it
+ to contains more than one argument. Alternatively you
+ can compile it on a per token basis; which is what people
+ usually expect by setting RESOLVE_ENV_PER_TOKEN. But this
+ hampers fancier hacks.
+ [Dirk-Willem van Gulik]
+
+ *) Changed the 'ErrorDocument' syntax in that it NO longer
+ supports the asymetric
+
+ ErrorDocument 301 "Some message
+
+ Note the opening " quote, without a closing quote. It now
+ has either the following syntaxes
+
+ ErrorDocument XXX /local/uri
+ ErrorDocument XXX http://valid/url
+ ErrorDocument XXX "Some Message"
+
+ The recognition heuristic is: if it has a space it
+ is a message. If it has no spaces and starts with a /
+ or is a valid URL then treat it that way. Otherwise it
+ is assumed to be a message.
+
+ This breaks backward compatibility but makes live a hell
+ of a lot easier for GUI's and config file parsers.
+ [Dirk-Willem van Gulik]
+
+ *) Changed 'CacheNegotiatedDocs' from its present/not-present
+ syntax into a 'on' or 'off' syntax. As it currently is the
+ only non nesting token which uses NO_ARGS and thus is an
+ absolute pain for any config interface automation. This
+ breaks backward compatibility. [Dirk-Willem van Gulik]
+
+ *) Add ability to add external modules to the build process. This is
+ done with --with-module=/path/to/module. Modules can only be added
+ as static modules at this point.
+ [Ryan Bloom]
+
+Changes with Apache 2.0a1
+
+ *) Fix FreeBSD 3.3 core dump.
+ Basically, ap_initialize() needs to get called before
+ create_process(), since create_process() passes op_on structure
+ to semop() to get a lock, but op_on isn't initialized until
+ ap_initialize() calls setup_lock(). Here is a slight
+ rearrangement to main() which calls ap_initialize() earlier...
+ [Jeff Trawick <trawick@us.ibm.com>]
+
+ *) Enable Apache to use sendfile/TransmitFile API
+ [Bill Stoddard, David Reid, Paul Reder]
+
+ *) Re-Implement Win32 APR network I/O APIs and most of the file I/O
+ APIs.
+ [Bill Stoddard]
+
+ *) Make file I/O and network I/O writev/sendv APIs consistent.
+ Eliminate use of ap_iovec_t and use Posix struct iovec.
+ Use seperate variable on ap_writev to set the number of iovecs
+ passed in and number of bytes written.
+ [Bill Stoddard]
+
+ *) Adapt file iol to use APR functions. Replaced ap_open_file()
+ with ap_create_file_iol(). ap_create_file_iol() requires that
+ the file be opened prior to the call using ap_open().
+ [Bill Stoddard]
+
+ *) Port mod_include and mod_cgi to 2.0
+ [Paul Reder, Bill Stoddard]
+
+ *) ap_send{,v}, ap_recv, ap_sendfile API clarification --
+ bytes_read/bytes_written is always valid (never -1). Plus
+ some fixes to buff.c to correct problems introduced by the
+ errno => ap_status_t changes a while back. Plus a fix to
+ chunked encoding introduced right at the beginning of 2.0.
+ [Dean Gaudet]
+
+ *) Revamped UNIX build system to use autoconf and libtool.
+ [Manoj Kasichainula, Sascha Schumann]
+
+ *) port mod_rewrite to 2.0. [Paul J. Reder <rederpj@raleigh.ibm.com>]
+
+ *) More rigorous checking of Host: headers to fix security problems
+ with mass name-based virtual hosting (whether using mod_rewrite
+ or mod_vhost_alias).
+ [Ben Hyde, Tony Finch]
+
+ *) Add back support for UseCanonicalName in <Directory> containers.
+ [Manoj Kasichainula]
+
+ *) Added APLOG_STARTUP log type. This allows us to write an error
+ message without any of the date and time information. As a part
+ of this change, I also removed all of the calls to fprintf(stderr
+ and replaced them with calls to ap_log_error using APLOG_STARTUP
+ writing to stderr is no longer portable, because we don't direct
+ stderr to the error log on all platforms.
+ [Ryan Bloom]
+
+ *) Convert error logging functions to take errno as an argument.
+ This makes our error logs more portable, because some Windows API's
+ don't set errno. This change allows us to still output a valid
+ message on all of our platforms.
+ [Ryan Bloom]
+
+ *) mod_mime_magic runs in 2.0-dev now.
+ [Paul Reder <rederpj@raleigh.ibm.com>]
+
+ *) sendfile has been added to APR.
+ [John Zedlewski <zedlwski@Princeton.EDU>]
+
+ *) buff.c has been converted to no longer use errno.
+ [Manoj Kasichainula]
+
+ *) mod_speling runs in 2.0-dev now: a bug in readdir_r handling and
+ interface adaption to APR functions did it. [Martin Kraemer]
+
+ *) Support DSOs properly on 32-bit HP-UX 11.0
+ [Dilip Khandekar <dilip@cup.hp.com>]
+
+ *) Updated MM in APR source tree from version 1.0.8 to 1.0.11
+ [Ralf S. Engelschall]
+
+ *) Cleaned APR build environment integration and bootstrap APR
+ automatically for developers from src/Configure.
+ [Ralf S. Engelschall]
+
+ *) Fixed building of src/support/htpasswd.c
+ [Ralf S. Engelschall]
+
+ *) When generating the Location: header, mod_speling forgot
+ to escape the spelling-fixed uri. (Forw-Port from 1.3)
+ [Martin Kraemer]
+
+ *) Moved mod_auth_digest.c from experimental to standard. [Roy Fielding]
+
+ *) Change all pools to APR contexts. This is the first step to
+ incorporating APR into Apache. [Ryan Bloom]
+
+ *) Move "handler not found" warning message to below the check
+ for a wildcard handler. [Dirk <dirkm@teleport.com>, Roy Fielding]
+ PR#2584, PR#2751, PR#3349, PR#3436, PR#3548, PR#4384, PR#4795, PR#4807
+
+ *) Support line-continuation feature in config.option file and
+ allow the loading of multiple option sections at once via
+ ``--with-option=<section1>,<section2>,...''
+ [Ralf S. Engelschall]
+
+ *) Rebuilt CVS repository with Apache 1.3.9 as basis. [Roy Fielding]
+
+Changes with Apache MPM
+
+ *) Use asynchronous AcceptEx() and a completion port to accept and
+ dispatch connections to threads in Windows NT/2000.
+ [Bill Stoddard]
+
+ *) Implement WINNT Win32 MPM from original Win32 code in http_main.c
+ [Bill Stoddard]
+
+ *) Implement the APACI --with-option facility
+ (per default used the config.option file).
+ [Ralf S. Engelschall]
+
+ *) MPM BEOS port. [David Reid <abb37@dial.pipex.com>]
+
+ *) Start to implement module-defined hooks that are a) fast and b) typesafe.
+ Replace pre_connection module call with a register_hook call and
+ implement pre_connection as a hook. The intent is that these hooks will
+ be extended to allow Apache to be multi-protocol, and also to allow the
+ calling order to be specified on a per-hook/per-module basis.
+ [Ben Laurie]
+
+ *) Implement mpm_* methods as "modules". Each method gets its own
+ subdir in src/modules (eg: src/modules/prefork). Selection
+ of method uses Rule MPM_METHOD. [Jim Jagielski]
+
+ *) Port the hybrid server from the apache-apr repository as
+ mpm_mpmt_pthread. [Manoj Kasichainula]
+
+ *) os/unix/unixd.[ch]: detach, setuid, setgid, stuff which will be common
+ amongst the unix MPMs.
+
+ *) mpm_prefork: throw away all the alarm/timeout crud; and clean up the
+ signal handling for the new world order. [Dean Gaudet]
+
+ *) Crude ap_thread_mutex abstraction so that we get the pthread stuff out
+ of alloc.c for now. [Dean Gaudet]
+
+ *) Handle partial large writes correctly. [Ben Laurie]
+
+ *) Eliminate conn_rec's pointer to server. All it knows is the base server
+ based on IP/port. [Ben Laurie]
+
+ *) Port a bunch of modules to the new module structure.
+ ["Michael H. Voase" <mvoase@midcoast.com.au>]
+
+ *) I/O layering and BUFF revamp. See docs/buff.txt. [Dean Gaudet]
+
+ *) Basic restructuring to introduce the MPM concept; includes various
+ changes to the module API... better described by
+ docs/initial_blurb.txt. [Dean Gaudet]
+
+Changes with Apache pthreads
+
+ *) New buff option added: BO_TIMEOUT. It describes the timeout for
+ buff operations (generally over a network).
+ [Dean Gaudet, Ryan Bloom, Manoj Kasichainula]
+
+ *) Created http_accept abstraction. Added 4 new functions (not exported):
+ init_accept(), begin_accepting_requests(), get_request(),
+ stop_accepting_requests() [Bill Stoddard]
+
+ *) Fix to ap_rprintf call that allows mod_info to work properly.
+ [James Morris <jmorris@intercode.com.au>]
+
+ *) user and ap_auth_type fields were moved from connection_rec to
+ request_rec. [Ryan Bloom]
+
+ *) Removed the ap_block_alarms and ap_unblock_alarm calls. These aren't
+ needed in a threaded server.
+
+ *) Initial pthread implementation from from Dean's apache-nspr code.
+ [Bill Stoddard, Ryan Bloom]
+
+
+Changes with Apache 1.3.9
+
+ *) Remove bogus error message when a redirect doesn't set Location.
+ Instead, use an empty string to avoid coredump if the error message
+ was supposed to include a location. [Roy Fielding]
+
+ *) Don't allow configure to include mod_auth_digest unless it is
+ explicitly requested, even if the user asked for all modules.
+ [Roy Fielding]
+
+ *) Translate module names to dll names for OS/2 so that they are no more
+ than 8 characters long and have an extension of "dll" instead of "so".
+ [Brian Havard]
+
+ *) Print out pointer to Rule DEV_RANDOM when truerand lib not found.
+ Fix test-compile check to check for randbyte instead of trand32.
+ Use ap_base64encode_binary/decode instead of copy in mod_auth_digest.c
+ and tweak to make Amaya happier. [Ronald Tschalär]
+
+ *) Ensure that the installed expat include files are world readable,
+ just like the other header files. [Martin Kraemer]
+
+ *) Fixed generated AddModule adjustments in APACI's `configure' script
+ in order to allow (new) modules like mod_vhost_alias to be handled
+ correctly (which was touched by the adjustments for mod_alias).
+ [Ralf S. Engelschall]
+
+ *) For binary builds, add -R flag to apachectl to work around the lack of
+ an absolute path to the ./libexec directory where the libhttp.ep file
+ is needed for SHARED_CORE architectures. [Randy Terbush]
+
+ *) WIN32: Create the CGI script process as DETACHED. This may solve the
+ problem observed by some Win95/98 users where they get CGI script
+ output sent to the console. [Bill Stoddard]
+
+ *) Fix (re)naming in the uuencode/decode section. The ap/ap_
+ routines are now called ap_base64* and are 'plain' (i.e., no
+ pool access or anything clever). Inside util.c the routines acting
+ like pstrdup are called ap_pbase64encode() and ap_pbase64decode().
+ The oddly named ap_uuencode(), ap_uudecode() are kept around for
+ now but deprecated. [dirkx]
+
+ *) Clean up the base64 and SHA1 additions and make sure they are
+ represented in the ApacheCore.def, ApacheCoreOS2.def, and httpd.exp
+ files. [Roy Fielding]
+
+ *) WIN32: Migrate to InstallShield 5.5 and provide a bit more error
+ checking. Allow compiling on VS 6.0. [Randy Terbush]
+
+ *) Fixed assumption of absolute paths in binbuild.sh. [Tony Finch]
+
+ *) Use TestCompile to search for the truerand library (rather than blindly
+ assuming its existence). If it is not found, complain (but do not
+ exit - yet). [Martin Kraemer]
+
+ *) We forgot to add the new exported function names to
+ src/support/httpd.exp. [Bill Stoddard, Randy Terbush]
+
+ *) Add description of -T command-line option to usage().
+ [Ralf S. Engelschall]
+
+ *) For "some" platforms (notably, EBCDIC based ones), libos needs to be
+ searched only AFTER libap has been searched, because libap needs
+ some symbols from libos. [Martin Kraemer]
+
+ *) Fix conflict with original mod_digest related to the symbol of the
+ module dispatch list (which has to be unique for DSO and follow the
+ usual conventions for the installation procedure).
+ [Ralf S. Engelschall]
+
+ *) Add a dbm-library check for the "usual places" (-ldbm, -lndbm, -ldb)
+ for other platforms as well. [Martin Kraemer]
+
+ *) Make ap_sha1.c compile for EBCDIC platforms: replace remaining LONG
+ types by AP_LONG and replace reference to renamed variable 'ubuf'
+ by 'buffer'. [Martin Kraemer]
+
+Changes with Apache 1.3.8 [not released]
+
+ *) Flush the output buffer immediately after sending an error or redirect
+ response, since the result may be needed by the client to abort a
+ long data transfer or restart a series of pipelined requests.
+ [Tom Vaughan <tvaughan@aventail.com>, Roy Fielding]
+
+ *) PORT: Improved compilation and DSO support on Sequent DYNIX/ptx.
+ [Ian Turner <iant@sequent.com>] PR#4735
+
+ *) Local struct mmap in http_core.c conflicted with system structure
+ name on DYNIX -- changed to mmap_rec. [Roy Fielding] PR#4735
+
+ *) Added updated mod_digest as modules/experimental/mod_auth_digest.
+ [Ronald Tschalär <ronald@innovation.ch>]
+
+ *) Fix a memory leak where the module counts were getting messed
+ up across restarts. [David Harris <dharris@drh.net>]
+
+ *) CIDR addresses such as a.b.c.d/24 where d != 0 weren't handled
+ properly in mod_access.
+ ["Paul J. Reder" <rederpj@raleigh.ibm.com>] PR#4770
+
+ *) RewriteLock/RewriteMap didn't work properly with virtual hosts.
+ [Dmitry Khrustalev <dima@bog.msu.su>] PR#3874
+
+ *) PORT: Support for compaq/tandem/com.
+ [Michael Ottati <michael.ottati@compaq.com>, dirkx]
+
+ *) Added SHA1 password encryption support to easy migration from
+ Netscape servers. See support/SHA1 for more information.
+ Caused the separation of ap_md5.c into md5, sha1 and a general
+ ap_checkpass.c with just a validate_passwd routine. Added a
+ couple of flags to support/htpasswd. Some reuse of the to64()
+ function; hence renamed to ap_to64().
+ [Dirk-Willem van Gulik, Clinton Wong <clintdw@netcom.com>]
+
+ *) Change for EBCDIC platforms (TPF and BS2000) to correctly deal
+ with ASCII/EBCDIC conversions in "ident" query.
+ [David McCreedy <McCreedy@us.ibm.com>]
+
+ *) Get rid of redefinition warning on MAC_OS_X_SERVER platform.
+ Change "Power Macintosh" to Power* so if uname prints "Power Book"
+ we're still happy on Rhapsody platforms. [Wilfredo Sanchez]
+
+ *) Fix SIGSEGV on some systems because the Vary fix below included
+ a call to table_do with a variable argument list that was not
+ NULL terminated. Replaced with better implementation. [Roy Fielding]
+
+Changes with Apache 1.3.7 [not released]
+
+ *) The "Vary" response header field is now sanitised right before
+ the header is sent back to the client. Multiple "Vary" fields
+ are combined, and duplicate tokens (e.g., "Vary: host, host" or
+ "Vary: host, negotiate, host, accept-language") are reduced to
+ single instances. This is a better solution than the force-no-vary
+ one (which is still valid for clients that can't cope with Vary
+ at all). PR#3118 [Dean Gaudet, Roy Fielding, Ken Coar]
+
+ *) Portability changes for BeOS. [David Reid abb37@dial.pipex.com]
+
+ *) Link DSO's with "gcc -shared" instead of "ld -Bshareable" at
+ least on Linux and FreeBSD for now.
+ [Rasmus Lerdorf]
+
+ *) Win32: More apache -k restart work. Restarts are now honored
+ immediately and connections in the listen queue are -not- lost.
+ This is made possible by the use of the WSADuplicateSocket()
+ call. The listeners are opened in the parent, duplicated, then
+ the duplicates are passed to the child. The original listen sockets
+ are not closed by the parent across a restart, thus the listen queue
+ is preserved.
+ [Bill Stoddard <stoddard@raleigh.ibm.com>]
+
+ *) Fix handling of case when a client has sent "Expect: 100-continue"
+ and we are going to respond with an error, but get stuck waiting to
+ discard the body in the pointless hope of preserving the connection.
+ [Roy Fielding, Joe Orton <jeo101@york.ac.uk>] PR#4499, PR#3806
+
+ *) Fix 'configure' to work correctly with SysV-based versions of
+ 'tr' (consistent with Configure's use as well). [Jim Jagielski]
+
+ *) apxs: Add "-S var=val" option which allows for override of CFG_*
+ built-in values. Add "-e" option which works like -i but doesn't
+ install the DSO; useful for editing httpd.conf with apxs. Fix
+ editing code so that multiple invocations of apxs -a will not
+ create duplicate LoadModule/AddModule entries; apxs can now be
+ used to re- enable/disable a module. [Wilfredo Sanchez]
+
+ *) Win32: Update the server to use Winsock 2. Specifically, link with
+ ws2_32.lib rather than wsock32.lib. This gives us access to
+ WSADuplcateSocket() in addition to some other enhanced comm APIs.
+ Win 95 users may need to update their TCP/IP stack to pick up
+ Winsock 2. (See http://www.microsoft.com/windows95/downloads/)
+ [Bill Stoddard stoddard@raleigh.ibm.com]
+
+ *) Win32: Redirect CGI script stderr (script debug info) into the
+ error.log when CGI scripts fail. This makes Apache on Win32
+ behave more like Unix.
+ [Bill Stoddard stoddard@raleigh.ibm.com]
+
+ *) Fixed `httpd' usage display: -D was missing.
+ [Ralf S. Engelschall] PR#4614
+
+ *) Fix `make r' test procedure in src/regex/: ap_isprint was not found.
+ [Ralf S. Engelschall] PR#4561, PR#4562
+
+ *) OS/2: Fix problem with accept lock semaphores where server would die with
+ "OS2SEM: Error 105 getting accept lock. Exiting!"
+ [Brian Havard] PR#4505
+
+ *) Add DSO support for DGUX 4.x using gcc. Tested on x86 platforms.
+ [Randy Terbush <randy@covalent.net>]
+
+ *) Add the new mass-vhost module (mod_vhost_alias.c) developed and
+ used by Demon Internet, Ltd. [Tony Finch <fanf@demon.net>]
+
+ *) Better GCC detection for DSO flags under Solaris 2 where the `cc'
+ command potentially _is_ GCC. [Ralf S. Engelschall]
+
+ *) Fix apxs build issues on AIX
+ [Rasmus Lerdorf <rasmus@raleigh.ibm.com>]
+
+ *) DocumentRoot Checking: Under previous versions, when Apache
+ first started up, it used to do a stat of each DocumentRoot to
+ see if it existed and was a directory. If not, then an error
+ message was printed. THIS HAS BEEN DISABLED. If DocumentRoot
+ does not exist, you will get error messages in error_log. If
+ the '-t' command line option is used (to check the configuration)
+ the check of DocumentRoot IS performed. An additional command
+ line option, '-T', has been added if you want to avoid the
+ DocumentRoot check even when checking the configuration.
+ [Jim Jagielski]
+
+ *) Win32: The query switch "apache -S" didn't exit after showing the
+ vhost settings. That was inconsistent with the other query functions.
+ [Bill Stoddard - Fixed by Martin on Unix in 1.3.4]
+
+ *) Win32: Changed behaviour of apache -k restart.
+ Previously, the server would drain all connections in the stack's
+ listen queue before honoring the restart. On a busy server, this
+ could take hours. Now, a restart is honored almost immediately.
+ All connections in Apache's queues are handled but connections in
+ the stack's listen queue are discarded. Restart triggered by
+ MaxRequestPerChild is unchanged.
+ [Bill Stoddard <stoddard@raleigh.ibm.com>]
+
+ *) Win32: Eliminated unnecessary call to wait_for_multiple_objects in
+ the accept loop. Good for a 5% performance boost. Cleaned up
+ parent/child process management code.
+ [Bill Stoddard <stoddard@raleigh.ibm.com>]
+
+ *) Added ceiling on file size for memory mapped files.
+ [John Giannandrea <jg@meer.net>] PR#4122
+
+ *) Fix ndbm.h include problems with brain-dead glibc >= 2.1 which
+ has ndbm.h in a non-standard db1/ subdir. PR#4431, PR#4528
+ [Henri Gomez <gomez@slib.fr>, Ralf S. Engelschall]
+
+ *) Determine AP_BYTE_ORDER for ap_config_auto.h and already
+ use this at least for Expat. [Ralf S. Engelschall]
+
+ *) Allow .module files to specify libraries with Lib:.
+ [Ben Laurie]
+
+ *) Allow SetEnvIf[NoCase] to test environment variables as well
+ as header fields and request attributes. [Ken Coar]
+
+ *) Fix mod_autoindex's handling of ScanHTMLTitles when file
+ content-types are "text/html;parameters". PR#4524 [Ken Coar]
+
+ *) Remove "mxb" support from mod_negotiation -- it was a draft feature
+ never accepted into any standard, and it opens up certain DoS
+ attacks. [Koen Holtman <Koen.Holtman@cern.ch>]
+
+ *) TestCompile updated. We can now run programs and output the
+ results during the Configure process. [ Jim Jagielski]
+
+ *) The source is now quad (long long) aware as needed. Specifically,
+ the Configure process determines the correct size of off_t and
+ *void. When the OS/platform/compiler supports quads, ap_snprintf()
+ provides for the 'q' format qualifier (if quads are not available,
+ 'q' is silently "demoted" to long). [Jim Jagielski]
+
+ *) When the username or password fed to htpasswd is too long, include the
+ size limit in the error message. Also report illegal characters
+ (currently only ':') in the username. Add the size restrictions
+ to the man page. [Ken Coar]
+
+ *) Fixed the configure --without-support option so it doesn't result in
+ an infinite loop. [Marc Slemko]
+
+ *) Piped error logs could cause a segfault if an error occured
+ during configuration after a restart.
+ [Aidan Cully <aidan@panix.com>] PR#4456
+
+ *) If a "Location" field was stored in r->err_headers_out rather
+ than r->headers_out, redirect processing wouldn't find it and
+ the server would core dump on ap_escape_html(NULL). Check both
+ tables and raise HTTP_INTERNAL_SERVER_ERROR with a log message
+ if Location isn't set. [Doug MacEachern, Ken Coar]
+
+ *) Add RULE_EXPAT, the src/lib/ directory structure, and a modified copy
+ of the Expat 1.0.2 distribution. [Greg Stein]
+
+ *) Replace regexec() calls with calls to a new API stub function
+ ap_regexec(). This solves problems with DSO modules which use the regex
+ library. [Jens-Uwe Mager <jum@helios.de>, Ralf S. Engelschall]
+
+ *) Add 'Request_Protocol' special keyword to mod_setenvif so that
+ environment variables can be set according to the protocol version
+ (e.g., HTTP/0.9 or HTTP/1.1) of the request. [Ken Coar]
+
+ *) Add DSO support for OpenStep (Mach 4.2) platform.
+ [Ralf S. Engelschall, Rex Dieter <rdieter@math.unl.edu>] PR#3997
+
+ *) Fix sed regex for generating ap_config_auto.h in src/Configure.
+ [Jan Gallo <gallo@pvt.sk>] PR#3690, PR#4373
+
+ *) Switch to /bin/sh5 in APACI on Ultrix and friends to avoid problems with
+ their brain-dead /bin/sh. [Ralf S. Engelschall] PR#4372
+
+ *) Better DSO flags recognition on NetBSD platforms using ELF.
+ [Todd Vierling <tv@pobox.com>] PR#4310
+
+ *) Always log months in english format for %t in mod_log_config.
+ [Petr Lampa <lampa@fee.vutbr.cz>] PR#4366, 679
+
+ *) Support for server-parsed and multiview-determined ReadmeName and
+ HeaderName files in mod_autoindex. Removed the restriction on
+ "/"s in ReadmeName and HeaderName directives since the *sub_req*
+ routines will deal with the access issues. (It's now possible to
+ have {site|group|project|customer|...} wide readmes and headers.)
+ [Raymond S Brand <rsbx@rsbx.net>, Ken Coar] PR#1574, 3026, 3529,
+ 3569, 4256
+
+ *) When stat() fails, don't assume anything about the contents of
+ the struct stat. [Ed Korthof <ed@bitmechanic.com>]
+
+ *) It's OK for a semop to return EINTR, just loop around and try
+ again. [Dean Gaudet]
+
+ *) Fix configuration engine re-entrant hangups, which solve a
+ handful of problems seen with mod_perl <Perl> configuration sections
+ [Salvador Ortiz Garcia <sog@msg.com.mx>]
+
+ *) Mac OS and Mac OS X Server now use the appropriate custom layout
+ by default when building with APACI; allow for platform-specific
+ variable defaults in configure. [Wilfredo Sanchez]
+
+ *) Do setgid() before initgroups() in http_main; some platforms
+ zap the grouplist when setgid() is called. This was fixed in
+ suexec earlier, but the main httpd code missed the change.
+ [Rob Saccoccio <robs@InfiniteTechnology.com>] PR#2579
+
+ *) Add recognition of .tgz as a gzipped tarchive.
+ [Bertrand de Singly <bertrand.de-singly@polytechnique.fr>] PR#2364
+
+ *) mod_include's fsize/flastmod should allow only relative paths, just
+ like "include file". [Jaroslav Benkovsky <benkovsk@pha.pvt.cz>]
+
+ *) OS/2: Add support for building loadable modules using DLLs.
+ [Brian Havard]
+
+ *) Add iconsdir, htdocsdir, and cgidir to config.layout.
+ [Wilfredo Sanchez]
+
+ *) Fix minor but annoying bug with the test for Configuration.tmpl
+ being newer than Configuration so that it is less likely to fail
+ when using APACI and shadow sources. [Wilfredo Sanchez]
+
+ *) PORT: Add initial support for Mac OS (versions 10.0 and
+ greater). Use Mac OS X Server layout for now. Clean up dyld code
+ in unix/os.c, and don't install the dyld error handlers, which
+ are no longer needed in Mac OS. [Wilfredo Sanchez]
+
+ *) Rename Rhapsody layout to "Mac OS X Server". Change install
+ locations to appropriate ones for user-built (as opposed to
+ system) installs. [Wilfredo Sanchez]
+
+ *) Modify mod_autoindex's handling of AddDescription so that the
+ behaviour matches the documentation. [Ken Coar] PR#1898, 3072.
+
+ *) Add functionality to the install-bindist.sh script created by
+ binbuild.sh to use tar when copying distribution files to the
+ serverroot. This allows upgrading an existing installation
+ without nesting the new distribution in the old.
+
+ install-bindist.sh now detects the local perl5 path to install
+ apxs and dbmmanage with proper path to perl interpreter.
+
+ Add an install-binsupport target which copies the source files
+ for apxs and dbmmanage to bindist to allow these scripts to
+ be properly installed relative to the destination serverroot.
+ [Randy Terbush, Covalent Technologies, randy@covalent.net]
+
+ *) Fix intermittent SEGV in ap_proxy_cache_error() in
+ src/modules/proxy_util.c where a NULL filepointer and
+ temporary filename were closed and unlinked.
+ [Graham Leggett <minfrin@sharp.fm>,
+ Tim Costello <tjcostel@socs.uts.edu.au>] PR#3178
+
+ *) Fix inconsistent error messages reported by mod_proxy.
+ [Graham Leggett <minfrin@sharp.fm>]
+
+ *) OS/2: Fix terminating CGIs that aren't compiled by EMX GCC when a
+ connection is aborted. [Brian Havard]
+
+ *) Force the LANG envariable to the known state of "C" so that we
+ have assurance about how string manipulators (e.g., tr) will
+ function. [Ken Coar] PR#1630
+
+ *) Add a directive to allow customising of the tracking cookie name.
+ [Ken Coar] PR#2921, 4303
+
+ *) Add "force-no-vary" envariable to allow servers to work around
+ clients that choke on "Vary" fields in the response header.
+ [Ken Coar, Dmitry Khrustalev <dima@zippy.machaon.ru>] PR#4118
+
+ *) Fixed a bug in mod_dir that causes a child process will infinitely
+ recurse when it attemps to handle a request for a directory wnd the
+ value of the DirectoryIndex directive is a single dot. Also likely
+ to happen for anyother values of DirectoryIndex that will map back
+ to the same directory. The handler now only considers regular files
+ as being index candidates. No PR#s found.
+ [Raymond S Brand <rsbx@rsbx.net>]
+
+ *) Ease configuration debugging by making TestCompile fall back to
+ using "make" if the $MAKE variable is unset [Martin Kraemer]
+
+ *) Fixed the ServerSignature directive to work as documented.
+ [Raymond S Brand <rsbx@rsbx.net>] PR#4248
+
+ *) Add "opt" (SysV-style) layout to config.layout. [Raymond S Brand
+ <rsbx@rsbx.net>]
+
+ *) Add APACI --without-execstrip option which can be used to disable the
+ stripping of executables on installation. This is very important for DSO
+ and debugging situations. [Ralf S. Engelschall]
+
+ *) Add support for OS/2 (case insenstive filesystem, .exe suffix, etc)
+ to APACI files and related scripts.
+ [Yitzchak Scott-Thoennes <sthoenna@efn.org>, Ralf S. Engelschall] PR#4269
+
+ *) Add support for standalone mode in TPF
+ [Joe Moenich <moenich@us.ibm.com>]
+
+ *) Fix number of bytes copied by read_connection() in src/support/ab.c
+ [Jim Cox <jc@superlink.net>] PR#4271
+
+ *) Fix special RewriteCond "-s" pattern matching.
+ [Bob Finch <bob@nas.com>]
+
+ *) Fix value quoting in src/Configure script for ap_config_auto.h
+ [Paul Sutton <paul@awe.com>]
+
+ *) Make sure RewriteLock can be used only in the global context, (i.e.
+ outside of any <VirtualHost> sections) because it's a global facility of
+ the rewrite engine. [Ralf S. Engelschall]
+
+ *) Fix the ownership delegation for proxy directory under `make install'.
+ [Ralf S. Engelschall]
+
+ *) APACI would not correctly build suexec. [Maria Verina
+ <mariav@icgeb.trieste.it>] PR#4260
+
+ *) mod_mime_magic passed only the first 4k of a file to
+ uncompress/gzip, but those tools sometimes do not produce
+ any output unless a sufficient portion of the compressed
+ file is input. Change to pass the entire file -- but
+ only read 4k of output.
+ [Marcin Cieslak <saper@system.pl>] PR#4097
+
+ *) "IndexOptions None" generated extra spaces at the end of each
+ line. [inkling@firstnethou.com] PR#3770
+
+ *) The "100 Continue" response wasn't being sent after internal
+ redirects. [Jose KAHAN <kahan@w3.org>] PR#3910, 3806, 3575
+
+ *) When padding the name with spaces for display, mod_autoindex would
+ count &, <, and > in their escaped width, messing up the display.
+ [Dean Gaudet] PR#4075, 3758
+
+ *) PORT: fixed a compilation problem on NEXT.
+ [Jacques Distler <distler@golem.ph.utexas.edu>] PR#4130
+
+ *) r->request_time wasn't being set properly in certain error conditions.
+ [Dean Gaudet] PR#4156
+
+ *) PORT: deal with UTS compiler error in http_protocol.c
+ [Dave Dykstra <dwd@bell-labs.com>] PR#4189
+
+ *) Add ap_vrprintf() function. [John Tobey <jtobey@banta-im.com>] PR#4246
+
+ *) Fix the mod_mime hash table to work properly with locales other
+ than C. [Dean Gaudet] PR#3427
+
+ *) Fix a memory leak which is exacerbated by certain configurations.
+ [Dean Gaudet] PR#4225
+
+ *) Prevent clobbering saved IFS values in APACI. [Jim Jagielski]
+
+ *) Fix buffer overflows in ap_uuencode and ap_uudecode pointed out
+ by "Peter 'Luna' Altberg <peter@altberg.nu>" and PR#3422
+ [Peter 'Luna' Altberg <peter@altberg.nu>, Ronald Tschalär]
+
+ *) Make {Set,Unset,Pass}Env per-directory instead of per-server.
+ [Ben Laurie]
+
+ *) Correct an apparent typo: on the Windows and MPE platforms, the
+ htpasswd utility was limiting passwords to only 8 characters.
+ [Ken Coar]
+
+ *) EBCDIC platforms: David submitted patches for two bugs in the
+ MD5 digest port for EBCDIC machines:
+ a) the htdigest utility overwrote the old contents of the digest file
+ b) the Content-MD5 header value (ContentDigest directive) was wrong
+ when the returned file was not converted from EBCDIC, but was a
+ binary (e.g., image file) in the first place.
+ [David McCreedy <mccreedy@us.ibm.com>]
+
+ *) support/htpasswd now permits the password to be specified on the
+ command line with the '-b' switch. This is useful when passwords
+ need to be maintained by scripts -- particularly in the Win32
+ environment. [Ken Coar]
+
+ *) Win32: Win32 multiple services patch. Added capability to install and
+ run multiple copies of apache as individual services.
+
+ Example 1:
+ apache -n apache1 -i -f c:/httpd.conf
+ Installs apache as service 'apache1' and associates c:/httpd.conf
+ with that service.
+ net start apache1
+ Starts apache1 service.
+ net stop apache1
+ Stops apache1 service
+
+ Example 2:
+ apache -n apache2 -i
+ Installs apache as service 'apache2'. httpd.conf is located under
+ the default server root (/apache/conf/httpd.conf).
+ net start apache2
+ Starts apache2 service.
+
+ Example 3:
+ apache -n apache3 -i -d c:/program files/apache
+ Install apache as service 'apache3' and sets server root to
+ c:/program files/apache.
+
+ Example 4:
+ apache -n apache2 -k restart
+ Restart apache2 service
+
+ [Keith Wannamaker, Ken Parzygnat, Bill Stoddard]
+
+ *) Correct the signed/unsigned character handling for the MD5 routines;
+ mismatches were causing compilation problems with gcc -pedantic and
+ in the TPF cross-compilation. [Ken Coar]
+
+ *) OS/2: Rework CGI handling to use spawn*() instead of fork/exec, achieving
+ a roughly 5 fold speed up. [Brian Havard]
+
+ *) proxy ftp: instead of using the hardwired string "text/plain" as
+ a fallback type for files served by the ftp proxy, use the
+ ap_default_type() function to determine the configured type.
+ This allows for special configurations like
+ <Directory proxy:ftp://some.host>
+ DefaultType gargle/blurb
+ </Directory>
+ Additionally, add the Content-Encoding: header to FTP proxy replies
+ when the encoding is defined (by the AddEncoding directive).
+ Because it was missing, it was almost impossible to browse compressed
+ files using the FTP proxy (works now perfectly in Communicator).
+ The ftp proxy now also returns the Date: and Server: header lines (if not
+ much else... This code is "somewhat" broken) like normal requests do.
+ [Martin Kraemer]
+
+ *) Be more smart in APACI's configure script when determining the UID/GID
+ for User/Group directives and use the determined UID/GID to initialize
+ the permissions on the proxycachedir.
+ [Dirk-Willem van Gulik, Ralf S. Engelschall]
+
+ *) Changed the forking-prior-to-cleanup in the proxy module to first
+ check wether it actually needs to collect garbage. This reduces
+ the number of fork()s from one/request to just the odd one an hour.
+ [Dirk-Willem van Gulik]
+
+ *) Added proxy, auth and header support to src/support/ab.c. Added a
+ README file to src/support/
+ [Dirk-Willem van Gulik]
+
+ *) Don't hard-code the path to AWK in --shadow bootstrapping Makefile.
+ [Ralf S. Engelschall] PR#4050
+
+ *) Add support for DSO module compilation on BSD/OS 3.x.
+ [Randy Terbush, Covalent Technologies]
+
+ *) Fix sed-substitutions in `make install': path elements like `httpd/conf'
+ (for instance from an APACI configure --sysconfdir=/etc/httpd/conf
+ option) were substituted with $(TARGET).conf, etc. Same for other strings
+ with dots where the dot wasn't matched as plain text.
+ [Ralf S. Engelschall]
+
+ *) PORT: Add support for FreeBSD 4.x [Ralf S. Engelschall]
+
+ *) Fix verbose output of APACI configure (option -v)
+ [Martin Kraemer, Ralf S. Engelschall]
+
+Changes with Apache 1.3.6
+
+ *) Removed new PassAllEnv code due to DSO problems. [Lars Eilebrecht]
+
+Changes with Apache 1.3.5 [not released]
+
+ *) M_INVALID needed a value within the scope of METHODS so that unknown
+ methods can be access controlled. [Roy Fielding] PR#3821
+
+ *) Added PassAllEnv; makes server's entire environment available
+ to CGIs and SSIs executed within directive's scope. [Ken Coar]
+
+ *) ap_uuencode() always added two trailing '='s and encoding of
+ 8 bit characters on a machine with signed char may produced
+ incorrect results. Additionally ap_uuencode() should now
+ work correctly on EBCDIC platforms.
+ [Ronald Tschalär <ronald@innovation.ch>] PR#3411
+
+ *) WIN32: Binary installer now runs the configuration DLL before
+ the reboot prompt (which is only given if MSVCRT.DLL system
+ DLL is new or updated). This should avoid the configuration
+ directory being empty after installation. [Paul Sutton]
+ PR#3767, 3800, 3827, 3850, 3900, 3953, 3988
+
+ *) WIN32: Binary installer now creates Start menu options to start
+ and stop Apache as a console application and to uninstall
+ the Apache service on NT. [Paul Sutton] PR#3741
+
+ *) WIN32: Apache.exe now contains an icon. [Paul Sutton]
+
+ *) PORT: Switch back to using fcntl() locking on Linux -- instabilities
+ have been reported with flock() locking (probably related to kernel
+ version). [Dean Gaudet] PR#2723, 3531
+
+ *) Using APACI, the main config file (usually httpd.conf) was
+ not being adjusted as $(TARGET).conf. [Wilfredo Sanchez
+ <wsanchez@apple.com>]
+
+ *) PORT: AIX does not require the SHARED_CODE "hack"
+ [Ryan Bloom <rbb@raleigh.ibm.com>]
+
+ *) Set-Cookie headers were being doubled up for some CGIs by the O(n^2)
+ avoidance code added in 1.3.3.
+ [Dean Gaudet, Jeff Lewis <lewis@stanford.edu>] PR#3872
+
+ *) ap_isxdigit was somehow neglected when adding the ap_isfoo() macros
+ for 8-bit safeness. [Dean Gaudet]
+
+ *) PORT: Use -fPIC instead of -fpic on Solaris and SunOS for compiling DSOs
+ because SPARCs have a small machine-specific maximum size for the Global
+ Offset Table which is often exceeded when compiling one of the larger
+ third-party modules with Apache. [Peter Urban <Peter.Urban@epfl.ch>] PR#3977
+
+ *) Move the directive `ExtendedStatus' in httpd.conf-dist-win _after_ the
+ DSO/DLL section because it's a directive from mod_status and isn't
+ available before the DLL of mod_status is loaded.
+ [Martin POESCHL <mpoeschl@gmx.net>] PR#3936
+
+ *) SECURITY: Fix a bug in the calculation of the buffer size for the line
+ continuation facility in Apache's configuration files which could
+ lead to a buffer overflow situation.
+ [Thomas Devanneaux <Thomas.Devanneaux@enst.fr>] PR#3617
+
+ *) Make documentation and error messages of APACI's --activate-module=FILE
+ option more clear. [Jan Wolter <janc@wwnet.net>] PR#3995
+
+ *) Fix the gcc version check (for enabling the `inline' facility) to
+ really support all future gcc versions >= 2.7 until we know more.
+ [John Tobey <jtobey@banta-im.com>] PR#3983
+
+ *) Let APACI's configure script correctly complain for unknown --enable-XXX
+ and --disable-XXX options. [Ralf S. Engelschall] PR#3958
+
+ *) Link the shared core bootstrap program (``Rule SHARED_CORE=yes'') also
+ against libap.a and use its ap_snprintf() instead of sprintf() to avoid
+ possible buffer overflows. [Ralf S. Engelschall]
+
+ *) Remove no longer used non-API function ap_single_module_init().
+ [Ralf S. Engelschall]
+
+ *) Add Apple's Mac OS X Server Layout "Rhapsody" to config.layout.
+ [Wilfredo Sanchez]
+
+ *) Add cgidir, htdocsdir, iconsdir variables to Makefile.tmpl in order
+ to make platform installations easier. [Wilfredo Sanchez]
+
+ *) In configure, do not append the target name to the directory path if
+ the path already contains "apache". [Ralf S. Engelschall]
+
+ *) SIGPIPE is now ignored by the server core. The request write routines
+ (ap_rputc, ap_rputs, ap_rvputs, ap_rwrite, ap_rprintf, ap_rflush) now
+ correctly check for output errors and mark the connection as aborted.
+ Replaced many direct (unchecked) calls to ap_b* routines with the
+ analogous ap_r* calls. [Roy Fielding]
+
+ *) Enhanced mod_rewrite's mapfile handling: The in-core cache for text and
+ DBM format mapfiles now uses a 4-way hash table with LRU functionality.
+ Furthermore map lookups for non-existent keys are now cached as well.
+ Additionally "txt" maps are now parsed with simple string functions
+ instead of using ap_pregcomp(). As a side effect a bug that prevented
+ the usage of keys containing the "," character was fixed.
+ The changes drastically improve the performance when large rewrite maps
+ are in use.
+ [Michael van Elst <mlelstv@serpens.swb.de>, Lars Eilebrecht] PR#3160
+
+ *) Added ap_sub_req_method_uri() for doing a subrequest with a method
+ other than GET, and const'd the definition of method in request_rec.
+ [Greg Stein]
+
+ *) Use proper pid_t type for saving PIDs in alloc.c. [John Bley]
+
+ *) Replaced use of WIN32 define with HAVE_DRIVE_LETTERS to indicate
+ when the OS allows a DOS drive letter within pathnames. [Brian Havard]
+
+ *) Add %V to mod_log_config, this logs the hostname according to the
+ UseCanonicalName setting (this is the pre-1.3.4 behaviour of
+ %v). Useful for mass vhosting. [Tony Finch <dot@dotat.at>]
+
+ *) Add support for \n and \t to mod_log_config, can be used to produce
+ more reliable logs with multiline entries. [Tony Finch <dot@dotat.at>]
+
+ *) Fixed a few compiler nits. [John Bley <jbb6@acpub.duke.edu>]
+
+ *) Added informative error messages for failed munmap() and fseek() calls
+ in http_core.c. [John Bley, Roy Fielding]
+
+ *) Added some informative error messages for some failed malloc()
+ calls. [John Bley <jbb6@acpub.duke.edu>, Jim Jagielski]
+
+ *) OS/2 ap_os_canonical_filename()'s behaviour is improved: ap_assert()
+ is removed. This allows <Directory proxy:*> directives to work and
+ prevents invalid requests from killing the process.
+ [Brian Havard <brianh@kheldar.apana.org.au>]
+
+ *) Reorganised FAQ document.
+ [Joshua Slive <slive@finance.commerce.ubc.ca>] PR#2497
+
+ *) src/support/: The ApacheBench benchmark program was overhauled by
+ David N. Welton: you can now have it generate an HTML TABLE, presumably
+ for integration into other HTML sources. David updated the ab man page
+ as well and added some missing descriptions. Thanks!
+ [David N. Welton <davidw@prosa.it>]
+
+ *) Win32: The filename validity checker now allows filenames containing
+ characters in the range 0x80 to 0xff (for example accented characters).
+ [Paul Sutton] PR#3890
+
+ *) Added conditional logging based upon environment variables to
+ mod_log_config. mod_log_referer and mod_log_agent
+ are now deprecated. [Ken Coar]
+
+ *) Allow apache acting as a proxy server to relay the real
+ reason of a failure to a client rather than the "internal
+ server error" it does currently. The general exposure mechanism
+ can be triggered by any module by setting the "verbose-error-to"
+ note to "*"; this allows more than just proxy errors to be exposed.
+ [Cliff Skolnick, Roy Fielding, Martin Kraemer] Related to PR#3455, 4086
+
+ *) Moved man pages for ab and apachectrl to section 8.
+ [Wilfredo Sanchez, Roy Fielding]
+
+ *) Added -S option to install.sh so that options can be passed to
+ strip on some platforms. [Ralf S. Engelschall, Wilfredo Sanchez]
+
+ *) Tweak modules Makefile generated by Configure so that it handles
+ the test case of no modules being selected. [chaz@reliant.com]
+
+ *) Added a <LimitExcept method ...> sectioning directive that allows
+ the user to assign authentication control to any HTTP method that
+ is *not* given in the argument list; i.e., the logical negation
+ of the <Limit> directive. This is particularly useful for controlling
+ access on methods unknown to the Apache core, but perhaps known by
+ some module or CGI script. [Roy Fielding, Tony Finch]
+
+ *) Prevent apachectl from complaining if the PIDFILE exists but
+ does not contain a process id, as might occur if the server is
+ being rapidly restarted. [Wilfredo Sanchez]
+
+ *) Win32: Add global symbols missing from ApacheCore.def. [Carl Olsen]
+
+ *) Entity tag comparisons for If-Match and If-None-Match were not being
+ performed correctly -- weak tags might cause false positives. Also,
+ strong comparison wasn't properly enforced in all cases.
+ [Roy Fielding, Ken Coar, Dean Gaudet] PR#2065, 3657
+
+ *) OS/2: Supply OS/2 error code instead of errno on semaphore errors.
+ [Brian Havard]
+
+ *) Work around a bug in Lynx regarding its sending "Negotiate: trans"
+ even though it doesn't understand TCN. [Koen Holtman, Roy Fielding]
+
+ *) Added ap_size_list_item(), ap_get_list_item(), and ap_find_list_item()
+ to util.c for parsing an HTTP header field value to extract the next
+ list item, taking into account the possible presence of nested comments,
+ quoted-pairs, and quoted-strings. ap_get_list_item() also removes
+ insignificant whitespace and lowercases non-quoted tokens.
+ [Roy Fielding] PR#2065
+
+ *) proxy: The various calls to ap_proxyerror() can return HTTP/1.1 status
+ code different from 500. This allows the proxy to, e.g., return
+ "403 Forbidden" for ProxyBlock'ed URL's. [Martin Kraemer] Related to PR#3455
+
+ *) Fix ordering of language variants for the case where the traditional
+ negotiation algorithm is being used with multiple language variants
+ and no Accept-Language. [James Treacy <treacy@debian.org>] PR#3299, 3688
+
+ *) Do not round the TCN quality calculation to 5 decimal places,
+ unlike RFC 2296, because the calculation might need 12 decimal places
+ to get the right result. [Roy Fielding]
+
+ *) Remove unused code to disable transparent negotiation when
+ negotiating on encoding only, as we now handle encoding too
+ (though this is nonstandard for TCN), remove charset=ISO-8859-1
+ fiddle from the fiddle-averse RVSA comparison, and fix bugs in
+ some debugging statements within mod_negotiation. [Koen Holtman]
+
+ *) Fixed a rare memory corruption possibility in mod_dir if the index
+ file is negotiable and no acceptable variant can be found.
+ [Dean Gaudet, Roy Fielding, Martin Kraemer]
+
+ *) Win32: Add new config directive, ScriptInterpreterSource, to enable
+ searching the Win32 registry for script interpreters.
+ [Bill Stoddard]
+
+ *) Win32: The compiled-in default filename for the error log is now
+ error.log, which matches the default in the distributed httpd.conf.
+ [Paul Sutton]
+
+ *) Win32: Any error messages from -i or -u command line options are now
+ displayed on the console output rather than sent to the error log.
+ Also the "Running Apache..." message is not output unless Apache is
+ going to serve requests. [Paul Sutton]
+
+ *) Rework the MD5 authentication scheme to use FreeBSD's algorithm,
+ and use a private significator ('$apr1$') to mark passwords as
+ being smashed with our own algorithm. Also abstract the password
+ checking into a new ap_validate_password() routine. [Ken Coar]
+
+ *) Win32: The filename validity checker now allows "COM" but refuses
+ access to "COM1" through "COM4". This allows filenames such
+ as "com.name" to be served. [Paul Sutton] PR#3769.
+
+ *) BS2000: Adapt to the new ufork() system call interface which will
+ make subtasking easier on the OSD/POSIX mainframe environment.
+ [Martin Kraemer]
+
+ *) Add a compatibility define for escape_uri() -> ap_escape_uri() to
+ ap_compat.h. [David White <david@persimmon.com>] PR#3725
+
+ *) Make NDBM file suffix determination for mod_rewrite more accurate, i.e.
+ use `.db' instead of `.pag' not only for FreeBSD, but also when
+ the NDBM library looks like Berkeley-DB based.
+ [Ralf S. Engelschall] PR#3773
+
+ *) Add ability to handle DES or MD5 authentication passwords.
+ [Ryan Bloom <rbb@Raleigh.IBM.Com>]
+
+ *) Fix O(n^2) memory consumption in mod_speling. [Dean Gaudet]
+
+ *) SECURITY: Avoid some buffer overflow problems when escaping
+ quoted strings. (This overflow was on the heap and we believe
+ impossible to exploit.) [Rick Perry <perry@ece.vill.edu>]
+
+ *) Let src/Configure be aware of CFLAGS options starting with plus
+ signs as it's the case for the HP/UX compiler.
+ [Doug Yatcilla <yatcilda@umdnj.edu>] PR#3681
+
+ *) Remove the hard-wire of TAR=tar (we now check for gtar and gnutar first)
+ and check to see if the tar we wind up with supports '-h'.
+ [Jim Jagielski] PR#3671
+
+ *) A consistent and conservative style for all shell scripts has been
+ implemented. Basically, all shell string tests use the traditional
+ hack of 'if [ "x$var" != "x" ]' or 'if [ "x$var" = "xstring" ]'
+ to protect against bare null variable strings (ie: wrapping both
+ sides with double quotes and prepending 'x'). 'x' was chosen
+ because it's more universal and hopefully easier for old shell
+ prgrammers, as well as being easier to search for in 'vi' (/x\$) :)
+ [Jim Jagielski]
+
+ *) The status module now prints out both the main server generation as
+ well as the generation of each process. Also, the vhost info is
+ printed with '?notable'. [Jim Jagielski]
+
+ *) Move src/main/md5c.c to src/ap/ap_md5c.c; it's httpd-neutral
+ and this makes its functions available to things in src/support.
+ [Ken Coar]
+
+Changes with Apache 1.3.4
+
+ *) Renamed macros status_drops_connection to ap_status_drops_connection
+ and vestigial scan_script_header to ap_scan_script_header_err,
+ mostly for aesthetic reasons. [Roy Fielding]
+
+ *) The query switch "httpd -S" didn't exit after showing the
+ vhost settings. That was inconsistent with the other query functions.
+ [Martin Kraemer]
+
+ *) Moved the MODULE_MAGIC_COOKIE from before the versions and
+ filename to the end of the STANDARD_MODULE_STUFF. Its
+ presence at the beginning prevented reporting of the filename
+ for modules compiled before 1 January 1999. [Ken Coar]
+
+ *) SECURITY: ap_os_is_filename_valid() has been added to Win32
+ to detect and prevent access to special DOS device file names.
+ [Paul Sutton, Ken Parzygnat]
+
+ *) WIN32: Created new makefiles Makefile_win32.txt (normal build)
+ and Makefile_win32_debug.txt (debug build) that work on Win95.
+ Run each of the following from the src directory:
+ nmake /f Makefile_win32.txt # compiles normal build
+ nmake /f Makefile_win32.txt install # compiles and installs
+ nmake /f Makefile_win32.txt clean # removes compiled junk
+ nmake /f Makefile_win32_debug.txt # compiles debug build
+ nmake /f Makefile_win32_debug.txt install
+ nmake /f Makefile_win32_debug.txt clean
+ [Roy Fielding]
+
+ *) Added binbuild.sh and findprg.sh helpers to make it easier for us
+ to build binary distributions. [Lars Eilebrecht]
+
+ *) IndexOptions SuppressColumnSorting only turned off making
+ the column headers anchors; you could still change the display
+ order by manually adding a '?N=A' or similar query string to the
+ URL. Now SuppressColumnSorting locks in the sort order so
+ it can't be overridden this way. [Ken Coar]
+
+ *) Added IndexOrderDefault directive to supply a default sort order
+ for FancyIndexed directory listings. [Ken Coar] PR#1699
+
+ *) Change the ap_assert macro to a variant that works on all platforms.
+ [Richard Prinz <richard.prinz@cso.net>] PR#2575
+
+ *) Make sure under ELF-based NetBSD (now) and OpenBSD (future) we don't
+ search for an underscore on dlsym() (as it's already the case
+ for FreeBSD 3.0). [Todd Vierling <tv@pobox.com>] PR#2462
+
+ *) Small fix for mod_env.html: The module was documented as to be _not_
+ compiled into Apache per default, although it _IS_ compiled into
+ Apache per default. [Sim Harbert <sim@mindspring.com>] PR#3572
+
+ *) Instead of fixing a bug in the generation procedure for config.status (a
+ backslash was missing) we remove the bug together with it's complete
+ context because the special cases of the past can now no longer occur
+ because of the recent magic for the --with-layout default.
+ [Ralf S. Engelschall] PR#3590
+
+ *) Make top-level Makefile aware of a parallel build procedures (make -j) by
+ making sure the src/support/ tools are _forced_ to be build last (they
+ depend on other libraries).
+ [Markus Theissinger <markus.theissinger@gmx.de>]
+
+ *) Fix installation procedure: Now that os-inline.c is actually used (a
+ recently fixed bug prevented this) we need to also install os-include.c
+ in addition to os.h into the PREFIX/include/ location or building of
+ module DSOs with APXS fails. [Ralf S. Engelschall] PR#3527
+
+ *) Added MODULE_MAGIC_COOKIE as the first field in a module structure to
+ allow us to distinguish between a garbled DSO (or even a file which isn't
+ an Apache module DSO at all) and a DSO which doesn't match the current
+ Apache API. [Ralf S. Engelschall] PR#3152
+
+ *) Two minor enhancements to mod_rewrite: First RewriteRule now also
+ supports the ``nocase|NC'' flag (as RewriteCond already does for ages) to
+ match case insensitive (this especially avoids nasty patterns like
+ `[tT][eE][sS][tT]'). Second two additional internal map functions
+ `escape' and `unescape' were added which can be used to escape/unescape
+ to/from hex-encodings in URLs parts (this is especially useful in
+ combination with map lookups).
+ [Magnus Bodin, Ian Kallen, Ralf S. Engelschall]
+
+ *) Renamed the macro escape_uri() to ap_escape_uri() which was
+ forgotten (because it was a macro) in the symbol renaming process.
+ [Ralf S. Engelschall]
+
+ *) Fix some inconsistencies related to the scopes of directives. The only
+ user visible change is that the directives `UseCanonicalName' and
+ `ContentDigest' now use the (more correct) `Options' scope instead of
+ (less correct) `AuthConfig' scope. [Ralf S. Engelschall]
+
+ *) Using DSO, the Server token was being mangled. Specifically, the
+ module's token was being added first before the Apache token. This
+ has been fixed. [Jim Jagielski]
+
+ *) Major overhaul of mod_negotiation.c, part 2.
+ - properly handle "identity" within Accept-Encoding.
+ - allow encoded variants in RVSA negotiation and let them appear in
+ the Alternates field using the non-standard "encoding" tag-list.
+ - fixed both negotiation algorithms so that an explicitly accepted
+ encoding is preferred over no encoding if "identity" is not
+ included within Accept-Encoding.
+ - added ap_array_pstrcat() to alloc.c for efficient concatenation
+ of large substring sequences.
+ - replaced O(n^2) memory hogs in mod_negotiation with ap_array_pstrcat.
+ [Roy Fielding]
+
+ *) Major overhaul of mod_negotiation.c, part 1.
+ - cleanups to mod_negotiation comments and code structure
+ - made compliant with HTTP/1.1 proposed standard (rfc2068) and added
+ support for everything in the upcoming HTTP/1.1
+ revision (draft-ietf-http-v11-spec-rev-06.txt).
+ - language tag matching also handles tags with more than 2
+ levels like x-y-z
+ - empty Accept, Accept-Language, Accept-Charset headers are
+ processed correctly; previously an empty header would make all
+ values acceptable instead of unacceptable.
+ - allowed for q values in Accept-Encoding
+ - added support for transparent content negotiation (rfc2295 and
+ rfc2296) (though we do not implement all features in these drafts,
+ e.g. no feature negotiation). Removed old experimental version.
+ - implemented 'structured entity tags' for better cache correctness
+ (structured entity tags ensure that caches which can deal with Vary
+ will (eventually) be updated if the set of variants on the server
+ is changed)
+ - this involved adding a vlist_validator element to request_rec
+ - this involved adding the ap_make_etag() function to the global API
+ - modified guessing of charsets used by Apache negotiation algorithm
+ to guess 'no charset' if the variant is not a text/* type
+ - added code to sort multiviews variants into a canonical order so that
+ negotiation results are consistent across backup/restores and mirrors
+ - removed possibility of a type map file resolving to another type map
+ file as its best variant
+ [Koen Holtman, Roy Fielding, Lars Eilebrecht] PR#3451, 3299, 1987
+
+ *) RFC2396 allows the syntax http://host:/path (with no port number)
+ but the proxy disallowed it (ap_proxy_canon_netloc()).
+ [David Kristol <dmk@bell-labs.com>] PR#3530
+
+ *) When modules update/modify the file name in the configfile_t structure,
+ syntax errors will report the updated name, not the original one.
+ [Fabien Coelho <coelho@cri.ensmp.fr>] PR#3573
+
+ *) Correct some filename case assumptions from WIN32 to
+ CASE_BLIND_FILESYSTEM. [Brian Havard <brianh@kheldar.apana.org.au>]
+
+ *) For %v log ServerName regardless of the UseCanonicalName
+ setting (similarly for %p). [Dean Gaudet]
+
+ *) Configure was initializing the variables $OSDIR, $INCDIR and $SHELL
+ rather late (too late for some invocations of TestCompile).
+ This improves the make environment available to TestCompile and
+ the *.module scripts. [Martin Kraemer]
+
+ *) The hashbang emulation code in ap_execve.c would interpret
+ #!/hashbang/scripts correctly, but failed to fall back to a
+ standard shell for scripts which did NOT start with #!
+ Now SHELL_PATH is started in these cases. [Martin Kraemer]
+
+ *) PORT: Added the Cyberguard V2 port [Richard Stagg <stagg@lentil.org>]
+ PR#3336
+
+ *) Update APXS manual page: some -q option arguments were missing
+ and another was incorrect. [Mark Anderson <mda@discerning.com>] PR#3553
+
+ *) Cleanup the command line options: `-?' was documented to show
+ the usage list but does it with an error because `?' is not a valid
+ command. OTOH a lot of users expect `-h' to print such a usage list and
+ instead are annoyed for ages by our huge unreadable list of directives.
+ So we now changed the command line options this way:
+ 1. `-L' => `-R'
+ Intent: we need `-L' to be free, and `-R' for the DSO run-time path is
+ very similar to the popular linker option.
+ 2. `-h' => `-L'
+ Intent: while -l gives the small list of modules, -L now gives the
+ large list of directives implemented by these modules. This is also
+ consistent with -v (short version info) and -V (large version info).
+ 3. `-?' => `-h'
+ Intent: it's now the expected option ;-)
+ The manual page was adjusted accordingly.
+ [Ralf S. Engelschall] PR#2714
+
+ *) Fixed problem of fclose() on an unopened file in suexec if LOG_EXEC
+ wasn't defined. [Rick Franchuk <rickf@transpect.net>]
+
+ *) Removed recently introduced bugs and disfigurements in APACI:
+ o fixed argument line processing: using $args was broken: It was not
+ initialized and using args="$args $apc_option" and even args="$args
+ \"$apc_option\"" fails in the second processing round for any arguments
+ containing whitespaces. The only correct way is to use the construct
+ "$@" (but not possible here) or iterate _both_ times over the implicit
+ argument line (no argument to for-loop) which is what we now use.
+ o make --with-layout=Apache the default without creating
+ redundancy (copying the --with-layout block in the argument parsing
+ loop). We achieve this by using the "$@" construct together with the
+ `set' command to prepend --with-layout=Apache to the command line in
+ case --with-layout is not used.
+ o fixed auto-suffix handling now that config.layout exists.
+ Paths which are auto-suffixed are marked with a trailing plus sign in
+ config.layout and every path now can be marked this way (not only the
+ four paths for which we do it currently). Additionally the suffix is
+ no longer a static one. Instead it's now `/<target>' where <target> is
+ the argument of the --target option or per default `httpd'.
+ o allow also tabs (and only spaces) where we match whitespaces
+ o various fixes and cleanups related to used shell coding style
+ o made Jim happy by replacing `Written by' with `Initially written by' ;-)
+ o trimmed output of --help to fit into 80 columns
+ [Ralf S. Engelschall]
+
+ *) Added two new core API functions, ap_single_module_configure() and
+ ap_single_module_init(), which are now used by mod_so to configure a module
+ after loading. [Ralf S. Engelschall]
+
+ *) PORT: Add defines for USE_FLOCK_SERIALIZED_ACCEPT and
+ SINGLE_LISTEN_UNSERIALIZED_ACCEPT to NetBSD/OpenBSD section
+ of ap_config.h to allow serialized accept for multiport listens.
+ [Roy Fielding, Curt Sampson] PR#3120
+
+ *) PORT: Fixed a misplaced #endif for NetBSD/OpenBSD section
+ of ap_config.h that would skip several defines if DEFAULT_GROUP
+ was overridden. [Roy Fielding]
+
+ *) PORT: The I86 version of DGUX has support for strncasecmp and
+ strcasecmp, so allow it in ap_config.h. [Amiel Lee Yee] PR#3247
+
+ *) Fix ordering of definitions in ap_config.h so that ap_inline is
+ defined before it might be used. [Victor Khimenko]
+
+ *) PORT: Add Dynamic Shared Object (DSO) support for BSDI (v4.0).
+ [Tom Serkowski <tks@bsdi.com>] PR#3453
+
+ *) Make generation of src/Configuration.apaci more robust: It failed to
+ differenciate between modules when one module name was a postfix of
+ another (e.g. cgi vs. fastcgi). We now check for mod_XXX, libXXX and even
+ just XXX (think about totally non-standard names like "apache_ssl", too).
+ [Ralf S. Engelschall] PR#3380
+
+ *) In src/Configure remove the SERVER_SUBVERSION support (already deprecated
+ since 1.3b7) and make whitespace handling more robust (it failed horrible
+ when whitespaces were present in the arguments of -D options).
+ [Ralf S. Engelschall] PR#3240
+
+ *) Add APACI --shadow=DIR variant (in addition to --shadow). This now first
+ creates an external package shadow tree in DIR before the local build
+ shadow tree is generated under DIR. This way one can have the extracted
+ Apache distribution tree read-only on NFS or CDROM and still build Apache
+ from these sources. An automatically triggered VPATH-like mechanism is
+ provided through the TOP variable, too.
+ [Ralf S. Engelschall, Wilfredo Sanchez <wsanchez@apple.com>]
+
+ *) Fix negotiation so that a Vary response header is correctly
+ generated when, for a particular dimension, variants only vary
+ in having or not having a value for that dimension. [Paul Sutton]
+
+ *) Fix negotiation so that we prefer an encoded variant over an
+ unencoded variant if the user-agent explicitly says it can
+ accept that encoding. Previously we always preferred the unencoded
+ variant.
+ [Paul Ausbeck <paula@alumni.cse.ucsc.edu>, Paul Sutton] PR#3447
+
+ *) Fix APXS tool: query variables LIBS_SHLIB and TARGET were not recognized
+ and the usage page was inconsistent with the functionality and manpage.
+ [Ralf S. Engelschall]
+
+ *) Allow special options -Wc,xxx and -Wl,xxx on APXS compile/link command.
+ They can occur multiple times and their arguments (`xxx') are passed AS
+ IS to the compiler/linker command. [Ralf S. Engelschall]
+
+ *) Fixed possible (but harmless in practice) bug in the DBM lookup
+ procedure of mod_rewrite: very long keys were truncated.
+ [Ralf S. Engelschall]
+
+ *) Added a generic --with-layout=[FILE:]ID option. ID here is a layout
+ identifier, currently "Apache" and "GNU" are pre-defined in the file
+ config.layout. Custom layouts are possible by using FILE:ID as the
+ argument where the layout ID is taken from FILE.
+
+ The config.layout file consists of <Layout ID>..</Layout> sections
+ where inside those sections "path_variable: path_value" pairs can be
+ specified. These lines are converted to path_variable='path_value'.
+
+ *) Add a DefaultLanguage directive so that files missing a language
+ extension (e.g., .fr, .de) can be labelled as being some other
+ default language. DefaultLanguage can appear in <Directory> and
+ <Files> containers as well as .htaccess files. [Paul Sutton]
+ PR#1180
+
+ *) Fix TARGET configuration when configuring and installing using
+ APACI configure. TARGET now defines the basename of the configuration
+ file, startup script, manual page, etc. log_error_core() now reports
+ the server binary name given by argv[0]. TARGET can now also be defined
+ with --target=TARGET parameter passed to APACI configure.
+ [Ralf Engelschall, Randy Terbush]
+
+ *) mod_include.c:handle_perl() now properly tests for OPT_INCNOEXEC
+ rather than OPT_INCLUDES [Rainer Schoepf <schoepf@uni-mainz.de>]
+
+ *) ap_md5_binary() was using sprintf() rather than a table lookup
+ to convert binary bytes to hex digits.
+ [Ronald Tschalär <ronald@innovation.ch>] PR#3409
+
+ *) Fix SEGV in TCN negotiation if no variants are acceptable.
+ [Martin Plechsmid <plechsmi@karlin.mff.cuni.cz>] PR#1987
+
+ *) API: ap_exists_config_define() function is now "public" [Doug MacEachern]
+
+ *) Fix documentation of `Action' directive: It can activate a CGI script
+ when either a handler or a MIME content type is triggered by the request.
+ [Andrew Pimlott <pimlott@math.harvard.edu>] PR#3340
+
+ *) Document the `add' command of `dbmmanage' in `dbmmanage.1' manpage.
+ [David MacKenzie <djm@uu.net>] PR#3394
+
+ *) Ignore a "ErrorDocument 401" directive with a full URL and write a
+ notice to the error log. It is not possible to send a 401 response
+ and a redirect at the same time. [Lars Eilebrecht]
+
+ *) Fallback to native compilers for IRIX-32 platform. It seems that
+ a gcc 2.8.1 compiled apache is logging client addresses with all
+ bits set (255.255.255.255). This is the second such problem caused
+ by gcc 2.8.1 compiler. The first being broken semaphore locking.
+ [Randy Terbush]
+
+ *) Updated mime.types to reflect current Internet media types
+ and include a URL to the registry.
+ [Manoj Kasichainula, Roy Fielding] PR#2380, 2286, 2246
+
+ *) SECURITY: Do a more complete check in mod_include to avoid
+ an infinite loop of recursive SSI includes. [Marc Slemko] PR#3323
+
+ *) Add APACI --suexec-docroot and --suexec-logfile options which can be
+ used to set the document root directory (DOC_ROOT) and the suexec
+ logfile (LOG_EXEC), respectively. Additionally the --layout option
+ was changed to show more information about the suEXEC setup.
+ [Lars Eilebrecht] PR#3316, 3357, 3361
+
+ *) Added the last two WebDAV status codes of 424 (Failed Dependency)
+ and 507 (Insufficient Storage) for use by third-party modules.
+ [Roy Fielding]
+
+ *) Enabled all of the WebDAV method names for use by third-party
+ modules, Limit, and Script directives. That includes PATCH,
+ PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, and UNLOCK.
+ Improved mod_actions.c so that it can use any of the methods
+ defined in httpd.h. Added ap_method_number_of(method) for
+ getting the internal method number. [Roy Fielding]
+
+ *) PORT: Add a port to the TPF OS. [Joe Moenich <moenich@us.ibm.com> and
+ others at IBM]
+
+ *) Fix problems with handling of UNC names (e.g., \\host\path)
+ on Win32. [Ken Parzygnat <kparz@us.ibm.com>]
+
+ *) Rework os_canonical_*() on Win32 so it's simpler, more
+ robust, and works. [Ken Parzygnat <kparz@us.ibm.com>]
+ PR#2555, 2915, 3064, 3232
+
+ *) Work around incomplete implementation of strftime on Win32.
+ [Manoj Kasichainula, Ken Parzygnat <kparz@us.ibm.com>]
+
+ *) Move a typedef to fix compile problems on Linux with 1.x kernels.
+ [Manoj Kasichainula] PR#3177
+
+ *) PORT: Add a port to the Concurrent PowerMAX OS. [Tom Horsley
+ <Tom.Horsley@mail.ccur.com>]
+
+ *) WIN32: Log more explicit error messages if spawning an interpreted
+ script failed, including the command line used to attempt to execute
+ the interpreter and the Win32 error code returned. [Marc Slemko]
+
+ *) Disable sending of error-notes on a 500 (Internal Server Error) response
+ since it often includes file path info. Enable sending of error-notes
+ on a 501 (Method Not Implemented). [Roy Fielding] PR#3173
+
+ *) http_config.c would respond with 501 (Method Not Implemented) if a
+ content type handler was specified but could not be found, which
+ should have been a 500 response. Likewise, mod_proxy.c would responsd
+ with a 501 if the URI scheme is unrecognized instead of the correct
+ response of 403 (Forbidden). [Roy Fielding]
+
+ *) SECURITY: Eliminate DoS attack when a bad URI path contains what
+ looks like a printf format escape. [Marc Slemko, Studenten Net Twente]
+
+ *) Fix in mod_autoindex: for files where the last modified time stamp was
+ unavailable, an empty string was printed which was 2 bytes short.
+ The size and description columns were therefore not aligned correctly.
+ [Martin Kraemer] (no PR#)
+
+ *) Update BS2000 OS code to work with recent versions. Starting with
+ release A17, the child fork() must be replaced by a _rfork().
+ (BS2000 only) [Martin Kraemer]
+
+ *) Add the actual server_rec structure of the specific Vhost to the
+ scoreboard file and avoid a string copy (as well as allow some
+ further future enhancements). [Harrie Hazewinkel
+ <harrie.hazewinkel@jrc.it>]
+
+ *) Add APACI --permute-module=foo:bar option which can be used to
+ on-the-fly/batch permute the order of two modules (mod_foo and mod_bar)
+ in the Configuration[.apaci] file. Two special and important variants are
+ supported for the option argument: first BEGIN:foo which permutes module
+ mod_foo with the begin of the module list, i.e. it `moves' the module to
+ the begin of the list (gives it lowest priority). And second foo:END
+ which permutes mod_foo with the end of the module list, i.e. it `moves'
+ the module to the end of the list (gives it highest priority).
+ [Ralf S. Engelschall]
+
+ *) Fix problem with 'apache -k shutdown' and startup event
+ synchronisation (Win32). [Ken Parzygnat <kparz@raleigh.ibm.com>]
+ PR#3255
+
+ *) The config parser wasn't correctly noticing a missing '>'
+ on container start lines (e.g., it wouldn't spot
+ "<Directory /" as a syntax error). [Ryan Bloom <rbbloom@us.ibm.com>]
+ PR#3279
+
+ *) Add a 'RemoveHandler' directive which will selectively remove
+ all handler associations for the specified file extensions.
+ [Ryan Bloom <rbbloom@us.ibm.com>] PR#1799.
+
+ *) Properly handle & allow "nul" and ".*/null" in AccessConfig and
+ ResourceConfig directives on Win32. Also add a note to the effect
+ of 'useless User directive ignored on Win32' to the errorlog if
+ a User directive is encountered on Win32.
+ [Ken Parzygnat <kparz@raleigh.ibm.com>] PR#2078, 2303.
+
+ *) Fix multiple whitespace handling in imagemaps for mod_imap which was
+ broken since Apache 1.3.1 where we took out compressing of multiple
+ spaces in ap_cfg_getline().
+ [Ivan Richwalski <ivan@seppuku.net>] PR#3249
+
+ *) Fix Berkeley-DB/2.x support in mod_auth_db: The data structures were not
+ initialized correctly and the db_open() call used an invalid mode
+ parameter. [Ron Klatchko <ron@ckm.ucsf.edu>] PR#3171
+
+ *) PORT: DSO support for UnixWare 7
+ [Ralf S. Engelschall, Ron Record <rr@sco.com>]
+
+ *) Merge the contents of the {srm,access}.conf-dist* files into the
+ httpd.conf-dist* files. The srm and access files now contain
+ only comments, and httpd.conf has all the combined contents in
+ a rational order. [Ken Coar]
+
+ *) PORT: DSO/ELF support for FreeBSD 3.0.
+ [Ralf S. Engelschall, Dirk Froemberg <ibex@physik.TU-Berlin.DE>]
+
+ *) Add a "default-handler" handler that calls the default_hander()
+ function which is normally called for static content. This allows
+ you to override a specific handler. [Marc Slemko]
+
+ *) Further simplify checking for absolute paths by replacing an
+ hard-coded syntax check with a call to a routine we already created to
+ do this. [Ken Parzygnat <kparz@raleigh.ibm.com>] PR#2976, 3074
+
+ *) Log an error if we encounter a malformed "require" directive
+ in mod_auth if we know that we know that no other module can
+ deal with it. [Marc Slemko]
+
+ *) Remove ap_private_extern method of hiding conflicting symbols
+ on the NEXT platform because it is not correct for all versions,
+ and the versions for which it is correct are unknown.
+ [Wilfredo Sanchez <wsanchez@apple.com>]
+
+ *) Fix inheritance of IndexOptions NameWidth and remove unintended
+ restriction on +NameWidth, +IconHeight, and +IconWidth. [Ken Coar]
+
+ *) Fix per-directory config merging for cases in which a 500 error
+ is encountered in an .htaccess file somewhere down the tree.
+ [Ken Coar] PR#2409
+
+ *) Minor performance improvement to ap_escape_html(). [Roy Fielding]
+
+ *) Fixed a segmentation violation in mod_proxy when a response is
+ non-cachable. [Roy Fielding, traced by Doug Bloebaum]. PR#2950, 3056
+
+Changes with Apache 1.3.3
+
+ *) Added a complete implementation of the Expect header field as
+ specified in rev-05 of HTTP/1.1. Disabled the 100 Continue
+ response when we already know the final status, which is mighty
+ useful for PUT responses that result in 302 or 401. [Roy Fielding]
+
+ *) Remove extra trailing whitespace from the getline results as part
+ of the protocol processing, which is extra nice because it works
+ between continuation lines, is almost no cost in the normal case
+ of no extra whitespace, and saves memory. [Roy Fielding]
+
+ *) Added new HTTP status codes and default response bodies from the
+ revised HTTP/1.1 (307, 416, 417), WebDAV (102, 207, 422, 423), and
+ HTTP Extension Framework (510) specifications. Did not add the
+ WebDAV 424 and 425 codes because they are bogus. We don't use any
+ of these codes yet, but they are now available to 3rd-party modules.
+ [Roy Fielding]
+
+ *) Fix a possible race condition between timed-out requests and the
+ ap_bhalfduplex select that might result in an infinite loop on
+ platforms that do not validate the descriptor. [Roy Fielding]
+
+ *) WIN32: Add "-k shutdown" and "-k restart" options to signal a
+ running Apache server [Paul Sutton]
+
+ *) Fix mod_autoindex bug where directories got a size of "0k" instead
+ of "-". [Martin Plechsmid <plechsmi@karlin.mff.cuni.cz>, Marc Slemko]
+ PR#3130
+
+ *) PORT: DRS 6000 machine. [Paul Debleecker <pdebleecker@jetair.be>]
+
+ *) Add the server signature text (from the core ServerSignature directive)
+ to the list of envariables available to scripts, SSI, and the like.
+ [Ken Coar]
+
+ *) PORT: Fix sys/resource.h handling for SCO 3.x platform.
+ [M. Laak <maert@proinv.ee>] PR#3108
+
+ *) Fallback from sysconf-based to plain HZ-based `ticks per second'
+ calculation in mod_status for all systems which don't have POSIX
+ sysconf() (like UTS 2.1) and not only for the NEXT platform.
+ [Dave Dykstra <dwd@bell-labs.com>] PR#3055
+
+ *) Fix `require ...' directive parsing in mod_auth, mod_auth_dbm and
+ mod_auth_db by using ap_getword_white() (which uses ap_isspace())
+ instead of ap_getword(..., ' ') (which parses only according to spaces
+ but not tabs). [James Morris <jmorris@intercode.com.au>,
+ Ralf S. Engelschall] PR#3105
+
+ *) Fix the SERVER_NAME variable under sub-request situations (where
+ `UseCanonicalName off' is used) like CGI's called from SSI pages or
+ RewriteCond variables by adopting r->hostname to sub-requests.
+ [James Grinter <jrg@blodwen.demon.co.uk>] PR#3111
+
+ *) Fix stderr redirection under syslog-based error logging situation.
+ [Youichirou Koga <y-koga@jp.FreeBSD.org>] PR#3095
+
+ *) Document `ErrorLog syslog:facility' variant of error logging.
+ [Youichirou Koga <y-koga@jp.FreeBSD.org>] PR#3096
+
+ *) Fix http://localhost/ hints in top-level INSTALL document.
+ [Rob Jenson <robjen@spotch.com>, Ralf S. Engelschall] PR#3088
+
+ *) Quote paths in default configuration files. [Wilfredo Sanchez]
+
+ *) PORT: Remove extra HAVE_SYS_RESOURCE_H define for RHAPSODY since
+ it is now taken care of properly by the header file tests.
+ [Wilfredo Sanchez <wsanchez@apple.com>]
+
+ *) Fix problem with scripts and filehandle inheritance on Win32.
+ [Ken Parzygnat <kparz@raleigh.ibm.com>] PR#2884, 2910
+
+ *) Win32 name canonicalisation could end up using the server's
+ working directory to fill in some blanks. [Ken Parzygnat
+ <kparz@raleigh.ibm.com>] PR#3001
+
+ *) Correct invalid assumption by ap_sub_req_lookup_file() that all
+ absolute paths begin with "/" -- because they don't on Win32.
+ [Ken Parzygnat <kparz@raleigh.ibm.com>] PR#2976, 3074
+
+ *) Add [REDIRECT_]VARIANTS environment variable to mod_speling
+ so that ErrorDocument 300 processors can reformat the list
+ if desired. [Ken Coar] PR#2859
+
+ *) Add +/- incremental prefixes to IndexOptions keywords, and
+ enable merging of multiple IndexOptions directives. [Ken Coar]
+
+ *) PORT: Allow GuessOS to recognize Unixware 7.0.1 [Steve Cameron
+ <steve.cameron@compaq.com>]
+
+ *) Reconstructed the loop through multiple htaccess file names so
+ that missing files are not confused with unreadable files.
+ [Roy Fielding]
+
+ *) The ap_pfopen and ap_pfdopen routines were failing to protect the
+ errno on an error, which leads to one error being mistaken for
+ another when reading non-existent .htaccess files.
+ [Jim Jagielski]
+
+ *) OS/2: The new header tests get things right, need to update
+ ap_config.h. [Brian Havard]
+
+ *) The Perl %ENV hash will now be setup by default when using the
+ mod_include `perl' command [Doug MacEachern]
+
+ *) PORT: Add Pyramid DC/OSx support to configuration mechanism.
+ [Earle Ake <akee@wpdiss1.wpafb.af.mil>]
+
+ *) PORT: Fix sys/resource.h handling for Amdahl's UTS 2.1
+ [Dave Dykstra <dwd@bell-labs.com>] PR#3054
+
+ *) Correct comment in mod_log_config.c about its internals.
+ [Elf Sternberg <elf@halcyon.com>]
+
+ *) Avoid possible line overflow in Configure: Use an awkfile to
+ handle the creation of modules.c [Jim Jagielski]
+
+Changes with Apache 1.3.2
+
+ *) Fix bug in ap_remove_module(), which caused problems for dso's
+ who were the top_module. [Doug MacEachern]
+
+ *) Add support for Berkeley-DB/2.x (in addition to Berkeley-DB/1.x) to
+ mod_auth_db to both be friendly to users who wants to use this version
+ and to avoid problems under platforms where only version 2.x is present.
+ [Dan Jacobowitz <drow@false.org>, Ralf S. Engelschall]
+
+ *) When using ap_log_rerror(), make the error message available to the
+ *ERROR_NOTES envariables by default. [Ken Coar]
+
+ *) BS2000 platform only: get rid of the nasty BS2000AuthFile.
+ You now must define a BS2000Account name for the server User.
+ This has fewer security implications than the old approach.
+ [Martin Kraemer]
+
+ *) Fix SHARED_CORE feature for HPUX platform: We now use extension `.sl'
+ instead of `.so' and `SHLIB_PATH' instead of `LD_LIBRARY_PATH' on this
+ platform to make the braindead HPUX linker happy. Notice, for the module
+ DSOs we don't have to use this, because these are loaded manually (and
+ not via HPUX' dld). [Ralf S. Engelschall] PR#2905, PR#2968
+
+ *) Remove 64 thread limit on Win32.
+ [Bill Stoddard <stoddard@raleigh.ibm.com>]
+
+ *) Remove redundant substitutions in top-level Makefile.tmpl.
+ [Ralf S. Engelschall]
+
+ *) Fix APACI's `Group' configuration adjustment - especially for Linux
+ platforms where `nogroup' exists in /etc/group. [Ralf S. Engelschall]
+
+ *) Make PrintPath work generically instead of having one version
+ strictly for OS/2. [Jim Jagielski, Brian Havard]
+
+ *) Fix the recently introduced C header file checking: We now use the C
+ pre-processor pass only (and no longer the complete compiler pass) to
+ determine whether a C header file exists or not. Because only this way
+ we're safe against inter-header dependencies (which caused horrible
+ portability problems). The only drawback is that we now have a CPP
+ configuration variable which has to be determined first (we do a similar
+ approach as GNU Autoconf does here). When all fails the user still has
+ the possibility to override it manually via APACI or src/Configuration.
+ As a fallback for the header check itself we can directly check the
+ existance of the file under /usr/include, too.
+ [Ralf S. Engelschall] PR#2777
+
+ *) PORT: Added RHAPSODY (Mac OS X Server) support. MAP_TMPFILE defined
+ as an alternate mechanism for mmap'd shared memory for RHAPSODY.
+ ap_private_extern defined to hide symbols that conflict with loaded
+ dynamic libraries on the NEXT and RHAPSODY platforms.
+ [Wilfredo Sanchez <wsanchez@apple.com>]
+
+ *) Delete PID file on clean shutdowns.
+ [Charles Randall <crandall@matchlogic.com>] PR#2947
+
+ *) Fix mod_auth_*.html documents: NSCA -> NCSA
+ [Youichirou Koga <y-koga@jp.FreeBSD.org>] PR#2991
+
+ *) Fix INSTALL document: www.gnu.ai.mit.edu -> www.gnu.org
+ [Karl Berry <karl@gnu.org>] PR#2994
+
+ *) Fix dbmmanage.1 manual page.
+ [Youichirou Koga <y-koga@jp.FreeBSD.org>] PR#2992
+
+ *) Fix possible buffer overflow situation in suexec.c.
+ [Jeff Stewart <jws@purdue.edu>] PR#2790
+
+ *) Add some more LIBS for the SCO5 platform which are needed for the already
+ used -lprot. It's actually a bug in SCO5, of course.
+ [Ronald Record <rr@sco.com>] PR#2533
+
+ *) Fix documentation of ProxyPass/ProxyPassReverse according to the
+ trailing slash problem. [Jon Drukman <jsd@gamespot.com>] PR#2933
+
+ *) Remove `-msym' option from LDFLAGS_SHLIB for the Digital UNIX (OSF/1)
+ platform, because it's only supported under version 4.0 and higher. But
+ because our GuessOS is still unaware of Digital UNIX versions and the
+ -msym is just to optimize the DSO statup time a little bit it's safe and
+ best when we leave it out now. [Ralf S. Engelschall] PR#2969
+
+ *) Fix the ap_log_error_old(), ap_log_unixerr() and ap_log_printf()
+ functions: First all three functions no longer fail on strings containing
+ "%" chars and second ap_log_printf() no longer does a double-formatting
+ (instead it directly passes through the message to be formatted to the
+ real internal formatting function). [Ralf S. Engelschall] PR#2941
+
+ *) Allow "Include" directives anywhere in the server config
+ files (but not .htaccess files). [Ken Coar] PR#2727
+
+ *) The proxy was refusing to serve CONNECT requests except to
+ port 443 (https://) and 563 (snews://). The new AllowCONNECT
+ directive allows the configuration of the ports to which a
+ CONNECT is allowed. [Sameer Parekh, Martin Kraemer]
+
+ *) mod_expires will now act on content that is not sent from a file
+ on disk. Previously it would never add an Expires: header to
+ any response that did not come from a file on disk; the only
+ case where it still doesn't (and can't) add one for that type of
+ content is if you are using a modification date based setting.
+ [Marc Slemko, Paul Phillips <paulp@go2net.com>]
+
+ *) Problems encountered during .htaccess parsing or CGI execution
+ that lead to a "500 Server Error" condition now provide explanatory
+ text (in the *ERROR_NOTES envariable) to ErrorDocument 500 scripts.
+ [Ken Coar] PR#1291
+
+ *) Add NameWidth keyword to IndexOptions directive so that the
+ width of the filename column is customisable. [Ken Coar, Dean Gaudet]
+ PR#1949, 2324.
+
+ *) Recognize lowercase _and_ uppercase `uname' results under
+ SCO OpenServer. [David Coelho <drc@ppt.com>]
+
+ *) As duplicate "HTTP/1.0 200 OK" lines within the header seem to be
+ a common problem of (mis-administrated?) IIS servers, make the apache
+ proxy immune to these errors (and ignore the duplicates, but log
+ the fact to error_log). [Martin Kraemer], after the proposal in PR#2914
+
+ *) The <IfModule and <IfDefine block starting directives now only
+ allow exactly one argument. Previously, the optional negation
+ character '!' could be separated by whitespace without a syntax
+ error being reported, albeit defeating the IfModule functionality
+ (enclosed directives would ALWAYS be executed). By using the
+ stricter syntax, these hard-to-track errors can be avoided.
+ [Martin Kraemer]
+
+ *) Simplify handling of IndexOptions in mod_autoindex -- and BTW
+ cause the standalone FancyIndexing directive to logically OR
+ into any existing IndexOptions settings rather than wiping
+ them out. [Ken Coar]
+
+ *) Changes in ftp proxy: make URL parsing simpler by using the
+ parsed_uri stuff.
+ + Add display of the "current directory" in cases where it's
+ different from the supplied path (e.g., ftp://user@host/ lives
+ in /home/user, not in /, therefore clicking on "../" in the
+ starting directory might send us to /home/).
+ + When ftp login fails, (esp. when a user name was part of the
+ URL already), we now return [401 Unauthorized ] to allow the
+ browser to pop up an authorization dialog. This makes passwords
+ slightly less visible (they don't appear in the regular log files)
+ and implements a functionality that other www proxy servers
+ already offered.
+ [Martin Kraemer]
+
+ *) Triggered by the recent "Via:" header changes, the proxy module would
+ dump core for replies with invalid headers (e.g., duplicate
+ "HTTP/1.0 200 OK" lines). These errors are now logged and the
+ core dump is avoided. Also, broken replies are not cached.
+ [Martin Kraemer] PR#2914
+
+ *) new `GprofDir' directive when compiled with -DGPROF, where gprof can
+ plop gmon.out profile data for each child [Doug MacEachern]
+
+ *) Use the construct ``"$@"'' instead of ``$*'' in the generated
+ config.status script to be immune against arguments with whitespaces.
+ [Yves Arrouye <yves@apple.com>] PR#2866
+
+ *) Replace the inlined information grabbing stuff for the configuration
+ adjustment feature (no --without-confadjust) with calls to a new helper
+ script `buildinfo.sh' which is both more flexible and already proofed to
+ be more robust against platform differences. This mainly fixes the
+ recently occured ``sed: command garbled: ...'' problems.
+ [Ralf S. Engelschall] PR#2776, PR#2848
+
+ *) Make ab.c again pass ``gcc -Wall -Wshadow -Wpointer-arith -Wcast-align
+ -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Winline''
+ without complains after we recently added the POST feature.
+ [Ralf S. Engelschall]
+
+ *) Renamed is_HTTP_xxx() macros to ap_is_HTTP_xxx() name. They are used inside
+ modules as API functions and we forgot them at the big symbol renaming.
+ [Ralf S. Engelschall]
+
+ *) Remove bad reference to non-existing SERVER_VERSION in mod_rewrite.html
+ [Youichirou Koga <y-koga@jp.FreeBSD.ORG>] PR#2895
+
+ *) Dynamically size the filename column of mod_autoindex output.
+ [Dean Gaudet]
+
+ *) Add the ability to do POST requests to the ab benchmarking tool.
+ [Kurt Sussman <kls@best.com>] PR#2871
+
+ *) Bump up MAX_ENV_FLAGS in mod_rewrite.h from the too conservatice limit of
+ 5 to 10 because there are some users out there who always have 5 to 8
+ variables in one RewriteRule and had to patch mod_rewrite.h for every
+ release. So 15 should be now more than enough, even for them. (I never
+ needed more than 4 in my RewriteRules ;-)
+ [Ralf S. Engelschall]
+
+ *) Make the proxy generate and understand Via: headers
+ [Martin Kraemer]
+
+ *) Change the proxy to use tables instead of array_headers for
+ the header lines. [Martin Kraemer]
+
+ *) Make sure the config.status file is not overridden when just
+ ``configure --help'' is used. [Ralf S. Engelschall] PR#2844
+
+ *) Split MODULE_MAGIC_NUMBER into _MAJOR/_MINOR numbers. This should
+ provide a way to trace API changes that add functionality but do
+ not create a compatibility issue for precompiled modules, etc.
+ See include/ap_mmn.h for more details. [Randy Terbush]
+
+ *) Fix suexec installation under `make install root=xxx' situation.
+ [Ralf S. Engelschall]
+
+ *) Extend the output of the -V switch to include the paths of all
+ compiled-in configuration files, if they were overridden at
+ compile time, for least astonishment of the user.
+ [Martin Kraemer]
+
+ *) When READing a request in ExtendedStatus mode, the "old"
+ vhost, request and client information is not displayed.
+ [Jim Jagielski]
+
+ *) STATUS is no longer available. Full status information now
+ run-time configurable using the ExtendedStatus directive.
+ [Jim Jagielski]
+
+ *) SECURITY: Eliminate O(n^2) space DoS attacks (and other O(n^2)
+ cpu time attacks) in header parsing. Add ap_overlap_tables(),
+ a function which can be used to perform bulk update operations
+ on tables in a more efficient manner. [Dean Gaudet]
+
+ *) SECURITY: Added compile-time and configurable limits for
+ various aspects of reading a client request to avoid some simple
+ denial of service attacks, including limits on maximum request-line
+ size (LimitRequestLine), number of header fields (LimitRequestFields),
+ and size of any one header field (LimitRequestFieldsize). Also added
+ a configurable directive LimitRequestBody for limiting the size of the
+ request message body. [Roy Fielding]
+
+ *) Make status module aware of DNS and logging states, even if
+ STATUS not defined. [Jim Jagielski]
+
+ *) Fix a problem with the new OS/2 mutexes. [Brian Havard]
+
+ *) Enhance mod_speling so that CheckSpelling can be used in
+ <Directory> containers and .htaccess files. [Ken Coar]
+
+ *) API: new ap_custom_response() function for hooking into the
+ ErrorDocument mechanism at runtime [Doug MacEachern]
+
+ *) API: new ap_uuencode() function [Doug MacEachern]
+
+ *) API: scan_script_header_err_core() now "public" and renamed
+ ap_scan_script_header_err_core() [Doug MacEachern]
+
+ *) The 'status' module will now show the process pid's and their
+ state even without full STATUS accounting. [Jim Jagielski]
+
+ *) Restore the client IP address to the error log messages, this
+ was lost during the transition from 1.2 to 1.3. Add a new
+ function ap_log_rerror() which takes a request_rec * and
+ formats it appropriately. [Dean Gaudet] PR#2661
+
+ *) Cure ap_cfg_getline() of its nasty habit of compressing internal
+ whitespace in input lines -- including within quoted strings.
+ [Ken Coar]
+ but leading and trailing whitespace should continue to be
+ stripped [Martin Kraemer]
+
+ *) Cleanup of the PrintPath/PrintPathOS2 helper functions. Avoid
+ the ugly use of an env. variable and use command-line args for
+ alternate $PATH. Make more like advanced 'type's as well.
+ [Jim Jagielski]
+
+ *) The IRIXN32 Rule was being ignored. Configure now correctly adds
+ -n32 only if IRIXN32 says to. [Jim Jagielski, Alain St-Denis
+ <alain.st-denis@ec.gc.ca>] PR#2736
+
+ *) Clean up a warning in mod_proxy. [Ralf S. Engelschall]
+
+ *) Renamed __EMX__ (internal define of the gcc port under OS/2) to OS2
+ following the same idea as "MSVC vs WIN32". Additionally the src/os/emx/
+ directory was renamed to src/os/os2/ for consistency.
+ [Brian Havard, Ralf S. Engelschall]
+
+ *) Add new Rule SHARED_CHAIN which can be used to enable linking of DSO
+ files (here modules) against other DSO files (here shared libraries).
+ This is done by determining a subset of LIBS which can be safely used for
+ linking the DSOs, i.e. PIC libs and shared libs. Currently the rule is
+ disabled for all platforms to avoid problems with this (experimental)
+ rule. But we provide it now for those people how ran into problems and
+ want to came out by forcing linking against DSOs.
+ [Ralf S. Engelschall] PR#2587
+
+ *) Fix suEXEC start message: Has to be of `notice' level to really get
+ printed together with the standard startup message because the `notice'
+ level is handled special inside ap_log_error() for startup messages.
+ [Ralf S. Engelschall] PR#2761 PR#2761 PR#2765
+
+ *) Add correct `model' MIME types from RFC2077 to mime.types file.
+ [Ralf S. Engelschall] PR#2732
+
+ *) Fixed examples in mod_rewrite.html document.
+ [Youichirou Koga <y-koga@jp.FreeBSD.org>, Ralf S. Engelschall] PR#2756
+
+ *) Allow ap_read_request errors to propagate through the normal request
+ handling loop so that the connection can be properly closed with
+ lingering_close, thus avoiding a potential TCP reset that would
+ cause the client to miss the HTTP error response. [Roy Fielding]
+
+ *) One more portability fix for APACI shadow tree support: Swap order of awk
+ and sed in top-level configure script to avoid sed fails on some
+ platforms (for instance SunOS 4.1.3 and NCR SysV) because of the
+ non-newline-termined output of Awk. [Ralf S. Engelschall] PR#2729
+
+ *) PORT: NEC EWS4800 support.
+ [MATSUURA Takanori <t-matsuu@protein.osaka-u.ac.jp>]
+
+ *) Fix a segfault in the proxy on OS/2. [Brian Havard]
+
+ *) Fix Win32 part of ap_spawn_child() by providing a reasonable child_info
+ structure instead of just NULL. This fixes at least the RewriteMap
+ programs under Win32. [Marco De Michele <mdemichele@tin.it>] PR#2483
+
+ *) Add workaround to top-level `configure' script for brain dead
+ `echo' commands which interpet escape sequences per default.
+ [Ralf S. Engelschall] PR#2654
+
+ *) Make sure that the path to the Perl interpreter is correctly
+ adjusted under `make install' also for the printenv CGI script.
+ [Ralf S. Engelschall] PR#2595
+
+ *) Update the mod_rewrite.html document to correctly reflect the situation
+ of the `proxy' (`[P]') feature. [Ralf S. Engelschall] PR#2679
+
+ *) Fix `install-includes' sub-target of `install' target in top-level
+ Makefile.tmpl: The umask+cp approach didn't work as expected (especially
+ for users which extracted the distribution under 'umask 077'), so replace
+ it by an explicit cp+chmod approach.
+ [Richard Lloyd, Curt Sampson, Ralf S. Engelschall] PR#2656 PR#2626
+
+ *) Fix `distclean' and `clean' targets in src/Makefile.tmpl to have same
+ behavior and to cleanup correctly even under enabled SHARED_CORE rule.
+ [Ralf S. Engelschall]
+
+ *) Use a more straight forward and thus less problematic Sed command in
+ src/helper/mkdir.sh script. [Ralf S. Engelschall]
+
+ *) Make sure the `configure' scripts doesn't fail when trying to guess the
+ domainname of the machine and there are multiple `domainname' and
+ `search' entries in /etc/resolv.conf.
+ [Ralf S. Engelschall] PR#2710
+
+ *) Add note about the SHARED_CORE requirement on some platforms also to the
+ INSTALL file because a lot of users don't read htdocs/manual/dso.html
+ first. [Ralf S. Engelschall] PR#2701
+
+ *) Fix document "hyperlink" for dso.html in src/Configuration.tmpl
+ [Knut A.Syed <Knut.Syed@nhh.no>] PR#2674
+
+ *) Modify mod_rewrite to update the Vary response field if the URL rewriting
+ engine does any manipulations or decisions based upon request fields.
+ [Ken Coar] PR#1644
+
+ *) Document the special APACI behavior for installation paths where
+ ``/apache'' is appended to paths under some (well defined, of course)
+ situations to prevent pollution of system locations with Apache files.
+ [Ralf S. Engelschall] PR#2660
+
+ *) Fixed problem with buffered response message not being sent for
+ the read_request error conditions of URI-too-long (414) and
+ malformed header fields (400). [Roy Fielding] PR#2646
+
+ *) Add support for the Max-Forwards: header line required by RFC2068 for
+ the TRACE method. This allows apache to TRACE along a chain of proxies
+ up to a predetermined depth. [Martin Kraemer]
+
+ *) Fix SHARED_CORE rule: The CFLAGS_SHLIB variable is no longer doubled
+ (compilers complained) and the .so.V.R.P filename extension was adjusted
+ to correctly reflect the 1.3.2 version.
+ [Ralf S. Engelschall] PR#2644
+
+ *) SECURITY: Plug "..." and other canonicalization holes under OS/2.
+ [Brian Havard]
+
+ *) PORT: implement serialized accepts for OS/2. [Brian Havard]
+
+ *) mod_include had problems with the fsize and flastmod directives
+ under WIN32. Fix also avoids the minor security hole of using
+ ".." paths for fsize and flastmod.
+ [Manoj Kasichainula <manojk@raleigh.ibm.com>] PR#2355
+
+ *) Fixed some Makefile dependency problems. [Dean Gaudet]
+
+Changes with Apache 1.3.1
+
+ *) Disable the incorrect entry for application/msword in the
+ mod_mime_magic "magic" file because it also matches other Office
+ documents. [Ralf S. Engelschall] PR#2608
+
+ *) Fix broken RANLIB handling in src/Configure (the entry from
+ src/Configuration.tmpl was ignored) and additionally force RANLIB to
+ /bin/true under HP/UX where ranlib exists but is deprecated.
+ [Ralf S. Engelschall] PR#2627
+
+ *) 'apachectl status' failed on some systems.
+ [Steve VanDevender <stevev@darkwing.uoregon.edu>, Lars Eilebrecht] PR#2613
+
+ *) Add new flags for ap_unparse_uri_components() to make it generate
+ the scheme://sitepart string only, or to omit the query string.
+ [Martin Kraemer]
+
+ *) WIN32: Canonicalize ServerRoot before checking to see if it
+ is a valid directory. The failure to do this caused certain
+ ServerRoot settings (eg. "ServerRoot /apache") to be improperly
+ rejected. [Marc Slemko]
+
+ *) Global renaming of C header files to both get rid of conflicts with third
+ party packages and to again reach consistency:
+ 1. conf.h -> ap_config.h
+ 2. conf_auto.h -> ap_config_auto.h \ these are now merged
+ 3. ap_config.h -> ap_config_auto.h / in the config process
+ 4. compat.h -> ap_compat.h
+ 5. apctype.h -> ap_ctype.h
+ Backward compatibility files for conf.h and compat.h were created.
+
+ *) mod_mmap_static will no longer take action on requests unless at
+ least one "mmapfile" directive is present in the configuration.
+ This experimental module has to do some black magic to operate
+ inside the current API and thus creates side-effects for other
+ modules under some circumstances.
+ [Ralf S. Engelschall]
+
+ *) Add conservative ticks around more egrep arguments in top-level configure
+ to avoid problems under brain-dead platforms like Digital UNIX (OSF1).
+ [Ralf S. Engelschall] PR#2596
+
+ *) mod_rewrite created RewriteLock files under the UID of the parent
+ process, thus the child processes had no write access to the files.
+ Now a chown() is done on the file to the uid of the children,
+ if applicable. [Lars Eilebrecht, Ralf S. Engelschall] PR#2341
+
+ *) Autogenerate some HAVE_XXXXX_H defines in conf_auto.h (determined via
+ TestCompile) instead of defining them manually in conf.h based on less
+ accurate platform definitions. This way we no longer have to fiddle with
+ OS-type and/or OS-version identifiers to discover whether a system header
+ file exists or not. Instead we now directly check for the existence of
+ those esoteric ones.
+ [Ralf S. Engelschall] PR#2093, PR#2361, PR#2377, PR#2434,
+ PR#2524, PR#2525, PR#2533, PR#2569
+
+ *) mod_setenvif (BrowserMatch* and friends) will now match a missing
+ field with "^$". [Ken Coar]
+
+ *) Set the RTLD_GLOBAL dlopen mode parameter to allow dynamically loaded
+ modules to load their own modules dynamically. This improves mod_perl
+ and mod_php3 when these modules are loaded dynamically into Apache.
+ [Rasmus Lerdorf]
+
+ *) Cache a proxied request in the event that the client cancels the
+ transfer, provided that the configured percentage of the file has
+ already been transfered. It works for HTTP transfers only. The
+ new configuration directive is called CacheForceCompletion.
+ [Glen Parker <glenebob@nwlink.com>] PR#2277
+
+ *) Add the "<!DOCTYPE HTML" magic cookie used by modern documents (and
+ required by HTML 3.2 and later) to mod_mime_magic's conf/magic.
+ [Anna Shergold <anna@inext.co.uk>]
+
+ *) Fix yet another signal-based race condition involving nested timers.
+ Signals suck. [Dean Gaudet]
+
+ *) suexec's error messages have been clarified a little bit. [Ken Coar]
+
+ *) Clean up some, but perhaps not all, 8-bit character set problems
+ with config file parsing, and URL parsing. We now define
+ ap_isdigit(), ap_isupper(), ... which cast to an (unsigned char).
+ This should work on most modern unixes.
+ [Dean Gaudet] PR#800, 2282, 2553 (and others)
+
+ *) The "handler not found" error was issued in cases where the handler
+ really did exist, but was just declining to serve the request.
+ [John Van Essen <jve@gamers.org>] PR#2529
+
+ *) Add Dynamic Shared Object (DSO) support for SCO5 (OpenServer 5.0.x).
+ [Ronald Record <rr@sco.com>] PR#2533
+
+ *) The APACI libexecdir was not extended with an "apache/" subdir
+ if the installation prefix didn't already contain "apache", but
+ it should be because the DSO files are Apache-specific. Now
+ libexecdir is treated the same way sysconfdir, datadir, localstatedir
+ and includedir are already treated.
+ [Charles Levert <charles@comm.polymtl.ca>] PR#2551
+
+ *) The <Limit> parsing routine was incorrectly treating methods as
+ case-insensitive. [Ken Coar]
+
+ *) The ap_bprintf() code neglected to test if there was an error on
+ the connection. ap_bflush() misdiagnosed a failure as a success.
+ [Dean Gaudet]
+
+ *) add support for #perl arg interpolation in mod_include
+ [Doug MacEachern]
+
+ *) API: Name changes of table_elts to ap_table_elts, is_table_empty
+ to ap_is_table_empty and bgetflag to ap_bgetflag. [Ben Laurie]
+
+ *) PORT: Add UnixWare 7 support
+ [Vadim Kostoglodoff <vadim@olly.ru>] PR#2463
+
+ *) Fix the Guess-DSO-flags-from-Perl stuff in src/Configure: "perl" was
+ used instead of "$PERL" which contains the correctly determined Perl
+ interpreter (important for instance on systems where "perl" and "perl5"
+ exists, like BSDI or FreeBSD, etc).
+ [Ralf S. Engelschall] PR#2505
+
+ *) Move the initial suEXEC-related startup message from plain
+ fprintf()/stderr to a delayed ap_log_error()-based one to avoid problems
+ when Apache is started from inetd (instead of standalone). Under this
+ situation startup messages on stderr lead to problems (the line is sent
+ to the client in front of the requested document).
+ [Ralf S. Engelschall] PR#871, PR#1318
+
+ *) Add a flag so ap_fnmatch() can be used for case-blind pattern matching.
+ [Ken Coar, Dean Gaudet]
+
+ *) WIN32: Don't collapse multiple slashes in PATH_INFO.
+ [Ben Laurie, Bill Stoddard <wgstodda@us.ibm.com>] PR#2274
+
+ *) WIN32 SECURITY: Eliminate trailing "."s in path components. These are
+ ignored by the Windows filesystem, and so can be used to bypass security.
+ [Ben Laurie, Alexei Kosut].
+
+ *) We now attempt to dump core when we get SIGILL. [Jim Jagielski]
+
+ *) PORT: remove broken test for MAP_FILE in http_main.c.
+ [Wilfredo Sanchez <wsanchez@apple.com>]
+
+ *) PORT: Change support/apachectl to use "kill -0 $pid" to test if the
+ httpd is running. This should be more portable than figuring out
+ which of three dozen different versions of "ps" are installed.
+ [a cast of dozens]
+
+ *) WIN32: If we can't figure out how to execute a file in a script
+ directory, bail out of the request with an error message. [W G Stoddard]
+
+ *) WIN32 SECURITY: Eliminate directories consisting of three or more dots;
+ these are treated by Win32 as if they are ".." but are not detected by
+ other machinery within Apache. This is something of a kludge but
+ eliminates a security hole. [Manoj Kasichainula, Ben Laurie]
+
+ *) Move ap_escape_quotes() from src/ap to src/main/util.c; it uses
+ pools and thus pollutes libap (until the pool stuff is moved there).
+ [Ken Coar]
+
+ *) IndexIgnore should be case-blind on Win32 (and any other case-aware
+ but case-insensitive platforms). New #define for this added to conf.h
+ (CASE_BLIND_FILESYSTEM). [Ken Coar] PR#2455
+
+ *) Enable DSO support for OpenBSD in general, not only for 2.x, because it
+ also works for OpenBSD 1.x. [Ralf S. Engelschall]
+
+ *) PORT: Fix compilation problem on ARM Linux.
+ [Sam Kington <sam@illuminated.co.uk>] PR#2443
+
+ *) Let APACI's configure script determine some configuration parameters
+ (Group, Port, ServerAdmin, ServerName) via some intelligent tests to
+ remove some of the classical hurdles for new users when setting up
+ Apache. This is done per default because it is useful for the average
+ user. Package authors can use the --without-confadjust option to disable
+ these configuration adjustments.
+ [Ralf S. Engelschall]
+
+ *) Added an EXTRA_DEPS configuration parameter which can be used
+ to add an extra Makefile dependency for the httpd target, for instance
+ to external third-party libraries, etc.
+ [Ralf S. Engelschall]
+
+ *) Add <IfDefine>..</IfDefine> sections to the core module (with same spirit
+ as <IfModule>..</IfModule> sections) which can be used to skip or process
+ contained commands dependend of ``-D PARAMETER'' options on the command
+ line. This can be used to achieve logical conditions like <IfDefine
+ ReverseProxy> instead of physically ones (e.g. <IfModule mod_proxy.c>)
+ and thus especially can be used for conditionally loading DSO-based
+ modules via LoadModule, etc. [Ralf S. Engelschall]
+
+ *) PORT: clean up a warning in mod_status for OS/2. [Brian Havard]
+
+ *) Make table elements const. This may prevent obscure errors. [Ben Laurie]
+
+ *) Fix parsing of FTP `SIZE' responses in proxy module: The newline was not
+ truncated which forced following HTTP headers to be data in the HTTP
+ reponse. [Ralf S. Engelschall, Charles Fu <ccwf@bacchus.com>]
+ PR#2412, 2367
+
+ *) Portability fix for APACI shadow tree support: Swap order of awk and sed
+ in top-level configure script to avoid sed fails on some platforms (for
+ instance SunOS 4.1.3 and NCR SysV) because of the non-newline-termined
+ output of Awk. [Bill Houle <bhoule@sandiegoca.ncr.com>] PR#2435
+
+ *) Improve performance of directory listings (mod_autoindex) by comparing
+ integer keys (last-modified and size) as integers rather than converting
+ them to strings first. Also use a set of explicit byte tests rather
+ than strcmp() to check for parent directory-ness of an entry. Oh, and
+ make sure the parent directory (if displayed) is *always* listed first
+ regardless of the sort key. Overall performance winnage should be good
+ in CPU time, instruction cache, and memory usage, particularly for large
+ directories. [Ken Coar]
+
+ *) Add a tiny but useful goody to APACI's configure script: The generation
+ of a config.status script (as GNU Autoconf does) which remembers the used
+ configure command and hence can be used to restore the configuration by
+ just re-running this script or for remembering the configuration between
+ releases.
+ [Ralf S. Engelschall]
+
+ *) Add httpd -t (test) option for running configuration syntax tests only.
+ If something is broken it complains and exits with a return code
+ non-equal to 0. This can be used manually by the user to check the Apache
+ configuration after editing and is also automatically used by apachectl
+ on (graceful) restart command to make sure Apache doesn't die on restarts
+ because of a configuration which is now broken since the last (re)start.
+ This way `apachectl restart' can be used inside cronjobs without having
+ to expect Apache to be falling down. Additionally the httpd -t can be run
+ via `apachectl configtest'.
+ [Ralf S. Engelschall] PR#2393
+
+ *) Minor display fix for "install" target of top-level Makefile:
+ the displayed installation command was incorrect although the
+ executed command was correct. Now they are in sync.
+ [Ralf S. Engelschall] PR#2402
+
+ *) Correct initialization of variable `allowed_globals' in http_main.c
+ [Justin Bradford <justin@ukans.edu>] PR#2400
+
+ *) Apache would incorrectly downcase the entire Content-Type passed from
+ CGIs. This affected server-push scripts and such which use
+ multipart/x-mixed-replace;boundary=ThisRandomString.
+ [Dean Gaudet] PR#2394
+
+ *) PORT: QNX update to properly guess 32-bit systems.
+ [Sean Boudreau <seanb@qnx.com>] PR#2390
+
+ *) Make sure the DSO emulation code for HPUX finds the proprietary shl_xxx()
+ functions which are in libdld under HPUX 9/10.
+ [Ralf S. Engelschall] PR#2378
+
+ *) Make sure the "install" target of the top-level Makefile doesn't break
+ because of a return code of 1 from an "if" (for instance under braindead
+ Ultrix the result code of an "if" construct is 1 if the "then" clause
+ didn't match). [Ralf S. Engelschall]
+
+ *) Add an additional "dummy" target to the "$(LIB)" target in generated
+ modules/xxx/Makefile's to avoid problems with SVR4 Make under "full-DSO"
+ situation (no libxxx.a built, only mod_xxx.so's) where LIB and OBJS are
+ empty. [Ralf S. Engelschall, Dean Gaudet, Martin Kraemer]
+
+ *) Replace two bad sprintf() calls with ap_snprintf() variants in
+ mod_rewrite. [Ralf S. Engelschall]
+
+ *) Fix missing usage description for MetaFiles directive.
+ [David MacKenzie <djm@va.pubnix.com>] PR#2384
+
+ *) mod_log_config wouldn't let vhosts use log formats defined in the
+ main server. [Christof Damian <damian@mediaconsult.com>] PR#2090
+
+ *) mod_usertrack was corrupting the client hostname. As part of the
+ fix, the cookie values were slightly extended to include the
+ fully qualified hostname of the client.
+ [Dean Gaudet] PR#2190, 2229, 2366
+
+ *) Fix a typo in pool debugging code. [Alvaro Martinez Echevarria]
+
+ *) mod_unique_id did not work on alpha linux (in general on any
+ architecture that has 64-bit time_t).
+ [Alvaro Martinez Echevarria]
+
+ *) PORT: Make SCO 5 (and probably 3) compile again. [Ben Laurie]
+
+ *) PORT: NCR MPRAS systems have the same bug with SIGHUP restart that
+ Solaris systems experience. So define WORKAROUND_SOLARIS_BUG.
+ [Klaus Weber <kweber@chephren.germany.ncr.com>] PR#1973
+
+ *) Change "Options None" to "Options FollowSymLinks" in the
+ <Directory /> section of the default access.conf-dist
+ (and -win even though it doesn't matter there). This has better
+ performance, and more intuitive semantics. [Dean Gaudet]
+
+ *) PORT: Updated support for UTS 2.1.2.
+ [Dave Dykstra <dwd@bell-labs.com>] PR#2320
+
+ *) Fix symbol export list (src/support/httpd.exp) after recent
+ API changes in the child spawning area.
+ [Jens-Uwe Mager <jum@helios.de>]
+
+ *) Workaround for configure script and old `test' commands which do not
+ support the -x flag (for instance under platforms like Ultrix). This is
+ solved by another helper script findprg.sh which searches for Perl and
+ Awk like PrintPath but _via different names_.
+ [Ralf S. Engelschall]
+
+ *) Remove the system() call from htpasswd.c, which eliminates a system
+ dependancy. ["M.D.Parker" <mdpc@netcom.com>] PR#2332
+
+ *) PORT: Fix compilation failures on NEXTSTEP.
+ [Rex Dieter <rdieter@math.unl.edu>] PR#2293, 2316
+
+ *) PORT: F_NDELAY is a typo, should have been FNDELAY. There's also
+ O_NDELAY on various systems. [Dave Dykstra <dwd@bell-labs.com>] PR#2313
+
+ *) PORT: helpers/GuessOS updates for various versions for NCR SVR4.
+ [juerg schreiner <j.schreiner@zh.ch>,
+ Bill Houle <Bill.Houle@SanDiegoCA.NCR.COM>] PR#2310
+
+ *) Fix recently introduced Win32 child spawning code in mod_rewrite.c which
+ was broken because of invalid ap_pstrcat() -> strcat() transformation.
+ [Ralf S. Engelschall]
+
+ *) Proxy Cache Fixes: account for directory sizes, fork off garbage collection
+ to continue in background, use predefined types (off_t, size_t, time_t),
+ log the current cache usage percentage at LogLevel debug
+ [Martin Kraemer, based on discussion between Dean Gaudet & Dirk vanGulik]
+
+Changes with Apache 1.3.0
+
+ *) Using a type map file as a custom error document was not possible.
+ [Lars Eilebrecht] PR#1031
+
+ *) Avoid problems with braindead Awks by additionally searching for gawk
+ and nawk in APACI's configure script.
+ [Dave Dykstra <dwd@bell-labs.com>, Ralf S. Engelschall] PR#2319
+
+ *) Rename md5.h to ap_md5.h to avoid conflicts with native MD5 on
+ some systems. [Randy Terbush]
+
+ *) Change usage of perror()+fprintf(stderr,...) in mod_rewrite to
+ more proper ap_log_error() variants.
+ [Ralf S. Engelschall]
+
+ *) Make sure the argument for the --add-module option to APACI's configure
+ script is of type [path/to/]mod_xxx.c because all calculations inside
+ configure and src/Configure depend on this.
+ [Ralf S. Engelschall] PR#2307
+
+ *) Changes usage of perror/fprintf to stderr to more proper ap_log_error
+ in mod_mime, mod_log_referer, mod_log_agent, and mod_log_config.
+ [Brian Behlendorf]
+
+ *) Various OS/2 cleanups ["Brian Havard" <brianh@kheldar.apana.org.au>]
+
+ *) PORT: QNX needed a #include <sys/mman.h>; and now it uses flock
+ serialized accept to handle multiple sockets.
+ [Rob Saccoccio <robs@InfiniteTechnology.com>] PR#2295, 2296
+
+ *) Have NT properly set the directory for CGI scripts
+ (& other spawned children)
+ [W G Stoddard <wgstodda@us.ibm.com>]
+
+ *) Propagate environment to CGI scripts correctly in Win32.
+ [W G Stoddard <wgstodda@us.ibm.com>] PR#2294
+
+ *) Some symbol renaming:
+ ap_spawn_child_err became ap_spawn_child
+ ap_spawn_child_err_buff became ap_bspawn_child
+ spawn_child was obsoleted and moved to compat.h
+ [Brian Behlendorf]
+
+ *) Upgrade the child spawning code in mod_rewrite for the RewriteMap
+ programs: ap_spawn_child_err() is used and the Win32 case now uses
+ CreateProcess() instead of a low-level execl() (which caused problems in
+ the past under Win32).
+ [Ralf S. Engelschall]
+
+ *) A few cosmetics and trivial enhancements to APXS to make the
+ generated Makefile more user friendly. [Ralf S. Engelschall]
+
+ *) Proxy Fix: The proxy special failure routine ap_proxyerror()
+ was updated to use the normal apache error processing, thereby allowing
+ proxy errors to be treated by ErrorDocument's as well. For this
+ purpose, a new module-to-core communication variable "error-notes"
+ was introduced; the proxy (and possibly other modules) communicates
+ its error text using this variable. Its content is copied to a new
+ cgi-env-var REDIRECT_ERROR_NOTES for use by ErrorDocuments.
+ The old proxy special error routine ap_proxy_log_uerror()
+ was replaced by regular ap_log_error() calls, many messages were made
+ more informative.
+ [Martin Kraemer] PR#494, 1259
+
+ *) SECURITY: A possible buffer overflow in the ftp proxy was fixed.
+ [Martin Kraemer]
+
+ *) Transform the configure message "You need root privileges for suEXEC"
+ from a fatal error into a (more friendly) warning because the building
+ ("make") of Apache we can allow, of course. Root privileges are needed
+ only for the installation step ("make install"). So make sure the
+ user is aware of this fact but let him proceed as long as he can.
+ [Ralf S. Engelschall] PR#2288
+
+ *) Renamed three more functions to common ap_ prefix which we missed at the
+ Big Symbol Renaming because they're #defines and not real C functions:
+ is_default_port(), default_port(), http_method().
+ [Ralf S. Engelschall]
+
+ *) A zero-length name after a $ in an SSI document should cause
+ just the $ to be in the expansion. This was broken during the
+ security fixes in 1.2.5. [Dean Gaudet] PR#1921, 2249
+
+ *) Call ap_destroy_sub_req() in ap_add_cgi_vars() to reclaim some
+ memory. [Rob Saccoccio <robs@InfiniteTechnology.com>] PR#2252
+
+ *) Fix src/support/httpd.exp (DSO export file which is currently only
+ used under AIX) because of recent changes to function names.
+ [Ralf S. Engelschall]
+
+Changes with Apache 1.3b7
+
+ *) Make sure a MIME-type can be forced via a RewriteRule even when no
+ substitution takes place, for instance via the following rule:
+ ``RewriteRule ^myscript$ - [T=application/x-httpd-cgi]'' This was often
+ requested by users in the past to force a single script without a .cgi
+ extension and outside any cgi-bin dirs to be executed as a CGI program.
+ [Ralf S. Engelschall] PR#2254
+
+ *) A fix for protocol issues surrounding 400, 408, and
+ 414 responses. [Ed Korthof]
+
+ *) Ignore MaxRequestsPerChild on WIN32. [Brian Behlendorf]
+
+ *) Fix discrepancy in proxy_ftp.c which was causing failures when
+ trying to connect to certain ftpd's, such as anonftpd.
+ [Rick Ohnemus <rick@ecompcon.com>]
+
+ *) Make mod_rewrite use ap_open_piped_log() for RewriteLog directive's
+ logfile instead of fiddling around itself with child spawning stuff.
+ [Ralf S. Engelschall]
+
+ *) Made RefererIgnore case-insensitive.
+
+ *) Mod_log_agent, mod_log_referer now use ap_open_piped_log for piped logs.
+ [Brian Behlendorf]
+
+ *) Replace use of spawn_child with ap_spawn_child_err_buff, to make everything
+ "safe" under Win32. In: mod_include.c, mod_mime_magic.c
+ [Brian Behlendorf]
+
+ *) Improve RFC1413 support. [Bob Beck <beck@bofh.ucs.ualberta.ca>]
+
+ *) Fix support script `dbmmanage': It was unable to handle some sort
+ of passwords, especially passwords with "0" chars.
+ [Ralf S. Engelschall] PR#2242
+
+ *) WIN32: Clicking on "Last Modified" in a fancy index caused a crash. Fixed.
+ [Ben Laurie] PR#2238
+
+ *) WIN32: CGIs could cause a hang (because of a deadlock in the standard C
+ library), so CGI handling has been changed to use Win32 native handles
+ instead of C file descriptors.
+ [Ben Laurie and Bill Stoddard <wgstodda@us.ibm.com>] PR#1129, 1607
+
+ *) The proxy cache would store an incorrect content-length in the cached
+ file copy after a cache update. That resulted in repeated fetching
+ of the original copy instead of using the cached copy.
+ [Ernst Kloppenburg <kloppen@isr.uni-stuttgart.de>] PR#2094
+
+ *) The Makefiles assumed that DSO files are build via $(LD). This
+ is broken for two reasons: First we never defined at least LD=ld
+ somewhere to make sure this works (it was silently assumed that most Make
+ provide a built-in LD definition - ARGL!) and second using the generic LD
+ variable is not the truth. Instead a special variable named LD_SHLIB is
+ reasonable because although "ld" is usually the default, the command for
+ building DSO files can be "libtool" or even "cc" on some systems.
+ [Ralf S. Engelschall]
+
+ *) Replace the AddVersionPlatform directive with ServerTokens which
+ provides for more control over the format of the Server:
+ header line. SERVER_SUBVERSION is no longer supported;
+ all module should use the ap_add_version_component()
+ API function instead. [Jim Jagielski]
+
+ *) Support for the NCR MP/RAS 3.0
+ [John Withers <withers@semi.kcsc.mwr.irs.gov>]
+
+ *) The LDFLAGS_SHLIB_EXPORT variable of src/Configuration[.tmpl] was
+ not retrieved in src/Configure and thus was not useable.
+ [Ralf S. Engelschall]
+
+ *) Various Makefile consistency cleanups:
+ - make OSDIR also automatically be relative to src/ like INCDIR
+ - SUBDIRS is now generated in src/Makefile only and not in
+ Makefile.config because it is a local define for this location.
+ - remove BROKEN_BPRINTF_FLAGS because is it no longer used inside
+ any Makefile but make sure that at least the "-K inline" is kept in
+ CFLAGS for SCO 5.
+ - update the "depend" targets in Makefile.tmpl files to use $(OSDIR), too.
+ - updated the dependencies theirself
+ - removed not existing SHLIB variable from "clean" targets
+ - replaced SHLIB_OBJS/SHLIBS_OBJ consistently with OBJS_PIC because OBJS
+ already exists and OBJS_PIC are also just plain objects and have not
+ directly to do with "shared" things. The only difference is that they
+ contain PIC. So OBJS_PIC is the more canonical name.
+ - Updated the Makefile-dependency lines for OBJS_PIC
+ - Removed the Makefile-dependency line in Configure to avoid double
+ definitions
+ - replaced ugly xx-so.o/xx.so-o hack with a clean and consistent usage
+ of xxx.lo as GNU libtool does with its PIC objects
+ - reduce local complexity in modules Makefile.tmpl by moving the last
+ existing target "depend" to the generation section in Configure, too.
+ - removed the historical $(SPACER) which was used in the past together
+ with BROKEN_BPRINTF_FLAGS to avoid zig-zags in the build process. This
+ is no longer needed.
+ - force the build and run of the gen_xxx programs under main/ as the
+ first step before building the objects because it looks cleaner
+ [Ralf S. Engelschall]
+
+ *) WIN32: Make Win32 work again after the /dev/null DoS fix.
+ [Ben Laurie]
+
+ *) WIN32: Check for buffer overflows in ap_os_canonical_filename.
+ [Ben Laurie]
+
+ *) WIN32: Don't force ISAPI headers to finish with \n.
+ [Jim Patterson <Jim.Patterson@Cognos.COM>, Ben Laurie] PR#2060
+
+ *) When opening "configuration" files (like httpd.conf, htaccess
+ and htpasswd), Apache will not allow them to be non-/dev/null
+ device files. This closes a DoS hole. At the same time,
+ we use ap_pfopen to open these files to handle timeouts.
+ [Jim Jagielski, Martin Kraemer]
+
+ *) Apache will now log the reason its httpd children exit if they exit
+ due to an unexpected signal. (It requires a new porting define,
+ SYS_SIGLIST, which if defined should point to a list of text
+ descriptions of the signals available. See PORTING.) [Dean Gaudet]
+
+ *) WIN32: chdir() doesn't make sense in a multithreaded environment
+ like WIN32. Before, Win32 CGI's could have had sporadic failures
+ if a chdir call from one thread was made between another chdir call
+ and a spawn in another thread. So, for now don't chdir for CGI scripts
+ in WIN32. The current CGI "spec" is unclear as to whether it's
+ necessary. Long-term fix is to either serialize the chdir/spawn combo
+ or use WIN32 native calls to spawn a process. This temp fix was
+ necessary to remove this as a showstopper for 1.3's release.
+ [Brian Behlendorf]
+
+ *) Cleanup the suEXEC support in APACI and make it more safe:
+ 1. Add big fat hint in INSTALL about risks and to read the
+ htdocs/manual/suexec.html document before using the suexec-related
+ configure options.
+ 2. Make sure the user has at least provided one --suexec-xxxx option
+ (specifies suEXEC parameters) in addition to --enable-suexec option.
+ If only --enable-suexec is given APACI stops with a hint to INSTALL
+ and htdocs/manual/suexec.html documents.
+ 3. Provide two additional --suexec-xxxx options to make the suEXEC
+ configuration complete (especially for package maintainers who else
+ had to patch the source tree) by providing ways to configure minimal
+ UID/GID and safe PATH, too.
+ [Ralf S. Engelschall]
+
+ *) Cleanup of the `configure --shadow' process:
+ - make sure the configure script creates its temporary files in the
+ shadow tree to avoid conflicts with parallel configure runs
+ - removed unnecessary option "-r" from "rm" call for Makefiles
+ - make sure the configure scripts creates the shadow-wrapper Makefile
+ only when no shadow trees already exists
+ - make sure "make distclean" removes the shadow-wrapper Makefile but only
+ when no more shadow trees exists
+ - overhauled mkshadow.sh script: now its more IFS-safe and approx. twice
+ as fast (in the past it needed 70sec, now it runs just 38sec)
+ - make sure CVS does not complain about the created files
+ Makefille.<gnutriple> and directories src.<gnutriple>
+ [Ralf S. Engelschall]
+
+ *) Added the ap_add_version_component() API routine and the
+ AddVersionPlatform core directive. The first allows modules to
+ declare themselves in the Server response header field value,
+ augmenting the SERVER_SUBVERSION define in the Configuration file
+ with run-time settings (more useful in a loadable-module environment).
+ AddVersionPlatform inserts a comment such as "(UNIX)" or "(Win32)"
+ into the server version string. [Ken Coar] PR#2056
+
+ *) Minor stability tweaks to avoid core dumps in ap_snprintf.
+ [Martin Kraemer]
+
+ *) Emit the "Accept-Range" header for the default handler.
+ [Brian Behlendorf] PR#1464
+
+ *) Add a note to httpd.conf-dist that apache will on some systems fail
+ to start when the Group # is set to a negative or large positive value.
+ [Martin Kraemer]
+
+ *) Make sure the module execution order is correct even when some modules
+ are loaded under runtime (`LoadModule') via the DSO mechanism:
+ 1. The list of loaded modules is now a dynamically allocated one
+ and not the original statically list from modules.c
+ 2. The loaded modules are now correctly setup by LoadModule for
+ later use by the AddModule command.
+ 3. When the DSO mechanism for modules is used APACI's `install'
+ target now enables all created `LoadModule' lines per default because
+ this is both already expected by the user _and_ needed to avoid
+ confusion with the next point and reduces the Makefile.tmpl complexity
+ 4. When the DSO mechanism for modules is used, APACI's `install'
+ target now additionally makes sure the module list is reconstructed
+ via a complete `ClearModuleList+AddModule...' entry.
+ 5. The support tool `apxs' now also makes sure an AddModule command
+ is added in addition to the LoadModule command.
+ 6. The modules.c generation was extended to now contain two
+ comments to make sure no one is confused by the confusing terminology
+ of loading/linking (we use load=link+load & link=activate instead of
+ the obvious load=activate & link=link :-( )
+ This way now there is no longer a difference under execution time between
+ statically and dynamically linked modules.
+ [Ralf S. Engelschall]
+
+ *) Fix the generated mod_xxx.c from "apxs -g -f xxx" after the
+ Big Symbol Renaming. [Ralf S. Engelschall]
+
+ *) Add a comment to mod_example.c showing the format of a FLAG command
+ handler. [Ken Coar]
+
+ *) Standardized the time format in mod_status to match that of other
+ places in the code (e.g. DATE_GMT). PR#1551
+
+ *) Fix handling of %Z in timefmt strings for those platforms with no time
+ zone information in their tm struct. [Paul Eggert <eggert@twinsun.com>]
+ PR#754
+
+ *) Makes mod_rewrite, mod_log_config, mod_status and the ServerSignature
+ feature compatible with 'UseCanonicalName off' by changing
+ r->server->server_hostname to ap_get_server_name(). And I changed some
+ functions which use r->server->port to use ap_get_server_port() instead,
+ because if there's no Port directive in the config r->server->port is 0.
+ [Lars Eilebrecht]
+
+ *) get/set_module_config are trivial enough to be better off inline. Worth
+ 1.5% performance boost. [Dean Gaudet]
+
+ *) Fix off-by-one error in ap_proxy_date_canon() in proxy_util.c
+ when ensuring 'x' is at least 30-chars big. [Jim Jagielski,
+ Brian Behlendorf]
+
+ *) [BS2000 security] BS2000 needs an extra authentication to initialize
+ the task environment to the unprivileged User id. Otherwise CGI scripts
+ would have a way to gain super user access. [Martin Kraemer]
+
+ *) Fix debug log messages for BS2000/OSD: instead of logging the whole
+ absolute path, only log base name of logging source as is done
+ in unix. [Martin Kraemer]
+
+ *) Ronald Tschalaer's Accept-Encoding patch - preserve the "x-" in
+ the encoding type from the Accept-Encoding header (if it's there)
+ and use it in the response, as that's probably what it'll be expecting.
+ [Ronald.Tschalaer@psi.ch]
+
+ *) Fix to mod_alias: translate_alias_redir is dealing with
+ a URI, not a filename, so the check for drive letters for win32
+ and emx is not necessary. [Dean Gaudet]
+
+ *) WIN32: Allow .cmd as an executable extension.
+ [Kari Likovuori <Kari.Likovuori@mol.fi>] PR#2146
+
+ *) Make Apache header files, and some variables, C++ friendly.
+ [Michael Anderson's <mka@redes.int.com.mx>]
+
+ *) Child processes can now "signal" (by exiting with a status
+ of APEXIT_CHILDFATAL) the parent process to abort and
+ shutdown the server if the error in the child process was
+ fatal enough. [Jim Jagielski]
+
+ *) mod_autoindex's find_itme() was sensitive to MIME type case.
+ [Jim Jagielski] PR#2112
+
+ *) Make sure the referer_log and agent_log entries in the default httpd.conf
+ file are also adjusted for the actual relative installation paths.
+ [Ralf S. Engelschall] PR#2175
+
+ *) WIN32: Extensive overhaul of the way UNCs are handled. [Ben Laurie]
+
+ *) WIN32: Make roots of filesystems (e.g. c:/) work. [Ben Laurie]
+ PR#1558
+
+ *) PORT: Various porting changes to support AIX 3.2, 4.1.5, 4.2 and 4.3.
+ Additionally the checks for finding the vendor DSO library were moved
+ from mod_so.c to Configure because first it needs $PLAT etc. and second
+ mod_so already uses an abstraction layer and does not fiddle with the
+ vendor functions itself.
+ [Jens-Uwe Mager, Ralf S. Engelschall]
+
+ *) PORT: Some optimization defines for NetBSD
+ [Jaromir Dolecek <dolecek@ics.muni.cz>] PR#2165
+
+ *) PORT: Dynamic Shared Object (DSO) support for NetBSD.
+ [Jaromir Dolecek <dolecek@ics.muni.cz>, Ralf S. Engelschall] PR#2158
+
+ *) Add Dynamic Shared Object (DSO) support for AIX (at least 4.2 but older
+ AIX variants should work fine, too. Even AIX 3.x should work). This is
+ accomplished by using the free DSO emulation code from Jens-Uwe Mager
+ which we put into a os/unix/os-dso-aix.c file.
+ [Ralf S. Engelschall]
+
+ *) PORT: Fix compiler warnings under AIX >= 4.2 where the manual pages imply
+ that we should use NET_SIZE_T == int but the include files force size_t.
+ [Ralf S. Engelschall]
+
+ *) Fix two bugs in select() handling in http_main.c.
+ [Roy Fielding]
+
+ *) Suppress "error(0)" messages for ap_log_error() when the APLOG_NOERRNO
+ is unset (as it is in situations like timeouts) where it is unclear
+ whether errno is set or not. [Martin Kraemer]
+
+ *) Just having APACI's localstatedir is too general and not enough for most
+ of the systems. 1.3b6 again required manual APACI patches by package
+ maintainers from RedHat and FreeBSD because for their filesystem layout a
+ little bit more flexibility in configuring the paths is needed. Hence we
+ provide three additional configure options (--runtimedir, --logfiledir,
+ --proxycachedir) which now can be used for more granular adjustments if
+ --localstatedir is not enough to fit the particular needs. As a nice
+ side-effect this reduces some subdir fiddling in configure+Makefile.tmpl.
+ [Ralf S. Engelschall]
+
+ *) Make the install root for "make install" in APACI's Makefile overrideable
+ by package authors. This way we are even more friendly to package
+ maintainers (especially Debian and RedHat) who build for the real prefix
+ via "configure --prefix=/<real>" but use a different local prefix via
+ "make root=/tmp/apache install" for rolling the package without bristling
+ the target location on their system.
+ [Ralf S. Engelschall]
+
+ *) Workaround sed limitations in APACI's configure script by now
+ substituting in chunks of 50 commands (because for instance HPUX's vendor
+ sed has a limit of max. 98 commands)
+ [Ralf S. Engelschall] PR#2136
+
+ *) Adding SOCKS5 support and fixing existing SOCKS4 support.
+ [Ralf S. Engelschall] PR#2140
+
+ *) Manually fix some symbols which were not renamed to prefix ap_ in the BIG
+ RENAMING process because they are defined as pre-processor macros instead
+ of real functions: bputc, bgetc, piped_log_write_fd, piped_log_read_fd
+ [Ralf S. Engelschall]
+
+ *) Workaround braindead AWK's when generating ap_config.h: The split() and
+ substr() functions cannot be nested under vendor AWK from Solaris 2.6.
+ [Ralf S. Engelschall] PR#2139
+
+ *) Various bugfixes and cleanups for the APACI configure script:
+ o fix IFS handling for _nested_ situation
+ o fix Perl interpreter search: take first one found instead of last one
+ o fix DSO consistency check
+ o print error messages to stderr instead of stdout
+ o add install-quiet for --shadow situation to Makefile stub
+ o reduce complexity by avoiding sed-hacks for rule and module list loops
+ [Ralf S. Engelschall]
+
+ *) Fix DEBUG_CGI situation in mod_cgi.c [David MacKenzie] PR#2114
+
+ *) Make sure the input field separator (IFS) shell variable is explicitly
+ initialized correctly before _every_ `for' loop and also restored after
+ the loops. [Ralf S. Engelschall]
+
+ *) Make sure that "make install" doesn't overwrite the `mime.types' and
+ `magic' files from an existing Apache installation. Because people often
+ customize these for own MIME and content types.
+ [Ralf S. Engelschall]
+
+ *) PORT: Dynamic Shared Object (DSO) support for OpenBSD 2.x
+ [Peter Galbavy, Ralf S. Engelschall] PR#2109
+
+ *) Fix the path to the ScoreBoardFile in the install-config target, too.
+ [Ralf S. Engelschall] PR#2105
+
+ *) Let "configure" clear out the users parameters (provided as shell
+ variables) to avoid side-effects in "src/Configure" when the user
+ exported them (which is not needed, but some users do it).
+ [Ralf S. Engelschall] PR#2101
+
+ *) Provide backward compatibility from some old src/Configuration.tmpl
+ parameter names to the canonical Autoconf-style shell variable names. For
+ instance CFLAGS vs. EXTRA_CFLAGS. The EXTRA_xxx variants are accepted now
+ but a hint message is displayed. [Ralf S. Engelschall]
+
+ *) Make sure that "make install" doesn't overwrite the DocumentRoot and
+ CGI scripts from an existing Apache installation.
+ [Ralf S. Engelschall, Jim Jagielski] PR#2084
+
+ *) Make `configure --compat' more "compatible" by first
+ let the libexecdir default to EPREFIX/libexec instead of EPREFIX/bin and
+ second by making sure the "avoid-bristling-suffix" /apache is not
+ appended to sysconfdir, datadir, localstatedir and includedir when
+ --compat is used. [Ralf S. Engelschall, Lars Eilebrecht]
+
+ *) NeXT required strdup() in support/logresolve.c
+ [Francisco Tomei <fatomei@sandburg.unm.edu>] PR#2082
+
+ *) AIX required sys/select.h in support/ab.c
+ [Jens Schleusener <Jens.Schleusener@dlr.de>] PR#2081
+
+ *) Fix the path to the MimeMagicFile in the install-config target, too.
+ [Ralf S. Engelschall] PR#2089
+
+ *) PORT: Added HP-UX 11 patches [Jeff Earickson <jaearick@colby.edu>]
+
+ *) If you start apache with the -S command line option it will dump
+ out the parsed vhost settings. This is useful for folks trying
+ to figure out what is wrong with their vhost configuration.
+ (Other dumps may be added in the future.) [Dean Gaudet]
+
+ *) Add %pA, %pI, and %pp codes to ap_vformatter (and hence ap_bprintf,
+ ap_snprintf, and ap_psprintf). See include/ap.h for docs.
+ [Dean Gaudet]
+
+ *) Because /usr/local/apache is the default prefix the ``configure
+ --compat'' option no longer has to set prefix, again. This way the
+ --compat option honors a leading --prefix option. [Lars Eilebrecht]
+
+ *) PORT: Cast the first argument of dlopen() in ap_os_dso_load()
+ to `char *' under OSF1 and FreeBSD 2.x where it is defined this way
+ to avoid "discard const" warnings. [Ralf S. Engelschall]
+
+ *) If a specific handler is set for a file yet the request still
+ ends up being handled by the default handler, log an error
+ message before handling it. This catches things such as trying
+ to use SSIs without mod_include enabled. [Marc Slemko]
+
+ *) Fix error logging for the startup case where ap_log_error() still uses
+ stderr as the target. Now the default log level is honored here, too.
+ [Ralf S. Engelschall]
+
+ *) PORT: Make sure some AWK's don't fail in src/Configure with "string too
+ long" errors when generating the MODULES entry for src/Makefile
+ [Ben Hyde, Ralf S. Engelschall]
+
+ *) Make sure src/Configure doesn't complain about the old directory
+ /usr/local/etc/httpd/ when APACI is used. [Lars Eilebrecht]
+
+Changes with Apache 1.3b6
+
+ *) PORT: Clean up warnings on Ultrix and HPUX. [Ben Hyde]
+
+ *) Adding DSO support for the HP/UX platform by emulating the dlopen-style
+ interface via the similar but proprietary HP/UX shl_xxx-style system
+ calls. [Ralf S. Engelschall]
+
+ *) PORT: Updated UnixWare 2.0.x and 2.1.x entries for DSO support and made
+ APACI Makefile.tmpl "install" target more robust for sensible UnixWare
+ Make. [Ralf S. Engelschall]
+
+ *) ++++ THE BIG SYMBOL RENAMING ++++
+ To avoid symbol clashes with third-party code compiled into the server,
+ we globally applied the prefix "ap_" to the following classes of
+ functions:
+ - Apache provided general functions (e.g., ap_cpystrn)
+ - Public API functions (e.g., palloc, bgets)
+ - Private functions which we can't make static (because of
+ cross-object usage) but should be (e.g., new_connection)
+ For backward source compatibility a new header file named compat.h was
+ created which provides defines for the old symbol names and can be used
+ by third-party module authors.
+ [The Apache Group]
+
+ *) Added dynamic shared object (DSO) support for SVR4-derivates: The
+ problem under SVR4 is that there is no command flag to force the linker
+ to export the global symbols of the httpd executable therewith they are
+ available to the DSO's. Instead of problematic hacks like creating a
+ dummy.so file (containing dummy references to all global symbols) the
+ httpd binary is linked against, we use a clean trick stolen from Perl 5:
+ Placing the Apache core code itself into a DSO library named libhttpd.so.
+ This way the global symbols _HAVE_ to be exported and thus are available
+ to any manually loaded DSO's under runtime. To reduce the impact to the
+ user to null we go even further and create a stub httpd executable which
+ automatically keeps track of the DSO library loading itself and thus
+ hides the complete mechanism from the user. Although the generation of
+ this DSO library is automatically triggered for platforms which
+ essentially need it (mostly all SVR4-derivates) it can be also enabled
+ manually via the Rule SHARED_CORE. This can be interesting in the future
+ where we perhaps exploit this libhttpd.so mechanism for providing nifty
+ features like graceful upgrades, or whatever.
+ [Ralf S. Engelschall, Martin Kraemer]
+
+ *) Build the libraries before building the rest of the tools. [Ben Hyde]
+
+ *) Add "distclean" target to src/-Makefiles to provide "make distclean" also
+ inside the src subtree (i.e. for non-APACI users). Following GNU Makefile
+ conventions while "clean" removes only stuff created by "all" targets,
+ "distclean" additionally removes the stuff from the configuration
+ process. This way "make distclean" (hence the name) provides a fresh
+ source tree as it was for distribution.
+ [Ralf S. Engelschall]
+
+ *) Allow top-level (APACI) Makefile to break on build errors
+ the same way the src/ subtree Makefiles breaks on them by replacing the
+ initial APACI sed-subdir-display-kludge with a more clean
+ variable-passing-solution: variable SDP can optionally hold the subdir
+ prefix which is consistently used for displaying the subdir movement.
+ This way even the top-level Makefile can stop correctly on errors as the
+ user expects. [Ralf S. Engelschall]
+
+ *) Fixed ordering of argument checks for RewriteBase directive.
+ [Todd Eigenschink <eigenstr@mixi.net>] PR#2045
+
+ *) Change Win32 IS_MODULE to SHARED_MODULE to match Unix' method of
+ indicating that a module is being compiled for dynamic loading. Also
+ remove #define IS_MODULE from modules and add SHARED_MODULE define
+ to the mak/dsp files. [Alexei Kosut]
+
+ *) Reduce logging level of "normal" warning messages to APLOG_INFO,
+ since we are now logging APLOG_WARNING by default. [Roy Fielding]
+
+ *) PORT: OS/2 tweak to deal with multiple .exe targets. [Brian Havard]
+
+ *) Add documentation file and src/Configuration.tmpl entry for the
+ experimental mod_mmap_static module. Because although it is and marked as
+ an experimental one it is distributed and thus should be documented and
+ prepared for configuration the same way as all others modules.
+ [Ralf S. Engelschall]
+
+ *) Add query (-q) option to apxs support tool to be able to manually query
+ specific settings from apxs. This is needed for instance when you
+ manually want to access Apache's header files and you need to assemble
+ the -I option. Now you can do -I`apxs -q INCLUDEDIR`.
+ [Ralf S. Engelschall]
+
+ *) Now src/Configure uses a fallback strategy for the shared object support
+ on platforms where no explicit information is available: If a Perl
+ installation exists we ask it about its shared object support and if it's
+ the dlopen-style one we shamelessly guess the compiler and linker flags
+ for creating shared objects from Perls knowledge. Of course, the user is
+ warning about what we are doing and informed that he should send us
+ the guessed flags when they work. [Ralf S. Engelschall]
+
+ *) Provide APACI --without-support option to be able to disable the build
+ and installation of the support tools from the src/support/ area.
+ Although its useful to have these installed per default we should provide
+ a way to compile and install without them for backward-compatibility.
+ [Ralf S. Engelschall]
+
+ *) Add of the new APache eXtenSion (apxs) support tool for building and
+ installing modules into an _already installed_ Apache package through the
+ dynamic shared object (DSO) mechanism [mod_so.c]. The trick here is that
+ this approach actually doesn't need the Apache source tree. The
+ (APACI-installed) server package is enough, because this now includes the
+ Apache C header files (PREFIX/include) and the new APXS tool
+ (SBINDIR/apxs). The intend is to provide a handy tool for third-party
+ module authors to build their Apache modules _OUTSIDE_ the Apache source
+ tree while avoiding them to fiddle around with the totally platform
+ dependend way of compiling DSO files. The tool supports all ranges of
+ modules, from trivial ones (single mod_foo.c) to complex ones (like PHP3
+ which has a mod_php3.c plus a pre-built libmodphp3-so.a) and even can
+ on-the-fly generate a minimalistic Makefile and sample module for the
+ first step to provide both a quick success event and to demonstrate the
+ APXS mechanism to module authors. [Ralf S. Engelschall]
+
+ *) Fix core dumps in use of CONNECT in proxy.
+ [Rainer.Scherg@rexroth.de] PR#1326, #1573, #1942
+
+ *) Modify the log directives in httpd.conf-dist files to use CustomLog
+ so that users have examples of how CustomLog can be used.
+ [Lars Eilebrecht]
+
+ *) Add the new Apache Autoconf-style Interface (APACI) for the top-level of
+ the Apache distribution tree. Until Apache 1.3 there was no real
+ out-of-the-box batch-capable build and installation procedure for the
+ complete Apache package. This is now provided by a top-level "configure"
+ script and a corresponding top-level "Makefile.tmpl" file. The goal is
+ to provide a GNU Autoconf-style frontend which is capable to both drive
+ the old src/Configure stuff in batch and additionally installs the
+ package with a GNU-conforming directory layout. Any options from the old
+ configuration scheme are available plus a lot of new options for flexibly
+ customizing Apache. [Ralf S. Engelschall]
+
+ *) The floating point ap_snprintf code wasn't threadsafe.
+ Had to remove the HAVE_CVT macro in order to do threadsafe
+ calling of the ?cvt() floating point routines. [Dean Gaudet]
+
+ *) PORT: Add the SCO_SV port. [Jim Jagielski] PR#1962
+
+ *) PORT: IRIX needs the -n32 flag iff using the 'cc' compiler
+ [Jim Jagielski] PR#1901
+
+ *) BUG: Configure was using TCC and CC inconsistently. Make sure
+ Configure knows which CC we are using. [Jim Jagielski]
+
+ *) "Options +Includes" wasn't correctly merged if "+IncludesNoExec"
+ was defined in a parent directory. [Lars Eilebrecht]
+
+ *) API: ap_snprintf() code mutated into ap_vformatter(), which is
+ a generic printf-style routine that can call arbitrary output
+ routines. Use this to replace http_bprintf.c. Add new routines
+ psprintf(), pvsprintf() which allocate the exact amount of memory
+ required for a string from a pool. Use psprintf() to clean up
+ various bits of code which used ap_snprintf()/pstrdup().
+ [Dean Gaudet]
+
+ *) PORT: HAVE_SNPRINTF doesn't do anything any longer. This is because
+ ap_snprintf() has different semantics and formatting codes than
+ snprintf(). [Dean Gaudet]
+
+ *) SIGXCPU and SIGXFSZ are now reset to SIG_DFL at boot-time. This
+ is necessary on at least Solaris where the /etc/rc?.d scripts
+ are run with these signals ignored, and "SIG_IGN" settings are
+ maintained across exec().
+ [Rein Tollevik <reint@sys.sol.no>] PR#2009
+
+ *) Fix the check for symbolic links in ``RewriteCond ... -l'': stat() was
+ used instead of lstat() and thus this flag didn't work as expected.
+ [Rein Tollevik <reint@sys.sol.no>] PR#2010
+
+ *) Fix the proxy pass-through feature of mod_rewrite for the case of
+ existing QUERY_STRING now that mod_proxy was recently changed because of
+ the new URL parsing stuff. [Ralf S. Engelschall]
+
+ *) A few changes to scoreboard definitions which helps gcc generate
+ better code. [Dean Gaudet]
+
+ *) ANSI C doesn't guarantee that "int foo : 2" in a structure will
+ be a signed bitfield. So mark a few bitfields as signed to
+ ensure correct code. [Dean Gaudet]
+
+ *) The default for HostnameLookups was changed to Off, but there
+ was a problem and it wasn't taking effect. [Dean Gaudet]
+
+ *) PORT: Clean up undefined signals on some platforms (SCO, BeOS).
+ [Dean Gaudet]
+
+ *) After a SIGHUP the listening sockets in the parent weren't
+ properly marked for closure on fork().
+ [Jürgen Keil <jk@tools.de>] PR#2000
+
+ *) Allow %2F in two situations: 1) it is in the query part of the URI,
+ therefore not exposed to %2F -> '/' translations and 2) the request
+ is a proxy request, so we're not dealing with a local resource anyway.
+ Without this, the proxy would fail to work for any URL's with
+ %2f in them (occurs quite often in
+ http://.../cgi-bin/...?http%3A%2F%2F... references) [Martin Kraemer]
+
+ *) Protect against FD_SETSIZE mismatches. [Dean Gaudet]
+
+ *) Make the shared object compilation command more portable by avoiding
+ the direct combination of `-c' & `-o' which is not honored by some
+ compilers like UnixWare's cc. [Ralf S. Engelschall]
+
+ *) WIN32: the proxy was creating filenames missing the last four
+ characters. While this normally doesn't stop anything from
+ working, it can result in extra collisions.
+ [Tim Costello <tjcostel@socs.uts.edu.au>] PR#1890
+
+ *) Now mod_proxy uses the response string (in addition to the response status
+ code) from the already used FTP SIZE command to setup the Content-Length
+ header if available. [Ralf S. Engelschall] PR#1183
+
+ *) Reanimated the (still undocumented) proxy receive buffer size directive:
+ Renamed from ReceiveBufferSize to ProxyReceiveBufferSize because the old
+ name was really too generic, added documentation for this directive to
+ the mod_proxy.html and corrected the hyperlink to it in the
+ new_features_1.3.html document. [Ralf S. Engelschall] PR#1348
+
+ *) Fix a bug in the src/helpers/fp2rp script and make it a little bit
+ faster [Martin Kraemer]
+
+ *) Make Configure die when you give it an unknown command switch.
+ [Ben Hyde]
+
+ *) Add five new and fresh manpages for the support programs: dbmmanage.1,
+ suexec.8, htdigest.1, rotatelogs.8 and logresolve.8. Now all up-to-date
+ and per default compiled support programs have manual pages - just to
+ document our stuff a little bit more and to be able to do really
+ Unix-like installations ;-) [Ralf S. Engelschall]
+
+ *) Major cleanups to the Configure script to make it and its generated
+ Makefiles again readable and maintainable: add SRCDIR option, removed
+ INCLUDES_DEPTH[0-2] kludge, cleanup of TARGET option, cleanup of
+ generated sections, consequently added Makefile headers with inheritance
+ information, added subdir movement messages for easier following where
+ the build process currently stays (more verbose then standard Make, less
+ verbose than GNU make), same style to comments in the Configure script,
+ added Apache license header, fixed a few bugs, etc. [Ralf S. Engelschall]
+
+ *) Add the new ApacheBench program "ab" to src/support/: This is derived
+ from the ZeusBench benchmarking program and can be used to determine the
+ response performance of an Apache installation. This version is
+ officially licensed with Zeus Technology, Ltd. See the license agreement
+ statements in <199803171224.NAA24547@en1.engelschall.com> in apache-core.
+ [Ralf S. Engelschall]
+
+ *) API: Various core functions that are definately not part of the API
+ have been made static, and a few have been marked API_EXPORT. Still
+ more have been marked CORE_EXPORT and are not intended for general
+ use by modules. [Doug MacEachern, Dean Gaudet]
+
+ *) mod_proxy was not clearing the Proxy-Connection header from
+ requests; now it does. This did not violate any spec, however
+ causes poor interactions when you are talking to remote proxies.
+ [Marc Slemko] PR#1741
+
+ *) Various cleanups to the command line interface and manual pages.
+ [Ralf S. Engelschall]
+
+ *) cfg_getline() was not properly handling lines that did not end
+ with a line termination character. [Marc Slemko] PR#1869, 1909
+
+ *) Performance tweak to mod_log_config. [Dmitry Khrustalev]
+
+ *) Clean up some undocumented behavior of mod_setenvif related to
+ "merging" two SetEnvIf directives when they match the same header
+ and regex. Document that mod_setenvif will perform comparisons in
+ the order they appear in the config file. Optimize mod_setenvif by
+ doing more work at config time rather than at runtime.
+ [Dean Gaudet]
+
+ *) src/include/ap_config.h now wraps it's #define's with #ifndef/#endif's
+ to allow for modules to overrule them and to reduce redefinition
+ warnings [Jim Jagielski]
+
+ *) [PORT] For A/UX change the OS-#define for -DAUX to -DAUX3.
+ [Jim Jagielski]
+
+ *) Making the hard-coded cross-module function call mime_find_ct() (from
+ mod_proxy to mod_mime) obsolete by making sure the API hook for MIME type
+ checking is really called even for proxy requests except for URLs with
+ HTTP schemes (because there we can optimize by not running the type
+ checking hooks due to the fact that the proxy gets the MIME Content-type
+ from the remote host later). This change cleans up mod_mime by removing
+ the ugly export kludge, makes the one-liner file mod_mime.h obsolete, and
+ especially unbundles mod_proxy and mod_mime. This way they both now can
+ be compiled as shared objects and are no longer tied together.
+ [Ralf S. Engelschall]
+
+ *) util.c cleanup and speedup. [Dean Gaudet]
+
+ *) API: Clarification, pstrndup() will always copy n bytes of the source
+ and NUL terminate at the (n+1)st byte. [Dean Gaudet]
+
+ *) Mark module command_rec and handler_rec structures const so that they
+ end up in the read-only data section (and are friendlier to systems
+ that don't do optimistic memory allocation on fork()). [Dean Gaudet]
+
+ *) Add check to the "Port" directive to make sure the specified
+ port is in the appropriate range. [Ben Hyde]
+
+ *) Performance improvements to invoke_handler().
+ [Dmitry Khrustalev <dima@bog.msu.su>]
+
+ *) Added support for building shared objects even for library-style modules
+ (which are built from more than one object file). This now provides the
+ ability to build mod_proxy as a shared object module. Additionally
+ modules like mod_example are now also supported for shared object
+ building because the generated Makefiles now no longer assume there is at
+ least one statically linked module. [Ralf S. Engelschall]
+
+ *) API: Clarify usage of content_type, handler, content_encoding,
+ content_language and content_languages fields in request_rec. They
+ must always be lowercased; and the strings pointed to shouldn't
+ be modified (you must copy them to modify them). Fix a few bugs
+ related to this. [Dean Gaudet]
+
+ *) API: Clarification: except for RAW_ARGS, all command handlers can
+ treat the char * parameters as permanent, and modifiable. There
+ is no need to pstrdup() them. Clean up some needless pstrdup().
+ [Dean Gaudet]
+
+ *) Now mod_so keeps track of which module shared objects with which names
+ are loaded and thus avoids multiple loading and unloading and irritating
+ error_log messages. [Ralf S. Engelschall]
+
+ *) Prior to the existence of mod_setenv it was necessary to tweak the TZ
+ environment variable in the apache core. But that tweaking interferes
+ with mod_setenv. So don't tweak if the user has specified an explicit
+ TZ variable. [Jay Soffian <jay@cimedia.com>] PR#1888
+
+ *) rputs() did not calculate r->sent_bodyct properly.
+ [Siegmund Stirnweiss <siegst@kat.ina.de>] PR#1900
+
+ *) The CGI spec says that REMOTE_HOST should be set to the remote hosts's
+ name, or left unset if this value is unavailable. Apache was setting
+ it to the IP address when unavailable.
+ [Tony Finch <fanf@demon.net>] PR#1925
+
+ *) Various improvements to the configuration and build support for compiling
+ modules as shared objects. Especially Solaris 2.x, SunOS 4.1, IRIX and
+ OSF1 support with GCC and vendor compilers was added. This way shared
+ object support is now provided out-of-the-box for FreeBSD, Linux,
+ Solaris, SunOS, IRIX and OSF1. In short: On all major platforms!
+ [Ralf S. Engelschall]
+
+ *) Minor cleanup in http_main -- split QNX and OS2 specific "mmap"
+ scoreboard code into separate #defines -- USE_POSIX_SCOREBOARD
+ and USE_OS2_SCOREBOARD. [Dean Gaudet]
+
+ *) Fix one more special locking problem for RewriteMap programs in
+ mod_rewrite: According to the documentation of flock(), "Locks are on
+ files, not file descriptors. That is, file descriptors duplicated
+ through dup(2) or fork(2) do not result in multiple instances of a lock,
+ but rather multiple references to a single lock. If a process holding a
+ lock on a file forks and the child explicitly unlocks the file, the
+ parent will lose its lock.". To overcome this we have to make sure the
+ RewriteLock file is opened _AFTER_ the childs were spawned which is now
+ the case by opening it in the child_init instead of the module_init API
+ hook. [Ralf S. Engelschall] PR#1029
+
+ *) Change to Location and LocationMatch semantics. LocationMatch no
+ longer lets a single slash match multiple adjacent slashes in the
+ URL. This change is for consistency with RewriteRule and
+ AliasMatch. Multiple slashes have meaning in URLs that they do
+ not have in (some) filesystems. Location on the other hand can
+ be considered a shorthand for a more complicated regex, and it
+ does match multiple slashes with a single slash -- which is
+ also consistent with the Alias directive.
+ [Dean Gaudet] related PR#1440
+
+ *) Fix bug with mod_mime_magic causing certain files, including files
+ of length 0, to result in no response from the server.
+ [Dean Gaudet]
+
+ *) The Configure script now generates src/include/ap_config.h which
+ contains the set of defines used when Apache is compiled on a platform.
+ This file can then be included by external modules before including
+ any Apache header files in case they are being built separately from
+ Apache. Along with this change, a couple of minor changes were
+ made to make Apache's #defines coexist peacefully with any autoconf
+ defines an external module might have. [Rasmus Lerdorf]
+
+ *) Fix mod_rewrite for the ugly API case where <VirtualHost> sections exist
+ but without any RewriteXXXXX directives. Here mod_rewrite is given no
+ chance by the API to initialize its per-server configuration and thus
+ receives the wrong one from the main server. This is now avoided by
+ remembering the server together with the config structure while
+ configuring and later assuming there is no config when we see a
+ difference between the remembered server and the one calling us.
+ [Ralf S. Engelschall] PR#1790
+
+ *) Fixed the DBM RewriteMap support for mod_rewrite: First the support now
+ is automatically disabled under configure time when the dbm_xxx functions
+ are not available. Second, two heavy source code errors in the DBM
+ support code were fixed. This makes DBM RewriteMap's usable again after
+ a long time of brokenness. [Ralf S. Engelschall] PR#1696
+
+ *) Now all configuration files support Unix-style line-continuation via
+ the trailing backslash ("\") character. This enables us to write down
+ complex or just very long directives in a more readable way. The
+ backslash character has to be really the last character before the
+ newline and it has not been prefixed by another (escaping) backslash.
+ [Ralf S. Engelschall]
+
+ *) When using ProxyPass the ?querystring was not passed correctly.
+ [Joel Truher <truher@wired.com>]
+
+ *) To deal with modules being compiled and [dynamically] linked
+ at a different time from the core, the SERVER_VERSION and
+ SERVER_BUILT symbols have been abstracted through the new
+ API routines apapi_get_server_version() and apapi_get_server_built().
+ [Ken Coar] PR#1448
+
+ *) WIN32: Preserve trailing slash in canonical path (and hence
+ in PATH_INFO). [Paul Sutton, Ben Laurie]
+
+ *) PORT: USE_PTHREAD_SERIALIZED_ACCEPT has proven unreliable
+ depending on the rev of Solaris and what mixture of modules
+ are in use. So it has been disabled, and Solaris is back to
+ using USE_FCNTL_SERIALIZED_ACCEPT. Users may experiment with
+ USE_PTHREAD_SERIALIZED_ACCEPT at their own risk, it may speed
+ up static content only servers. Or it may fail unpredictably.
+ [Dean Gaudet] PR#1779, 1854, 1904
+
+ *) mod_test_util_uri.c created which tests the logic in util_uri.c.
+ [Dean Gaudet]
+
+ *) API: Rewrite of absoluteURI handling, and in particular how
+ absoluteURIs match vhosts. Unless a request is a proxy request, a
+ "http://host" url is treated as if a similar "Host:" header had been
+ supplied. This change was made to support future HTTP/1.x protocols
+ which may require clients to send absoluteURIs for all requests.
+
+ In order to achieve this change subtle changes were made to the API. In a
+ request_rec, r->hostlen has been removed. r->unparsed_uri now exists so
+ that the unmodified uri can be retrieved easily. r->proxyreq is not set
+ by the core, modules must set it during the post_read_request or
+ translate_names phase.
+
+ Plus changes to the virtualhost test suite for absoluteURI testing.
+
+ This fixes several bugs with the proxy proxying requests to vhosts
+ managed by the same httpd.
+ [Dean Gaudet]
+
+ *) API: Cleanup of code in http_vhost.c, and remove vhost matching
+ code from mod_rewrite. The vhost matching is now performed by a
+ globally available function matches_request_vhost(). [Dean Gaudet]
+
+ *) Reduce memory usage, and speed up ServerAlias support. As a
+ side-effect users can list multiple ServerAlias directives
+ and they're all considered.
+ [Chia-liang Kao <clkao@cirx.org>] PR#1531
+
+ *) The "poly" directive in image maps did not include the borders of the
+ polygon, whereas the "rect" directive does. Fix this inconsistency.
+ [Konstantin Morshnev <moko@design.ru>] PR#1771
+
+ *) Make \\ behave as expected. [Ronald.Tschalaer@psi.ch]
+
+ *) Add the `%a' construct to LogFormat and CustomLog to log the client IP
+ address. [Todd Eigenschink <eigenstr@mixi.net>] PR#1885
+
+ *) API: A new source module main/util_uri.c; It contains a routine
+ parse_uri_components() and friends which breaks a URI into its component
+ parts. These parts are stored in a uri_components structure called
+ parsed_uri within each request_rec, and are available to all modules.
+ Additionally, an unparse routine is supplied which re-assembles the URI
+ components back to an URI, optionally hiding the username:password@ part
+ from ftp proxy requests, and other useful routines. Within the structure,
+ you find on a ready-for-use basis:
+ scheme; /* scheme ("http"/"ftp"/...) */
+ hostinfo; /* combined [user[:password]@]host[:port] */
+ user; /* user name, as in http://user:passwd@host:port/ */
+ password; /* password, as in http://user:passwd@host:port/ */
+ hostname; /* hostname from URI (or from Host: header) */
+ port_str; /* port string (integer representation is in "port") */
+ path; /* the request path (or "/" if only scheme://host was given) */
+ query; /* Everything after a '?' in the path, if present */
+ fragment; /* Trailing "#fragment" string, if present */
+ This is meant to serve as the platform for *BIG* savings in
+ code complexity for the proxy module (and maybe the vhost logic).
+ [Martin Kraemer]
+
+ *) Make all possible meta-construct expansions ($N, %N, %{NAME} and
+ ${map:key}) available for all location where a string is created in
+ mod_rewrite rewriting rulesets: 1st arg of RewriteCond, 2nd arg of
+ RewriteRule and for the [E=NAME:STRING] flag of RewriteRule. This way the
+ possible expansions are consequently usable at all string creation
+ locations. [Ralf S. Engelschall]
+
+ *) Fix initialization of RewriteLogLevel (default now is 0 as documented
+ and not 1) and the per-virtual-server merging of directives. Now all
+ directives except `RewriteEngine' and `RewriteOption' are either
+ completely overridden (default) or completely inherited (when
+ `RewriteOptions inherit') is used. [Ralf S. Engelschall] PR#1325
+
+ *) Fix `RewriteMap' program lookup in situations where such maps are
+ defined but disabled (`RewriteEngine off') in per-server context.
+ [Ralf S. Engelschall] PR#1431
+
+ *) Fix bug introduced in 1.3b4-dev, config with no Port setting would cause
+ server to bind to port 0 rather than 80. [Dean Gaudet]
+
+ *) Fix long-standing problem with RewriteMap _programs_ under Unix derivates
+ (like SunOS and FreeBSD) which don't accept the locking of pipes
+ directly. A new directive RewriteLock is introduced which can be used to
+ setup a separate locking file which then is used for synchronization.
+ [Ralf S. Engelschall] PR#1029
+
+ *) WIN32: The server root is obtained from the registry key
+ HKLM\SOFTWARE\Apache Group\Apache\<version> (version is currently
+ "1.3 beta"), unless overridden by the -d command line flag. The
+ value is stored by running "apache -i -d serverroot". [Paul Sutton]
+
+ *) Merged os/win32/mod_dll.c into modules/standard/mod_so.c to support
+ dynamic loading on Win32 and Unix via the same module. [Paul Sutton]
+
+ *) Now mod_rewrite no longer makes problematic assumptions on the characters
+ a username can contain when trying to expand it via /etc/passwd.
+ [Ralf S. Engelschall]
+
+ *) The mod_setenvif BrowserMatch backwards compatibility command did not
+ work properly with spaces in the regex. [Ronald Tschalaer] PR#1825
+
+ *) Add new RewriteMap types: First, `rnd' which is equivalent to the `txt'
+ type but with a special post-processing for the looked-up value: It
+ parses it into alternatives according to `|' chars and then only one
+ particular alternative is chosen randomly (this is an essential
+ functionality needed for balancing between backend-servers when using
+ Apache as a Reverse Proxy. The looked up value here is a list of
+ servers). Second, `int' with the built-in maps named `tolower' and
+ `toupper' which can be used to map URL parts to a fixed case (this is an
+ essential feature to fix the case of server names when doing mass
+ virtual-hosting with the help of mod_rewrite instead of using
+ <VirtualHost> sections). [Ralf S. Engelschall, parts based on code from
+ Jay Soffian <jay@cimedia.com>] PR#1631
+
+ *) Add a new directive to mod_proxy similar to ProxyPass: `ProxyPassReverse'.
+ This directive lets Apache adjust the URL in Location-headers on HTTP
+ redirect responses sent by the remote server. This way the virtually
+ mapped area is no longer left on redirects and thus by-passed which is
+ especially essential when running Apache as a reverse proxy.
+ [Ralf S. Engelschall]
+
+ *) Hide Proxy-Authorization from CGI/SSI/etc just like Authorization is
+ hidden. [Alvaro Martinez Echevarria]
+
+ *) Apache will, when started with the -X (single process) debugging flag,
+ honor the SIGINT or SIGQUIT signals again now. This capability got lost
+ a while ago during OS/2 signal handling changes.
+
+ *) [PORT] Work around the fact that NeXT runs on more than the
+ m68k chips in mod_status [Scott Anguish and Timothy Luoma
+ <luomat@peak.org>]
+
+ *) [PORT] Recognize FreeBSD versions so we can use the OS regex as well
+ as handling unsigned-chars for FreeBSD v3 and v2 [Andrey Chernov
+ <ache@nagual.pp.ru> and Jim] PR#1450
+
+ *) Use SA_RESETHAND or SA_ONESHOT when installing the coredump handlers.
+ In particular the handlers could trigger themselves into an infinite
+ loop if RLimitMem was used with a small amount of memory -- too small
+ for the signal stack frame to be set up. [Dean Gaudet]
+
+ *) Fix problems with absoluteURIs introduced during 1.3b4. [Dean Gaudet,
+ Alvaro Martinez Echevarria <alvaro@lander.es>]
+
+ *) Fix multiple UserDir problem introduced during 1.3b4-dev.
+ [Dean Gaudet] PR#1850
+
+ *) ap_cpystrn() had an off-by-1 error.
+ [Charles Fu <ccwf@klab.caltech.edu>] PR#1847
+
+ *) API: As Ken suggested the check_cmd_context() function and related
+ defines are non-static now so modules can use 'em. [Martin Kraemer]
+
+ *) mod_info would occasionally produce an unpaired <tt> in its
+ output. Fixed. [Martin Kraemer]
+
+ *) By default AIX binds a process (and it's children) to a single
+ processor. httpd children now unbind themselves from that cpu
+ and re-bind to one selected at random via bindprocessor()
+ [Doug MacEachern]
+
+ *) Linux 2.0 and above implement RLIMIT_AS, RLIMIT_DATA has almost no
+ effect. Work around it by using RLIMIT_AS for the RLimitMEM
+ directive. [Enrik Berkhan <enrik@inka.de>] PR#1816
+
+ *) mod_mime_magic error message should indicate the filename when
+ reads fail. ["M.D.Parker" <mdpc@netcom.com>] PR#1827
+
+ *) Previously Apache would permit </Files> to end <FilesMatch> (and
+ similary for Location and Directory), now this is diagnosed as an
+ error. Improve error messages for mismatched sections (<Files>,
+ <FilesMatch>, <Directory>, <DirectoryMatch>, ...).
+ [Dean Gaudet, Martin Kraemer]
+
+ *) <Files> is not permitted within <Location> (because of the
+ semantic ordering). [Dean Gaudet] PR#379
+
+ *) <Files> with wildcards was broken by the change in wildcard
+ semantics (* does not match /). To fix this, <Files> now
+ apply only to the basename of the request filename. This
+ fixes some other inconsistencies in <Files> semantics
+ (such as <Files a*b> not working). [Dean Gaudet] PR#1817
+
+ *) Removed bogus "dist.tar" target from Makefile.tmpl and make sure
+ backup files are removed on "clean" target [Ralf S. Engelschall]
+
+ *) PORT: Add -lm to LIBS for HPUX. [Dean Gaudet] PR#1639
+
+ *) Various errors from select() and accept() in child_main() would
+ result in an infinite loop. It seems these two tickle kernel
+ or library bugs occasionally, and result in log spammage and
+ a generally bad scene. Now the child exits immediately,
+ which seems to be a good workaround.
+ [Dean Gaudet] PR#1747, 1107, 588, 1787, 987, 588
+
+ *) Cleaned up some race conditions in unix child_main during
+ initialization. [Dean Gaudet]
+
+ *) SECURITY: "UserDir /abspath" without a * in the path would allow
+ remote users to access "/~.." and bypass access restrictions
+ (but note /~../.. was handled properly).
+ [Lauri Jesmin <jesmin@ut.ee>] PR#1701
+
+ *) API: os_is_path_absolute() now takes a const char * instead of a char *.
+ [Dean Gaudet]
+
+Changes with Apache 1.3b5
+
+ *) Source file dependencies in Makefile.tmpl files throughout the
+ source tree were updated to accurately reflect reality.
+ [Dean Gaudet]
+
+ *) Preserve the content encoding given by the AddEncoding directive
+ when the client doesn't otherwise specify an encoding.
+ [Ronald Tschalaer <Ronald.Tschalaer@psi.ch>]
+
+ *) Sort out problems with canonical filename handling happening too late.
+ [Dean Gaudet, Ben Laurie]
+
+Changes with Apache 1.3b4
+
+ *) The module structure was modified to include a *dynamic_load_handle
+ in the STANDARD_MODULE_STUFF portion, and the MODULE_MAGIC_NUMBER
+ has been bumped accordingly. [Paul Sutton]
+
+ *) All BrowserMatch directives mentioned in
+ htdocs/manual/known_client_problems.html are in the default
+ configuration files. [Lars Eilebrecht]
+
+ *) MiNT port update. [Jan Paul Schmidt]
+
+ *) HTTP/1.1 requires x-gzip and gzip encodings be treated
+ equivalent, similarly for x-compress and compress. Apache
+ now ignores a leading x- when comparing encodings. It also
+ preserves the encoding the client requests (for example if
+ it requests x-gzip, then Apache will respond with x-gzip
+ in the Content-Encoding header).
+ [Ronald Tschalaer <Ronald.Tschalaer@psi.ch>] PR#1772
+
+ *) Fix a memory leak on keep-alive connections. [Igor Tatarinov]
+
+ *) Added mod_so module to support dynamic loading of modules on Unix
+ (like mod_dld for Win32). This replaces mod_dld.c. Use SharedModule
+ instead of AddModule in Configuration to build shared modules
+ [Sameer Parekh, Paul Sutton]
+
+ *) Minor cleanups to r->finfo handling in some modules.
+ [Dean Gaudet]
+
+ *) Abstract read()/write() to ap_read()/ap_write().
+ Makes it easier to add other types of IO code such as SFIO.
+ [Randy Terbush]
+
+ *) API: Generalize default_port manipulations to make support of
+ different protocols easier. [Ben Laurie, Randy Terbush]
+
+ *) There are many cases where users do not want Apache to form
+ self-referential urls using the "canonical" ServerName and Port.
+ The new UseCanonicalName directive (default on), if set to off
+ will cause Apache to use the client-supplied hostname and port.
+ API: Part of this change required a change to the construct_url()
+ prototype; and the addition of get_server_name() and
+ get_server_port().
+ [Michael Douglass <mikedoug@texas.net>, Dean Gaudet]
+ PR#315, 459, 485, 1433
+
+ *) Yet another rearrangement of the source tree.. now all the common
+ header files are in the src/include directory. The -Imain -Iap
+ references in Makefiles have been changed to the simpler -Iinclude
+ instead. In addition to simplifying the build a little bit, this
+ also makes it clear when a module is referencing something in a
+ other than kosher manner (e.g., the proxy including mod_mime.h).
+ Module-private header files (the proxy, mod_mime, the regex library,
+ and mod_rewrite) have not been moved to src/include; nor have
+ the OS-abstraction files. [Ken Coar]
+
+ *) Fix a bug where r->hostname didn't have the :port stripped
+ from it. [Dean Gaudet]
+
+ *) Tweaked the headers_out table size, and the subprocess_env
+ table size guess in rename_original_environment(). Added
+ MAKE_TABLE_PROFILE which can help discover make_table()
+ calls that use too small an initial guess, see alloc.c.
+ [Dean Gaudet]
+
+ *) Options and AllowOverride weren't properly merging in the main
+ server setting inside vhosts (only an issue when you have no
+ <Directory> or other section containing an Options that affects
+ a request). Options +foo or -foo in the main_server wouldn't
+ affect the main_server's lookup defaults. [Dean Gaudet]
+
+ *) Variable 'cwd' was being used pointlessly before being set.
+ [Ken Coar] PR#1738
+
+ *) r->allowed handling cleaned up in the standard modules.
+ [Dean Gaudet]
+
+ *) Some case-sensitivity issues cleaned up to be consistent with
+ RFC2068. [Dean Gaudet]
+
+ *) SIGURG doesn't exist everywhere.
+ [Mark Andrew Heinrich <heinrich@tinderbox.Stanford.EDU>]
+
+ *) mod_unique_id was erroneously generating a second unique id when
+ an internal redirect occured. Such redirects occur, for example,
+ when processing a DirectoryIndex match. [Dean Gaudet]
+
+ *) API: table_add, table_merge, and table_set include implicit pstrdup()
+ of the key and value. But in many cases this is not required
+ because the key/value is a constant, or the value has been built
+ by pstrcat() or other similar means. New routines table_addn,
+ table_mergen, and table_setn have been added to the API, these
+ routines do not pstrdup() their arguments. The core code and
+ standard modules were changed to take advantage of these routines.
+ The resulting server is up to 20% faster in some situations.
+
+ Note that it is easy to get code subtly wrong if you pass a key/value
+ which is in a pool other than the pool of the table. The only
+ safe thing to do is to pass key/values which are in the pool of
+ the table, or in one of the ancestors of the pool of the table.
+ i.e. if the table is part of a subrequest, a value from the main
+ request's pool is OK since the subrequest pool is a sub_pool of the
+ main request's pool (and therefore has a lifespan at most as long as
+ the main pool). There is debugging code which can detect improper
+ usage, enabled by defining POOL_DEBUG. See alloc.c for more details.
+ [Dmitry Khrustalev <dima@bog.msu.su>, Dean Gaudet]
+
+ *) More mod_mime_magic cleanup: fewer syscalls; should handle "files"
+ which don't exist on disk more gracefully; handles vhosts properly.
+ Update documentation to reflect the code -- if there's no
+ MimeMagicFile directive then the module is not enabled.
+ [Dean Gaudet]
+
+ *) PORT: Some older *nix dialects cannot automatically start scripts
+ which begin with a #! interpreter line (the shell starts the scripts
+ appropriately on these platforms). Apache now supports starting of
+ "hashbang-scripts" when the NEED_HASHBANG_EMUL define is set.
+ [Martin Kraemer, with code from peter@zeus.dialix.oz.au (Peter Wemm)
+ taken from tcsh]
+
+ *) API: "typedef array_header table" removed from alloc.h, folks should
+ have been writing to use table as if it were an opaque type, but even
+ some standard modules got this wrong. By changing the definition
+ to "typedef struct table table" module authors will receive compile
+ time warnings that they're doing the wrong thing. This change
+ facilitates future changes with more sophisticated table
+ structures. Specifically, module authors should be using table_elts()
+ to get access to an array_header * for the table. [Dean Gaudet]
+
+ *) API: Renamed new_connection() to avoid namespace collision with LDAP
+ library routines. [Ken Coar, Rasmus Lerdorf]
+
+ *) WIN32: mod_speling is now available on the Win32 platform.
+ [Marc Slemko]
+
+ *) For clarity the following compile time definition was changed:
+
+ SAFE_UNSERIALIZED_ACCEPT -> SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+
+ Also, for example, HAVE_MMAP would mean to use mmap() scoreboards
+ and not be a general notice that the OS has mmap(). Now the
+ HAVE_MMAP/SHMGET #defines strictly are informational that the
+ OS has that method of shared memory; the type to use for
+ the scoreboard is a seperate #define (USE_MMAP_SCOREBOARD
+ and USE_SHMGET_SCOREBOARD). This allows outside modules to
+ determine if shared memory is available and allows Apache
+ to determine the best method to use for the scoreboard.
+ [Jim Jagielski]
+
+ *) PORT: UnixWare 2.1.2 SMP appears to require USE_FCNTL_SERIALIZED_ACCEPT,
+ as do various earlier versions. It should be safe on all versions.
+ Unixware 1.x appears to have the same SIGHUP bug as solaris does with
+ the slack code. A few other cleanups for Unixware.
+ [Tom Hughes <thh@cyberscience.com>] PR#1082, PR#1282, PR#1499, PR#1553
+
+ *) PORT: A/UX can handle single-listen accepts without mutex
+ locking, so we add SINGLE_LISTEN_UNSERIALIZED_ACCEPT. [Jim Jagielski]
+
+ *) When die() happens we need to eat any request body if one exists.
+ Otherwise we can't continue with a keepalive session. This shows up
+ as a POST problem with MSIE 4.0, typically against pages which are
+ authenticated. [Roy Fielding] PR#1399
+
+ *) If you define SECURITY_HOLE_PASS_AUTHORIZATION then the Authorization
+ header will be passed to CGIs. This is generally a security hole, so
+ it's not a default. [Marc Slemko] PR#549
+
+ *) Fix Y2K problem with date printing in suexec log.
+ [Paul Eggert <eggert@twinsun.com>] PR#1343
+
+ *) WIN32 deserves a pid file. [Ben Hyde]
+
+ *) suexec errors now include the errno/description. [Marc Slemko] PR#1543
+
+ *) PORT: OSF/1 now uses USE_FLOCK_SERIALIZED_ACCEPT to solve PR#467.
+ The choice of flock vs. fcntl was made based on timings which showed that
+ even on non-NFS, non-exported filesystems fcntl() was an order of
+ magnitude slower. It also uses SINGLE_LISTEN_UNSERIALIZED_ACCEPT so
+ that single socket users will see no difference. [Dean Gaudet] PR#467
+
+ *) "File does not exist" error message was erroneously including the
+ errno. [Marc Slemko]
+
+ *) Improve the warning message generated when a client drops the
+ connection (hits stop button, etc.) during a send. [Roy Fielding]
+
+ *) Defining GPROF will disable profiling in the parent and enable it
+ in the children. If you're profiling under Linux this is pretty much
+ necessary because SIGPROF is lost across a fork(). [Dean Gaudet]
+
+ *) htdigest and htpasswd needed slight tweaks to work on OS/2 and WIN32.
+ [Brian Havard]
+
+ *) The NeXT cc (which is gcc hacked up) doesn't appear to support some
+ gcc functionality. Work around it.
+ [Keith Severson <keith@sssd.navy.mil>] PR#1613
+
+ *) Some linkers complain when .o files contain no functions.
+ [Keith Severson <keith@sssd.navy.mil>] PR#1614
+
+ *) Some const declarations in mod_imap.c that were added for debugging
+ purposes caused some compilers heartburn without adding any
+ significant value, so they've been removed. [Ken Coar]
+
+ *) The src/main/*.h header files have had #ifndef wrappers added to
+ insulate them against duplicate calls if they get included through
+ multiple paths (e.g., in .c files as well as other .h files).
+ [Ken Coar]
+
+ *) The libap routines now have a header file for their prototypes,
+ src/ap/ap.h, to ease their use in non-httpd applications. [Ken Coar]
+
+ *) mod_autoindex with a plaintext header file would emit the <PRE>
+ start-tag before the HTML preamble, rather than after the preamble
+ but before the header file contents. [John Van Essen <jve@gamers.org>]
+ PR#1667
+
+ *) SECURITY: Fix a possible buffer overflow in logresolve. This is
+ only an issue on systems without a MAXDNAME define or where
+ the resolver returns domain names longer than MAXDNAME. [Marc Slemko]
+
+ *) SECURITY: Eliminate possible buffer overflow in cfg_getline, which
+ is used to read various types of files such as htaccess and
+ htpasswd files. [Marc Slemko]
+
+ *) SECURITY: Ensure that the buffer returned by ht_time is always
+ properly null terminated. [Marc Slemko]
+
+ *) The "Connection" header could be sent back with multiple "close"
+ tokens. Not an error, but a waste.
+ [Ronald.Tschalaer@psi.ch] PR#1683
+
+ *) mod_rewrite's RewriteLog should behave like mod_log_config, it
+ shouldn't force hostname lookups. [Dean Gaudet] PR#1684
+
+ *) "basic" auth needs a case-insensitive comparison.
+ [Ronald.Tschalaer@psi.ch] PR#1666
+
+ *) For maximum portability, the environment passed to CGIs should
+ only contain variables whose names match the regex
+ /[a-zA-Z][a-zA-Z0-9_]*/. This is now enforced by stamping
+ underscores over any character outside the regex. This
+ affects HTTP_* variables, in a way that should be backward
+ compatible for all the standard headers; and affects variables
+ set with SetEnv/BrowserMatch and similar directives.
+ [Dean Gaudet]
+
+ *) mod_speling returned incorrect HREF's when an ambigous match
+ was found. Noticed by <robinton@amtrash.comlink.de> (Soeren Ziehe)
+ [robinton@amtrash.comlink.de (Soeren Ziehe), Martin Kraemer]
+
+ *) PORT: Apache now compiles & runs on an EBCDIC mainframe
+ (the Siemens BS2000/OSD family) in the POSIX subsystem
+ [Martin Kraemer]
+
+ *) PORT: Fix problem killing children when terminating. Allow ^C
+ to shut down the server. [Brian Havard]
+
+ *) pstrdup() is implicit in calls to table_* functions, so there's
+ no need to do it before calling. Clean up a few cases.
+ [Marc Slemko, Dean Gaudet]
+
+ *) new -C and -c command line arguments
+ usage:
+ -C "directive" : process directive before reading config files
+ -c "directive" : process directive after reading config files
+ example:
+ httpd -C "PerlModule Apache::httpd_conf"
+ [Doug MacEachern, Martin Kraemer]
+
+ *) WIN32: Fix the execution of CGIs that are scripts and called
+ with path info that does not have an '=' in.
+ (eg. http://server/cgi-bin/printenv?foobar)
+ [Marc Slemko] PR#1591
+
+ *) WIN32: Fix a call to os_canonical_filename so it doesn't try to
+ mess with fake filenames. This fixes proxy caching on
+ win32. PR#1265
+
+ *) SECURITY: General mod_include cleanup, including fixing several
+ possible buffer overflows and a possible infinite loop.
+ [Dean Gaudet, Marc Slemko]
+
+ *) SECURITY: Numerous changes to mod_imap in a general cleanup
+ including fixing a possible buffer overflow. [Dean Gaudet]
+
+ *) WIN32: overhaul of multithreading code. Shutdowns are now graceful
+ (connections are not dropped). Code can handle graceful restarts
+ (but there is as yet no way to signal this to Apache). Various
+ other cleanups. [Paul Sutton]
+
+ *) The aplog_error changes specific to 1.3 introduced a buffer
+ overrun in the (now legacy) log_printf function. Fixed.
+ [Dean Gaudet]
+
+ *) mod_digest didn't properly deal with proxy authentication. It
+ also lacked a case-insensitive comparision of the "Digest"
+ token. [Ronald Tschalaer <Ronald.Tschalaer@psi.ch>] PR#1599
+
+ *) A few cleanups in mod_status for efficiency. [Dean Gaudet]
+
+ *) A few cleanups in mod_info to make it thread-safe, and remove an
+ off-by-5 bug that could hammer \0 on the stack. [Dean Gaudet]
+
+ *) no2slash() was O(n^2) in the length of the input. Make it O(n).
+ [Dean Gaudet]
+
+ *) API: migration from strncpy() to our "enhanced" version called
+ ap_cpystrn() for performance and functionality reasons.
+ Located in libap.a. [Jim Jagielski]
+
+ *) table_set() and table_unset() did not deal correctly with
+ multiple occurrences of the same key.
+ [Stephen Scheck <sscheck@infonex.net>, Ben Laurie] PR#1604
+
+ *) The AuthName must now be enclosed in quotes if it is to contain
+ spaces. [Ken Coar] PR#1195
+
+ *) API: new function: ap_escape_quotes(). [Ken Coar] PR#1195
+
+ *) WIN32: Work around optimiser bug that killed ISAPI in release
+ versions. [Ben Laurie] PR#1533
+
+ *) PORT: Update the MPE port [Mark Bixby, Jim Jagielski]
+
+ *) Interim (slow) fix for p->sub_pool critical sections in
+ alloc.c (affects win32 only). [Ben Hyde]
+
+ *) non-WIN32 was missing destroy_mutex definition. [Ben Hyde]
+
+ *) send_fd_length() did not calculate total_bytes_sent properly.
+ [Ben Reser <breser@regnow.com>] PR#1366
+
+ *) The bputc() macro was not properly integrated with the chunking
+ code; in many cases modules using bputc() could cause completely
+ bogus chunked output. (Typically this will show up as problems
+ with Internet Explorer 4.0 reading a page, but other browsers
+ having no problem.) [Dean Gaudet]
+
+ *) Create LARGE_WRITE_THRESHOLD define which determines how many
+ bytes have to be supplied to bwrite() before it will consider
+ doing a writev() to assemble multiple buffers in one system
+ call. This is critical for modules such as mod_include,
+ mod_autoindex, mod_php3 which all use bputc()/bputs() of smaller
+ strings in some cases. The result would be extra effort
+ setting up writev(), and in many cases extra effort building
+ chunks. The default is 31, it can be overriden at compile
+ time. [Dean Gaudet]
+
+ *) Move the gid switching code into the child so that log files
+ and pid files are opened with the root gid.
+ [Gregory A Lundberg <lundberg@vr.net>]
+
+ *) WIN32: Check for binaries by looking for the executable header
+ instead of counting control characters.
+ [Jim Patterson <Jim.Patterson@Cognos.COM>] PR#1340
+
+ *) ap_snprintf() moved from main/util_snprintf.c to ap/ap_snprintf.c
+ so the functionality is available to applications other than the
+ server itself (like the src/support tools). [Ken Coar]
+
+ *) ap_slack() moved out of main/util.c into ap/ap_slack.c as part of
+ the libap consolidation work. [Ken Coar]
+
+ *) ap_snprintf() with a len of 0 behaved like sprintf(). This is not
+ useful, and isn't what the standards require. Now it returns 0
+ and writes nothing. [Dean Gaudet]
+
+ *) When an error occurs in fcntl() locking suggest the user look up
+ the docs for LockFile. [Dean Gaudet]
+
+ *) Eliminate some dead code from writev_it_all().
+ [Igor Tatarinov <tatarino@prairie.NoDak.edu>]
+
+ *) mod_autoindex had an fread() without checking the result code.
+ It also wouldn't handle "AddIconByType (TXT,/icons/text.gif text/*"
+ (note the missing closing paren) properly. [Dean Gaudet]
+
+ *) It appears the "257th byte" bug (see
+ htdocs/manual/misc/known_client_problems.html#257th-byte) can happen
+ at the 256th byte as well. Fixed. [Dean Gaudet]
+
+ *) PORT: Fix mod_mime_magic under OS/2, no support for block devices.
+ [Brian Havard]
+
+ *) Fix memory corruption caused by allocating auth usernames in the
+ wrong pool. [Dean Gaudet] PR#1500
+
+ *) Fix an off-by-1, and an unterminated string error in
+ mod_mime_magic. [Dean Gaudet]
+
+ *) Fix a potential SEGV problem in mod_negotiation when dealing
+ with type-maps. [Dean Gaudet]
+
+ *) Better glibc support under Linux. [Dean Gaudet] PR#1542
+
+ *) "RedirectMatch gone /" would cause a SIGSEGV. [Dean Gaudet] PR#1319
+
+ *) WIN32: avoid overflows during file canonicalisations.
+ [malcolm@mgdev.demon.co.uk] PR#1378
+
+ *) WIN32: set_file_slot() didn't detect absolute paths. [Ben Laurie]
+ PR#1511, 1508
+
+ *) WIN32: mod_status display header didn't match fields. [Ben Laurie]
+
+ *) The pthread_mutex_* functions return an error code, and don't
+ set errno. [Igor Tatarinov <tatarino@prairie.NoDak.edu>]
+
+ *) WIN32: Allow spaces to prefix the interpreter in #! lines.
+ [Ben Laurie] PR#1101
+
+ *) WIN32: Cure file leak in CGIs. [Peter Tillemans <pti@net4all.be>] PR#1523
+
+ *) proxy_ftp: the directory listings generated by the proxy ftp module
+ now have a title in which the path components are clickable and allow
+ quick navigation to the clicked-on directory on the currently listed
+ ftp server. This also fixes a bug where the ".." directory links would
+ sometimes refer to the wrong directory. [Martin Kraemer]
+
+ *) WIN32: Allocate the correct amount of memory for the scoreboard.
+ [Ben Hyde] PR#1387
+
+ *) WIN32: Only lowercase the part of the path that is real. [Ben Laurie]
+ PR#1505
+
+ *) Fix problems with timeouts in inetd mode and -X mode. [Dean Gaudet]
+
+ *) Fix the spurious "(0)unknown error: mmap_handler: mmap failed"
+ error messages. [Ben Hyde]
+
+Changes with Apache 1.3b3
+
+ *) WIN32: Work around brain-damaged spawn calls that can't deal
+ with spaces and slashes. [Ben Laurie]
+
+ *) WIN32: Fix the code so CGIs can use socket calls on Windows.
+ The problem was that certain undocumented environment variables
+ needed for sockets to work under Win32 were not being passed.
+ [Frank Faubert <frank@sane.com>]
+
+ *) Add a "-V" command line flag to the httpd binary. This
+ flag shows some of the defines that Apache was compiled with.
+ It is useful for debugging purposes. [Martin Kraemer]
+
+ *) Start separating the ap_*() routines into their own library, so they
+ can be used by items in src/support among other things.
+ [Ken Coar] PR#512, 905, 1252, 1308
+
+ *) Give a more informative error when no AuthType is set.
+ [Lars Eilebrecht]
+
+ *) Remove strtoul() use from mod_proxy because it isn't available
+ on all platforms. [Marc Slemko] PR#1214
+
+ *) WIN32: Some Win32 systems terminated all responses after 16 kB.
+ This turns out to be a bug in Winsock - select() doesn't always
+ return the correct status. [Ben Laurie]
+
+ *) Directives owned by http_core can now use the new check_cmd_context()
+ routine to ensure that they're not being used within a container
+ (e.g., <Directory>) where they're invalid. [Martin Kraemer]
+
+ *) PORT: Recent changes made it necessary to add explicit prototype
+ for fgetc() and fgets() on SunOS 4.x. [Martin Kraemer, Ben Hyde]
+
+ *) It was necessary to distinguish between resources which are
+ allocated in the parent, for cleanup in the parent, and resources
+ which are allocated in each child, for cleanup in each child.
+ A new pool was created which is passed to the module child_init
+ and child_exit functions; modules are free to register per-child
+ cleanups there. This fixes a bug with reliable piped logs.
+ [Dean Gaudet]
+
+ *) mod_autoindex wasn't displaying the ReadmeName file at the bottom
+ unless it was also doing FancyIndexes, but it displayed the
+ HeaderName file at the top under all circumstances. It now shows
+ the ReadmeName file for simple indices, too, as it should.
+ [Ken Coar] PR#1373
+
+ *) http_core was mmap()ing even in cases where it wasn't going to
+ read the file. [Ben Hyde <bhyde@gensym.com>]
+
+ *) Complete rewrite ;-) of mod_rewrite's URL rewriting engine:
+ Now the rewriting engine (the heart of mod_rewrite) is organized more
+ straight-forward, first time well documented and reduced to the really
+ essential parts. All redundant cases were stripped off and processing now
+ is the same for both per-server and per-directory context with only a
+ minimum difference (the prefix stripping in per-dir context). As a
+ side-effect some subtle restrictions and two recently discovered problems
+ are gone: Wrong escaping of QUERY_STRING on redirects in per-directory
+ context and restrictions on the substitution URL on redirects.
+ Additionally some minor source cleanups were done.
+ [Ralf S. Engelschall]
+
+ *) Lars Eilebrecht wrote a whole new set of Apache Vhost Internals
+ documentation, examples, explanations and caveats. They live in a new
+ subdirectory htdocs/manual/vhost/. [Lars Eilebrecht <sfx@unix-ag.org>]
+
+ *) If ap_slack fails to allocate above the low slack line it's a good
+ indication that further problems will occur; it's a better indication
+ than many external libraries give us when we actually run out of
+ descriptors. So report it to the user once per restart.
+ [Dean Gaudet] PR#1181
+
+ *) Change mod_include and mod_autoindex to use Y2K-safe date formats
+ by default. [Ken Coar]
+
+ *) Add a "SuppressColumnSorting" option to the IndexOptions list,
+ which will keep the column heading from being links for sorting
+ the display. [Ken Coar, suggested by Brian Tiemann <btman@pacific.net>]
+ PR #1261
+
+ *) PORT: Update the LynxOS port. [Marius Groeger <mag@sysgo.de>]
+
+ *) Fix logic error when issuing a mmap() failed message
+ with a non-zero MMAP_THRESHOLD.
+ [David Chambers <davidc@flosun.salk.edu>] PR#1294
+
+ *) Preserve handler value on ProxyPass'ed requests by not
+ calling find_types on a proxy'd request; fixes problems
+ where some ProxyPass'ed URLs weren't actually passed
+ to the proxy.
+ [Lars Eilebrecht] PR#870
+
+ *) Fix a byte ordering problem in mod_access which prevented
+ the old-style syntax (i.e. "a.b.c." to match a class C)
+ from working properly. [Dean Gaudet] PR#1248, 1328, 1384
+
+ *) Fix problem with USE_FLOCK_SERIALIZED_ACCEPT not working
+ properly. Each child needs to open the lockfile instead
+ of using the passed file-descriptor from the parent.
+ [Jim Jagielski] PR#1056
+
+ *) Fix the error logging in mod_cgi; the recent error log changes
+ introduced a bug that prevented it from working correctly.
+ [M.D.Parker] PR#1352
+
+ *) Default to USE_FCNTL_SERIALIZED_ACCEPT on HPUX to properly
+ handle multiple Listen directives. [Marc Slemko] PR#872
+
+ *) Inherit a bugfix to fnmatch.c from FreeBSD sources.
+ ["[KOI8-R] áÎÄÒÅÊ þÅÒÎÏ×" <ache@nagual.pp.ru>] PR#1311
+
+ *) When a configuration parse complained about a bad directive,
+ the logger would use whatever (unrelated) value was in errno.
+ errno is now forced to EINVAL first in this case. [Ken Coar]
+
+ *) A sed command in the Configure script pushed the edge of POSIXness,
+ breaking on some systems. [Bhaba R.Misra <system@vt.edu>] PR#1368
+
+ *) Solaris >= 2.5 was totally broken due to a mess up using pthread
+ mutexes. [Roy Fielding, Dean Gaudet]
+
+ *) OS/2 Port updated; it should be possible to build OS/2 from the same
+ sources as Unix now. [Brian Havard <brianh@kheldar.apana.org.au>]
+
+ *) Fix a year formatting bug in mod_usertrack.
+ [Paul Eggert <eggert@twinsun.com>] PR#1342
+
+ *) A mild SIGTERM/SIGALRM race condition was eliminated.
+ [Dean Gaudet] PR#1211
+
+ *) Warn user that default path has changed if /usr/local/etc/httpd
+ is found on the system. [Lars Eilebrecht]
+
+ *) Various mod_mime_magic bug fixes and cleanups: Uncompression
+ should work, it should work on WIN32, and a few resource
+ leaks and abort conditions are fixed.
+ [Dean Gaudet] PR#1205
+
+ *) PORT: On AIX 1.x files can't be named '@', fix the proxy cache
+ to use '%' instead of '@' in its encodings.
+ [David Schuler <schuld@btv.ibm.com>] PR#1317
+
+ *) Improve the warning message generated when the "server is busy".
+ [Dean Gaudet] PR#1293
+
+ *) PORT: All ports which don't otherwise define DEF_WANTHSREGEX will
+ get Spencer regex by default. This is to avoid having to
+ discover bugs in operating system libraries. [Dean Gaudet]
+
+ *) PORT: "Fix" PR#467 by generating warnings on systems which we have
+ not been able to get working USE_*_SERIALIZED_ACCEPT settings for.
+ Document this a bit more in src/PORTING. [Dean Gaudet] PR#467
+
+ *) Ensure that one copy of config warnings makes it to the
+ error_log. [Dean Gaudet]
+
+ *) Invent new structure and associated methods to handle config file
+ reading. Add "custom" hook to use config file cfg_getline() on
+ something which is not a FILE* [Martin Kraemer]
+
+ *) Make single-exe Windows install. [Ben Laurie and Eric Esselink]
+
+ *) WIN32: Make CGI work under Win95. [Ben Laurie and Paul Sutton]
+
+ *) WIN32: Make index.html and friends work under Win95. [Ben Laurie]
+
+ *) PORT: Solaris 2.4 needs Spencer regex, the system regex is broken.
+ [John Line <jml4@cam.ac.uk>] PR#1321
+
+ *) Default pathname has been changed everywhere to /usr/local/apache
+ [Sameer <sameer@c2.net>]
+
+ *) PORT: AIX now uses USE_FCNTL_SERIALIZED_ACCEPT.
+ [David Bronder <David-Bronder@uiowa.edu>] PR#849
+
+ *) PORT: i386 AIX does not have memmove.
+ [David Schuler <schuld@btv.ibm.com>] PR#1267
+
+ *) PORT: HPUX now defaults to using Spencer regex.
+ [Philippe Vanhaesendonck <pvanhaes@be.oracle.com>,
+ Omar Del Rio <al112263@academ01.lag.itesm.mx>] PR#482, 1246
+
+ *) PORT: Some versions of NetBSD don't automatically define
+ __NetBSD__. Workaround by defining NETBSD.
+ [Chris Craft <ccraft@cncc.cc.co.us>] PR#977
+
+ *) PORT: UnixWare 2.x requires -lgen for syslog.
+ [Hans Snijder <hs@meganet.nl>] PR#1249
+
+ *) PORT: ULTRIX appears to not have syslog.
+ [Lars Eilebrecht <Lars.Eilebrecht@unix-ag.org>]
+
+ *) PORT: Basic Gemini port (treat it like unixware212).
+ ["Pavel Yakovlev (Paul McHacker)" <hac@tomcat.olly.ru>]
+
+ *) PORT: All SVR4 systems now use NET_SIZE_T = size_t, and
+ use USE_SHMGET_SCOREBOARD.
+ [Martin Kraemer]
+
+ *) Various improvements in detecting config file errors (missing closing
+ directives for <Directory>, <Files> etc. blocks, prohibiting global
+ server settings in <VirtualHost> blocks, flagging unhandled multiple
+ arguments to <Directory>, <Files> etc.)
+ [Martin Kraemer]
+
+ *) Add support to suexec wrapper program for mod_unique_id's UNIQUE_ID
+ variable to provide this one to suexec'd CGIs, too.
+ [M.D.Parker <mdpc@netcom.com>] PR#1284
+
+ *) New support tool: src/support/split-logfile, a sample Perl script which
+ splits up a combined access log into separate files based on the
+ name of the virtual host (listed first in the log records by "%v").
+ [Ken Coar]
+
+Changes with Apache 1.3b2 (there is no 1.3b1)
+
+ *) TestCompile was not passing $LIBS [Dean Gaudet]
+
+ *) Makefile.tmpl was not using $CFLAGS in the link phase.
+ [Martin Kraemer]
+
+ *) Add debugging code to alloc.c. Defining ALLOC_DEBUG provides a
+ rudimentary memory debugger which can be used on live servers with
+ low impact -- it sets all allocated and freed memory bytes to 0xa5.
+ Defining ALLOC_USE_MALLOC will cause the alloc code to use malloc()
+ and free() for each object. This is far more expensive and should
+ only be used for testing with tools such as Electric Fence and
+ Purify. See main/alloc.c for more details. [Dean Gaudet]
+
+ *) Configure uses a sh trap and didn't set its exitcode properly.
+ [Dean Gaudet] PR#1159
+
+ *) Yet another vhost revamp. Add the NameVirtualHost directive which
+ explicitly lists the ip:port pairs that are to be used for name-vhosts.
+ From a given ip:port, regardless what the Host: header is, you can
+ only reach the vhosts defined on that ip:port. The precedence of
+ vhosts was reversed to match other precedences in the config --
+ the earlier vhosts override the later vhosts. All vhost matching was
+ moved into http_vhost.[ch]. [Dean Gaudet]
+
+ *) ap_inline can be used to force inlining. GNUC __attribute__() can
+ be used for whatever reason is appropriate (i.e. format() warnings
+ for printf style functions). Both are enabled only with
+ gcc >= 2.7.x (so that we have fewer support issues with older
+ versions). [Dean Gaudet]
+
+ *) Fix support for Proxy Authentication (we were testing the response
+ status too early). [Marc Slemko]
+
+ *) CoreDumpDirectory directive directs where the core file is
+ written when a SIGSEGV, SIGBUS, SIGABORT or SIGABRT are
+ received. [Marc Slemko, Dean Gaudet]
+
+ *) PORT: Support for Atari MINT.
+ [Jan Paul Schmidt <Jan.P.Schmidt@mni.fh-giessen.de>]
+
+ *) When booting, apache will now detach itself from stdin, stdout,
+ and stderr. stderr will not be detached until after the config
+ files have been read so you will be able to see initial error
+ messages. After that all errors are logged in the error_log.
+ This makes it more convenient to start apache via rsh, ssh,
+ or crontabs. [Dean Gaudet] PR#523
+
+ *) mod_proxy was sending HTTP/1.1 responses to ftp requests by mistake.
+ Also removed the auto-generated link to www.apache.org that was the
+ source of so many misdirected bug reports. [Roy Fielding, Marc Slemko]
+
+ *) send_fb would not detect aborted connections in some situations.
+ [Dean Gaudet]
+
+ *) mod_include would use uninitialized data when parsing certain
+ expressions involving && and ||. [Brian Slesinsky] PR#1139
+
+ *) mod_imap should only handle GET methods. [Jay Bloodworth]
+
+ *) suexec.c wouldn't build without -DLOG_EXEC. [Jason A. Dour]
+
+ *) mod_autoindex improperly counted &escapes; as more than one
+ character in the description. It also improperly truncated
+ descriptions that were exactly the maximum length.
+ [Martin Kraemer]
+
+ *) RedirectMatch was not properly escaping the result (PR#1155). Also
+ "RedirectMatch /advertiser/(.*) $1" is now permitted.
+ [Dean Gaudet]
+
+ *) mod_include now uses symbolic names to check for request success
+ and return HTTP errors, and correctly handles all types of
+ redirections (previously it only did temporary redirect correctly).
+ [Ken Coar, Roy Fielding]
+
+ *) mod_userdir was modifying r->finfo in cases where it wasn't setting
+ r->filename. Since those two are meant to be in sync with each other
+ this is a bug. ["Paul B. Henson" <henson@intranet.csupomona.edu>]
+
+ *) PORT: Support Unisys SVR4, whose uname returns mostly useless data.
+ ["Kaufman, Steven E" <Steven.Kaufman@unisys.com>]
+
+ *) Inetd mode (which is buggy) uses timeouts without having setup the
+ jmpbuffer. [Dean Gaudet] PR#1064
+
+ *) Work around problem under Linux where a child will start looping
+ reporting a select error over and over.
+ [Rick Franchuk <rickf@transpect.net>] PR#1107, 987, 588
+
+ *) Fixed error in proxy_util.c when looping through multiple host IP
+ addresses. [Lars Eilebrecht] PR#974
+
+ *) If BUFFERED_LOGS is defined then mod_log_config will do atomic
+ buffered writes -- that is, it will buffer up to PIPE_BUF (i.e. 4k)
+ bytes before writing, but it will never split a log entry across a
+ buffer boundary. [Dean Gaudet]
+
+ *) API: the short_score record has been split into two pieces, one which
+ the parent writes on, and one which the child writes on. As part of
+ this change the get_scoreboard_info() function was removed, and
+ scoreboard_image was exported. This change fixes a race condition
+ in file based scoreboard systems, and speeds up changes involving the
+ scoreboard in earlier 1.3 development. [Dean Gaudet]
+
+ *) API: New register_other_child() API (see http_main.h) which allows
+ modules to register children with the parent for maintenance. It
+ is disabled by defining NO_OTHER_CHILD. [Dean Gaudet]
+
+ *) API: New piped_log API (see http_log.h) which implements piped logs,
+ and will use register_other_child to implement reliable piped logs
+ when it is available. The reliable piped logs part can be disabled
+ by defining NO_RELIABLE_PIPED_LOGS. At the moment reliable piped
+ logs is only available on Unix. [Dean Gaudet]
+
+ *) API: set_last_modified() broken into set_last_modified(), set_etag(), and
+ meets_conditions(). This allows conditional HTTP selection to be
+ handled separately from the storing of the header fields, and provides
+ the ability for CGIs to set their own ETags for conditional checking.
+ [Ken Coar, Roy Fielding] PR#895
+
+ *) Changes to mod_log_config to allow naming of format strings.
+ Format nicknames are defined with "LogFormat fmt nickname", and can
+ be used with "LogFormat nickname" and "CustomLog logtarget nickname".
+ [Ken Coar]
+
+ *) New module, "mod_speling", which can help find files even when
+ the URL is slightly misspelled. [Martin Kraemer, Alexei Kosut]
+
+ *) API: New function child_terminate() triggers the child process to
+ exit, while allowing the child finish what it needs to for the
+ current request first.
+ [Doug MacEachern, Alexei Kosut]
+
+ *) Windows now defaults to using full status reports with mod_status.
+ [Alexei Kosut] PR #1094
+
+ *) *Really* disable all mod_rewrite operations if the engine is off.
+ Some things (like RewriteMaps) were checked/performed even if they
+ weren't supposed to be. [Ken Coar] PR #991
+
+ *) Implement a new timer scheme which eliminates the need to call alarm() all
+ the time. Instead a counter in the scoreboard for each child is used to
+ show when the child has made forward progress. The parent samples this
+ counter every scoreboard maintenance cycle, and issues SIGALRM if no
+ progress has been made in the timeout period. This reduces the static
+ request best-case syscall count to 22 from 29. This scheme is only
+ used by systems with memory-based scoreboards. [Dean Gaudet]
+
+ *) The proxy now properly handles CONNECT requests which are sent
+ to proxy servers when using ProxyRemote. [Marc Slemko] PR#1024
+
+ *) A script called apachectl has been added to the support
+ directory. This script allows you to do things such as
+ "apachectl start" and "apachectl restart" from the command
+ line. [Marc Slemko]
+
+ *) Modules and core routines are now put into libraries, which
+ simplifies the link line tremendously (among other advantages).
+ [Paul Sutton]
+
+ *) Some of the MD5 names defined in Apache have been renamed to have
+ an `ap_' prefix to avoid conflicts with routines supplied by
+ external libraries. [Ken Coar]
+
+ *) Removal of mod_auth_msql.c from the distribution. There are many
+ other options for databases today. Rather than offer one option,
+ offer none at this time. mod_auth_msql and other SQL database
+ authentication modules can be found at the Apache Module Registry.
+ http://modules.apache.org/ It would be nice to offer a generic
+ mod_auth_sql option in the near future.
+
+ *) PORT: BeOS support added [Alexei Kosut]
+
+ *) Configure no longer accepts the -make option, since it creates
+ Makefile on the fly based on Makefile.tmpl and Configuration.
+
+ *) Apache now gracefully shuts down when it receives a SIGTERM, instead
+ of forcibly killing off all its processes and exiting without
+ cleaning up. [Alexei Kosut]
+
+ *) API: A new field in the request_rec, r->mtime, has been added to
+ avoid gratuitous parsing of date strings. It is intended to hold
+ the last-modified date of the resource (if applicable). An
+ update_mtime() routine has also been added to advance it if
+ appropriate. [Roy Fielding, Ken Coar]
+
+ *) SECURITY: If a htaccess file can not be read due to bad permissions,
+ deny access to the directory with a HTTP_FORBIDDEN. The previous
+ behavior was to ignore the htaccess file if it could not be read.
+ This change may make some setups with unreadable htaccess files
+ stop working. [Marc Slemko] PR#817
+
+ *) Add aplog_error() providing a mechanism to define levels of
+ verbosity to the server error logging. This addition also provides
+ the ability to log errors using syslogd. Error logging is configurable
+ on a per-server basis using the LogLevel directive. Conversion
+ of log_*() in progress. [Randy Terbush]
+
+ *) Further enhance aplog_error() to not log filename, line number, and
+ errno information when it isn't applicable. [Ken Coar, Dean Gaudet]
+
+ *) WIN32: Canonicalise filenames under Win32. Short filenames are
+ converted to long ones. Backslashes are converted to forward
+ slashes. Case is converted to lower. Parts of URLs that do not
+ correspond to files are left completely alone. [Ben Laurie]
+
+ *) PORT: 2 new OSs added to the list of ports:
+ Encore's UMAX V: Arieh Markel <amarkel@encore.com>
+ Acorn RISCiX: Stephen Borrill <sborrill@xemplar.co.uk>
+
+ *) Add the server version (SERVER_VERSION macro) to the "server
+ configured and running" entry in the error_log. Also build an
+ object file at link-time that contains the current time
+ (SERVER_BUILT global const char[]), and include that in the
+ message. [Ken Coar]
+
+ *) Set r->headers_out when sending responses from the proxy.
+ This fixes things such as the logging of headers sent from
+ the proxy. [Marc Slemko] PR#659
+
+ *) support/httpd_monitor is no longer distributed because the
+ scoreboard should not be file based if at all possible. Use
+ mod_status to see current server snapshot.
+
+ *) (set_file_slot): New function, allowing auth directives to be
+ independent of the server root, so the server documents can be
+ moved to a different directory or machine more easily.
+ [David J. MacKenzie]
+
+ *) If no TransferLog is given explicitly, decline
+ to log. This supports coexistence with other logging modules,
+ such as the custom one that UUNET uses. [David J. MacKenzie]
+
+ *) Check for titles in server-parsed HTML files.
+ Ignore leading newlines and returns in titles. The old behavior
+ of replacing a newline after <title> with a space causes the
+ title to be misaligned in the listing. [David J. MacKenzie]
+
+ *) Change mod_cern_meta to be configurable on a per-directory basis.
+ [David J. MacKenzie]
+
+ *) Add 'Include' directive to allow inclusion of configuration
+ files within configuration files. [Randy Terbush]
+
+ *) Proxy errors on connect() are logged to the error_log (nothing
+ new); now they include the IP address and port that failed
+ (*that's* new). [Ken Coar, Marc Slemko] PR#352
+
+ *) Various architectures now define USE_MMAP_FILES which causes
+ the server to use mmap() for static files. There are two
+ compile-time tunables MMAP_THRESHOLD (minimum number of bytes
+ required to use mmap(), default is 0), and MMAP_SEGMENT_SIZE (maximum
+ number of bytes written in one cycle from a single mmap()d object,
+ default 32768). [Dean Gaudet]
+
+ *) API: Added post_read_request API phase which is run right after reading
+ the request from a client, or right after an internal redirect. It is
+ useful for modules setting environment variables that depend only on
+ the headers/contents of the request. It does not run during subrequests
+ because subrequests inherit pretty much everything from the main
+ request. [Dean Gaudet]
+
+ *) Added mod_unique_id which is used to generate a unique identifier for
+ each hit, available in the environment variable UNIQUE_ID.
+ [Dean Gaudet]
+
+ *) init_modules is now called after the error logs have been opened. This
+ allows modules to emit information messages into the error logs.
+ [Dean Gaudet]
+
+ *) Fixed proxy-pass-through feature of mod_rewrite; Added error logging
+ information for case where proxy module is not available. [Marc Slemko]
+
+ *) PORT: Apache has need for mutexes to serialize its children around
+ accept. In prior versions either fcntl file locking or flock file
+ locking were used. The method is chosen by the definition of
+ USE_xxx_SERIALIZED_ACCEPT in conf.h. xxx is FCNTL for fcntl(),
+ and FLOCK for flock(). New options have been added:
+ - SYSVSEM to use System V style semaphores
+ - PTHREAD to use POSIX threads (appears to work on Solaris only)
+ - USLOCK to use IRIX uslock
+ Based on timing various techniques, the following changes were made
+ to the defaults:
+ - Linux 2.x uses flock instead of fcntl
+ - Solaris 2.x uses pthreads
+ - IRIX uses SysV semaphores -- however multiprocessor IRIX boxes
+ work far faster if you -DUSE_USLOCK_SERIALIZED_ACCEPT
+ [Dean Gaudet, Pierre-Yves Kerembellec <Pierre-Yves.Kerembellec@vtcom.fr>,
+ Martijn Koster <m.koster@pobox.com>]
+
+ *) PORT: The semantics of accept/select make it very desirable to use
+ mutexes to serialize accept when multiple Listens are in use. But
+ in the case where only a single socket is open it is sometimes
+ redundant to serialize accept(). Not all unixes do a good job with
+ potentially dozens of children blocked on accept() on the same
+ socket. It's now possible to define SINGLE_LISTEN_UNSERIALIZED_ACCEPT and
+ the server will avoid serialization when listening on only one socket,
+ and use serialization when listening on multiple sockets.
+ [Dean Gaudet] PR#467
+
+ *) Configure changes: TestLib replaced by TestCompile, which has
+ some additional capability (such as doing a sanity check of
+ the compiler and flags selected); the version of Solaris is now
+ available via the #define value of SOLARIS2; IRIX n32bit libs
+ now supported and selectable by new Configuration Rule: IRIXN32;
+ We no longer default to -O2 optimization. [Jim Jagielski]
+
+ *) Updated Configure: Configuration now uses AddModule to specify
+ module source or binary file location, relative to src directory.
+ Modules can be dropped into modules/extra, or in their own
+ directory, and modules can come with a Makefile or Configure can
+ create one. Modules can add compiler or library information to
+ generated Makefiles. [Paul Sutton]
+
+ *) Source core re-organisation: distributed modules are now in
+ modules/standard. All other source code is in main. OS-specific
+ code is in os/{unix,emx,win32} directories. [Paul Sutton]
+
+ *) mod_browser has been removed, since it's replaced by mod_setenvif.
+ [Ken Coar]
+
+ *) Fix another long-standing bug in sub_req_lookup_file where it would
+ happily skip past access checks on subdirectories looked up with
+ relative paths. (It's used by mod_dir, mod_negotiation,
+ and mod_include.) [Dean Gaudet]
+
+ *) directory_walk optimization to reduce an O(N*M) loop to O(N+M) where
+ N is the number of <Directory> sections, and M is the number of
+ components in the filename of an object.
+
+ To achieve this optimization the following config changes were made:
+ - Wildcards (* and ?, not the regex forms) in <Directory>s,
+ <Files>s, and <Location>s now treat a slash as a special
+ character. For example "/home/*/public_html" previously would
+ match "/home/a/andrew/public_html", now it only matches things
+ like "/home/bob/public_html". This mimics /bin/sh behaviour.
+ - It's possible now to use [] wildcarding in <Directory>, <Files>
+ or <Location>.
+ - Regex <Directory>s are applied after all non-regex <Directory>s.
+
+ [Dean Gaudet]
+
+ *) Fix a bug introduced in 1.3a1 directory_walk regarding .htaccess files
+ and corrupted paths. [Dean Gaudet]
+
+ *) Enhanced and cleaned up the URL rewriting engine of mod_rewrite:
+ First the grouped parts of RewriteRule pattern matches (parenthesis!) can
+ be accessed now via backreferences $1..$9 in RewriteConds test-against
+ strings in addition to RewriteRules subst string. Second the grouped
+ parts of RewriteCond pattern matches (parenthesis!) can be accessed now
+ via backreferences %1..%9 both in following RewriteCond test-against
+ strings and RewriteRules subst string. This provides maximum flexibility
+ through the use of backreferences.
+ Additionally the rewriting engine was cleaned up by putting common
+ code to the new expand_backrefs_inbuffer() function.
+ [Ralf S. Engelschall]
+
+ *) When merging the main server's <Directory> and <Location> sections into
+ a vhost, put the main server's first and the vhost's second. Otherwise
+ the vhost can't override the main server. [Dean Gaudet] PR#717
+
+ *) The <Directory> code would merge and re-merge the same section after
+ a match was found, possibly causing problems with some modules.
+ [Dean Gaudet]
+
+ *) ip-based vhosts are stored and queried using a hashing function, which
+ has been shown to improve performance on servers with many ip-vhosts.
+ Some other changes had to be made to accommodate this:
+ - the * address for vhosts now behaves like _default_
+ - the matching process now is:
+ - match an ip-vhost directly via hash (possibly matches main
+ server)
+ - if that fails, just pretend it matched the main server
+ - if so far only the main server has been matched, perform
+ name-based lookups (ServerName, ServerAlias, ServerPath)
+ *only on name-based vhosts*
+ - if they fail, look for _default_ vhosts
+ [Dean Gaudet, Dave Hankins <dhankins@sugarat.net>]
+
+ *) dbmmanage overhaul:
+ - merge dbmmanage and dbmmanage.new functionality, remove dbmmanage.new
+ - tie() to AnyDBM_File which will use one of DB_File, NDBM_File or
+ GDBM_File (-ldb, -lndbm, -lgdbm) (trying each in that order)
+ - provide better seed for rand
+ - prompt for password as per getpass(3) (turn off echo, read from
+ /dev/tty, etc.)
+ - use "newstyle" crypt based on $Config{osname} ($^O)
+ - will not add a user if already in database, use new `update' command
+ instead
+ - added `check' command to check a users' password
+ - added `import' command to convert existing password text-files or
+ dbm files exported with `view'
+ - more descriptive usage, general cleanup, 'use strict' clean, etc.
+ [Doug MacEachern]
+
+ *) Added psocket() which is a pool form of socket(), various places within
+ the proxy weren't properly blocking alarms while registering the cleanup
+ for its sockets. bclose() now uses pclose() and pclosesocket(). There
+ was a bug where the client socket was being close()d twice due a still
+ registered cleanup. [Dean Gaudet]
+
+ *) A few cleanups were made to reduce time(), getpid(), and signal() calls.
+ [Dean Gaudet]
+
+ *) PORT: AIX >= 4.2 requires -lm due to libc changes.
+ [Jason Venner <jason@idiom.com>] PR#667
+
+ *) Enable ``=""'' for RewriteCond directives to match against
+ the empty string. This is the preferred way instead of ``^$''.
+ [Ralf S. Engelschall]
+
+ *) Fixed an infinite loop in mod_imap for references above the server root
+ [Dean Gaudet] PR#748
+
+ *) mod_proxy now has a ReceiveBufferSize directive, similar to
+ SendBufferSize, so that the TCP window can be set appropriately
+ for LFNs. [Phillip A. Prindeville]
+
+ *) mod_browser has been replaced by the more general mod_setenvif
+ (courtesy of Paul Sutton). BrowserMatch* directives are still
+ available, but are now joined by SetEnvIf*, UnSetEnvIf*, and
+ UnSetEnvIfZero directives. [Ken Coar]
+
+ *) "HostnameLookups double" forces double-reverse DNS to succeed in
+ order for remote_host to be set (for logging, or for the env var
+ REMOTE_HOST). The old define MAXIMUM_DNS has been deprecated.
+ [Dean Gaudet]
+
+ *) mod_access overhaul:
+ - Now understands network/netmask syntax (i.e. 10.1.0.0/255.255.0.0)
+ and cidr syntax (i.e. 10.1.0.0/16). PR#762
+ - Critical path was sped up by pre-computing a few things at config time.
+ - The undocumented syntax "allow user-agents" was removed,
+ the replacement is "allow from env=foobar" combined with mod_browser.
+ - When used with hostnames it now forces a double-reverse lookup
+ no matter what the directory settings are. This double-reverse
+ doesn't affect any of the other routines that use the remote
+ hostname. In particular it's still passed to CGIs and the log
+ without the double-reverse check. Related PR#860.
+ [Dean Gaudet]
+
+ *) When a large bwrite() occurs (larger than the internal buffer size),
+ while there is already something in the buffer, apache will combine
+ the large write and the buffer into a single writev(). (This is
+ in anticipation of using mmap() for reading files.)
+ [Dean Gaudet]
+
+ *) In obscure cases where a partial socket write occurred while chunking,
+ Apache would omit the chunk header/footer on the next block. Cleaned
+ up other bugs/inconsistencies in error conditions in buff.c. Fixed
+ a bug where a long pause in DNS lookups could cause the last packet
+ of a response to be unduly delayed. [Roy Fielding, Dean Gaudet]
+
+ *) API: Added child_exit function to module structure. This is called
+ once per "heavy-weight process" just before a server child exit()'s
+ e.g. when max_requests_per_child is reached, etc.
+ [Doug MacEachern, Dean Gaudet]
+
+ *) mod_include cleanup showed that handle_else was being used to handle
+ endif. It didn't cause problems, but it was cleaned up too.
+ [Howard Fear]
+
+ *) mod_cern_meta would attempt to find meta files for the directory itself
+ in some cases, but not in others. It now avoids it in all cases.
+ [Dean Gaudet]
+
+ *) mod_mime_magic would core dump if there was a decompression error.
+ [Martin Kraemer <Martin.Kraemer@mch.sni.de>] PR#904
+
+ *) PORT: some variants of DGUX require -lsocket -lnsl
+ [Alexander L Jones <alex@systems-options.co.uk>] PR#732
+
+ *) mod_autoindex now allows sorting of FancyIndexed directory listings
+ by the various fields (name, size, et cetera), either in ascending
+ or descending order. Just click on the column header. [Ken Coar]
+
+ *) PORT: Various tweaks to eliminate pointer-int casting warnings on 64-bit
+ CPUs like the Alpha. Apache still stores ints in pointers, but that's
+ the relatively safe direction. [Dean Gaudet] PR#344
+
+ *) PORT: QNX mmap() support for faster/more reliable scoreboard handling.
+ [Igor N Kovalenko <infoh@mail.wplus.net>] PR#683
+
+ *) child_main avoids an unneeded call to select() when there is only one
+ listening socket. [Dean Gaudet]
+
+ *) In the event that the server is starved for idle servers it will
+ spawn 1, then 2, then 4, ..., then 32 servers each second,
+ doubling each second. It'll also give a warning in the errorlog
+ since the most common reason for this is a poor StartServers
+ setting. The define MAX_SPAWN_RATE can be used to raise/lower
+ the maximum. [Dean Gaudet]
+
+ *) Apache now provides an effectively unbuffered connection for
+ CGI scripts. This means that data will be sent to the client
+ as soon as the CGI pauses or stops output; previously, Apache would
+ buffer the output up to a fixed buffer size before sending, which
+ could result in the user viewing an empty page until the CGI finished
+ or output a complete buffer. It is no longer necessary to use an
+ "nph-" CGI to get unbuffered output. Given that most CGIs are written
+ in a language that by default does buffering (e.g. perl) this
+ shouldn't have a detrimental effect on performance.
+
+ "nph-" CGIs, which formerly provided a direct socket to the client
+ without any server post-processing, were not fully compatible with
+ HTTP/1.1 or SSL support. As such they would have had to implement
+ the transport details, such as encryption or chunking, in order
+ to work properly in certain situations. Now, the only difference
+ between nph and non-nph scripts is "non-parsed headers".
+ [Dean Gaudet, Sameer Parekh, Roy Fielding]
+
+ *) If a BUFF is switched from buffered to unbuffered reading the first
+ bread() will return whatever remained in the buffer prior to the
+ switch. [Dean Gaudet]
+
+Changes with Apache 1.3a1
+
+ *) Added another Configure helper script: TestLib. It determines
+ if a specified library exists. [Jim Jagielski]
+
+ *) PORT: Allow for use of n32bit libraries under IRIX 6.x
+ [derived from patch from Jeff Hayes <jhayes@aw.sgi.com>]
+ PR#721
+
+ *) PORT: Some architectures use size_t for various lengths in network
+ functions such as accept(), and getsockname(). The definition
+ NET_SIZE_T is used to control this. [Dean Gaudet]
+
+ *) PORT: Linux: Attempt to detect glibc based systems and include crypt.h
+ and -lcrypt. Test for various db libraries (dbm, ndbm, db) when
+ mod_auth_dbm or mod_auth_db are included. [Dean Gaudet]
+
+ *) PORT: QNX doesn't have initgroups() which support/suexec.c uses.
+ [Igor N Kovalenko <infoh@mail.wplus.net>]
+
+ *) "force-response-1.0" now only applies to requests which are HTTP/1.0 to
+ begin with. "nokeepalive" now works for HTTP/1.1 clients. Added
+ "downgrade-1.0" which causes Apache to pretend it received a 1.0.
+ [Dean Gaudet] related PR#875
+
+ *) API: Correct child_init() slot declaration from int to void, to
+ match the init() declaration. Update mod_example to use the new
+ hook. [Ken Coar]
+
+ *) added transport handle slot (t_handle) to the BUFF structure
+ [Doug MacEachern]
+
+ *) get_client_block() returns wrong length if policy is
+ REQUEST_CHUNKED_DECHUNK.
+ [Kenichi Hori <ken@d2.bs1.fc.nec.co.jp>] PR#815
+
+ *) Support the image map format of FrontPage. For example:
+ rect /url.hrm 10 20 30 40
+ ["Chris O'Byrne" <obyrne@iol.ie>] PR#807
+
+ *) PORT: -lresolv and -lsocks were in the wrong order for Solaris.
+ ["Darren O'Shaughnessy" <darren@aaii.oz.au>] PR#846
+
+ *) AddModuleInfo directive for mod_info which allows you to annotate
+ the output of mod_info. ["Lou D. Langholtz" <ldl@usi.utah.edu>]
+
+ *) Added NoProxy directive to avoid using ProxyRemote for selected
+ addresses. Added ProxyDomain directive to cause unqualified
+ names to be qualified by redirection.
+ [Martin Kraemer <Martin.Kraemer@mch.sni.de>]
+
+ *) Support Proxy Authentication, and don't pass the Proxy-Authorize
+ header to the remote host in the proxy. [Sameer Parekh and
+ Wallace]
+
+ *) Upgraded mod_rewrite from 3.0.6+ to latest officially available version
+ 3.0.9. This upgrade includes: fixed deadlooping on rewriting to same
+ URLs, fixed rewritelog(), fixed forced response code handling on
+ redirects from within .htaccess files, disabled pipe locking under
+ braindead SunOS 4.1.x, allow env variables to be set even on rules with
+ no substitution, bugfixed situations where HostnameLookups is off, made
+ mod_rewrite more thread-safe for NT port and fixed problem when creating
+ an empty query string via "xxx?".
+ This update also removes the copyright of Ralf S. Engelschall,
+ i.e. now mod_rewrite no longer has a shared copyright. Instead is is
+ exclusively copyrighted by the Apache Group now. This happened because
+ the author now has gifted mod_rewrite exclusively to the Apache Group and
+ no longer maintains an external version.
+ [Ralf S. Engelschall]
+
+ *) API: Added child_init function to module structure. This is called
+ once per "heavy-weight process" before any requests are handled.
+ See http_config.h for more details. [Dean Gaudet]
+
+ *) Anonymous_LogEmail was logging on each subrequest.
+ [Dean Gaudet] PR#421, 868
+
+ *) API: Added is_initial_req() which tests if the request being
+ processed is the initial request, or a subrequest.
+ [Doug MacEachern]
+
+ *) Extended SSI (mod_include) now handles additional relops for
+ string comparisons (<, >, <=, and >=). [Bruno Wolff III] PR#41
+
+ *) Configure fixed to correctly propagate user-selected options and
+ settings (such as CC and OPTIM) to Makefiles other than
+ src/Makefile (notably support/Makefile). [Ken Coar] PR#666, #834
+
+ *) IndexOptions SuppressHTMLPreamble now causes the actual HTML of
+ directory indices to start with the contents of the HeaderName file
+ if there is one. If there isn't one, the behaviour is unchanged.
+ [Ken Coar, Roy Fielding, Andrey A. Chernov]
+
+ *) WIN32: Modules can now be dynamically loaded DLLs using the
+ LoadModule/LoadFile directives. Note that module DLLs must be
+ compiled with the multithreaded DLL version of the runtime library.
+ [Alexei Kosut and Ben Laurie]
+
+ *) Automatic indexing removed from mod_dir and placed into mod_autoindex.
+ This allows the admin to completely remove automatic indexing
+ from the server, while still supporting the basic functions of
+ trailing-slash redirects and DirectoryIndex files. Note that if
+ you're carrying over an old Configuration file and you use directory
+ indexing then you'll want to add:
+
+ Module autoindex_module mod_autoindex.o
+
+ before mod_dir in your Configuration. [Dean Gaudet]
+
+ *) popendir/pclosedir created to properly protect directory scanning.
+ [Dean Gaudet] PR#525
+
+ *) AliasMatch, ScriptAliasMatch and RedirectMatch directives added,
+ giving regex support to mod_alias. <DirectoryMatch>, <LocationMatch>
+ and <FilesMatch> sections added to succeed <DirectoryMatch ~>, etc...
+ [Alexei Kosut]
+
+ *) The AccessFileName directive can now take more than one filename.
+ ["Lou D. Langholtz" <ldl@usi.utah.edu>]
+
+ *) The new mod_mime_magic can be used to "magically" determine the type
+ of a file if the extension is unknown. Based on the unix file(1)
+ command. [Ian Kluft <ikluft@cisco.com>]
+
+ *) We now determine and display the time spent processing a
+ request if desired. [Jim Jagielski]
+
+ *) mod_status: PID field of "dead" child slots no longer displays
+ main httpd process's PID. [Jim Jagielski]
+
+ *) Makefile.nt added - to build all the bits from the command line:
+ nmake -f Makefile.nt
+ Doesn't yet work properly. [Ben Laurie]
+
+ *) Default text of 404 error is now "Not Found" rather than the
+ potentially misleading "File Not Found". [Ken Coar]
+
+ *) CONFIG: "HostnameLookups" now defaults to off because it is far better
+ for the net if we require people that actually need this data to
+ enable it. [Linus Torvalds]
+
+ *) directory_walk() is an expensive function, keep a little more state to
+ avoid needless string counting. Add two new functions make_dirstr_parent
+ and make_dirstr_prefix which replace all existing uses of make_dirstr.
+ The new functions are a little less general than make_dirstr, but
+ work more efficiently (less memory, less string counting).
+ [Dean Gaudet]
+
+ *) EXTRA_LFLAGS was changed to EXTRA_LDFLAGS (and LFLAGS was changed
+ to LDFLAGS) to avoid complications with lex rules in make files.
+ [Dean Gaudet] PR#372
+
+ *) run_method optimized to avoid needless scanning over NULLs in the
+ module list. [Dean Gaudet]
+
+ *) Revamp of (unix) scoreboard management code such that it avoids
+ unnecessary traversals of the scoreboard on each hit. This is
+ particularly important for high volume sites with a large
+ HARD_SERVER_LIMIT. Some of the previous operations were O(n^2),
+ and are now O(n). See also SCOREBOARD_MAINTENANCE_INTERVAL in
+ httpd.h. [Dean Gaudet]
+
+ *) In configurations using multiple Listen statements it was possible for
+ busy sockets to starve other sockets of service. [Dean Gaudet]
+
+ *) Added hook so standalone_main can be replaced at compile time
+ (define STANDALONE_MAIN)
+ [Doug MacEachern]
+
+ *) Lowest-level read/write functions in buff.c will be replaced with
+ the SFIO library calls sfread/sfwrite if B_SFIO is defined at
+ compile time. The default sfio discipline will behave as apache
+ would without sfio compiled in.
+ [Doug MacEachern]
+
+ *) Enhance UserDir directive (mod_userdir) to accept a list of
+ usernames for the 'disable' keyword, and add 'enable user...' to
+ selectively *en*able userdirs if they're globally disabled.
+ [Ken Coar]
+
+ *) If NETSCAPE_DBM_COMPAT is defined in EXTRA_CFLAGS then Apache
+ will work with Netscape dbm files. (dbmmanage will probably not
+ work however.) [Alexander Spohr <aspohr@netmatic.com>] PR#444
+
+ *) Add a ListenBacklog directive to control the backlog parameter
+ passed to listen(). Also change the default to 511 from 512.
+ [Marc Slemko]
+
+ *) API: A new handler response DONE which informs apache that the
+ request has been handled and it can finish off quickly, similar to
+ how it handles errors. [Rob Hartill]
+
+ *) Turn off chunked encoding after sending terminating chunk/footer
+ so that we can't do it twice by accident. [Roy Fielding]
+
+ *) mod_expire also issues Cache-Control: max-age headers.
+ [Rob Hartill]
+
+ *) API: Added kill_only_once option for free_proc_chain so that it won't
+ aggressively try to kill off specific children. For fastcgi.
+ [Stanley Gambarin <gambarin@OpenMarket.com>]
+
+ *) mod_auth deals with extra ':' delimited fields. [Marc Slemko]
+
+ *) Added IconHeight and IconWidth to mod_dir's IndexOptions directive.
+ When used together, these cause mod_dir to emit HEIGHT and WIDTH
+ attributes in the FancyIndexing IMG tags. [Ken Coar]
+
+ *) PORT: Sequent and SONY NEWS-OS support added. [Jim Jagielski]
+
+ *) PORT: Added Windows NT support
+ [Ben Laurie and Ambarish Malpani <ambarish@valicert.com>]
+
+Changes with Apache 1.2.6
+
+ *) mod_include when using XBitHack Full would send ETags in addition to
+ sending Last-Modifieds. This is incorrect HTTP/1.1 behaviour.
+ [Dean Gaudet] PR#1133
+
+ *) SECURITY: When a client connects to a particular port/addr, and
+ gives a Host: header ensure that the virtual host requested can
+ actually be reached via that port/addr. [Ed Korthof <ed@organic.com>]
+
+ *) Support virtual hosts with wildcard port and/or multiple ports
+ properly. [Ed Korthof <ed@organic.com>]
+
+ *) Fixed some case-sensitivity issues according to RFC2068.
+ [Dean Gaudet]
+
+ *) Set r->allowed properly in mod_asis.c, mod_dir.c, mod_info.c,
+ and mod_include.c. [Dean Gaudet]
+
+ *) Variable 'cwd' was being used pointlessly before being set.
+ [Ken Coar] PR#1738
+
+ *) SIGURG doesn't exist on all platforms.
+ [Mark Andrew Heinrich <heinrich@tinderbox.Stanford.EDU>]
+
+ *) When an error occurs during a POST, or other operation with a
+ request body, the body has to be read from the net before allowing
+ a keepalive session to continue. [Roy Fielding] PR#1399
+
+ *) When an error occurs in fcntl() locking suggest the user look up
+ the docs for LockFile. [Dean Gaudet]
+
+ *) table_set() and table_unset() did not deal correctly with
+ multiple occurrences of the same key. [Stephen Scheck
+ <sscheck@infonex.net>, Ben Laurie] PR#1604
+
+ *) send_fd_length() did not calculate total_bytes_sent properly in error
+ cases. [Ben Reser <breser@regnow.com>] PR#1366
+
+ *) r->connection->user was allocated in the wrong pool causing corruption
+ in some cases when used with mod_cern_meta. [Dean Gaudet] PR#1500
+
+ *) mod_proxy was sending HTTP/1.1 responses to ftp requests by mistake.
+ Also removed the auto-generated link to www.apache.org that was the
+ source of so many misdirected bug reports. [Roy Fielding, Marc Slemko]
+
+ *) Multiple "close" tokens may have been set in the "Connection"
+ header, not an error, but a waste.
+ [Ronald.Tschalaer@psi.ch] PR#1683
+
+ *) "basic" and "digest" auth tokens should be tested case-insensitive.
+ [Ronald.Tschalaer@psi.ch] PR#1599, PR#1666
+
+ *) It appears the "257th byte" bug (see
+ htdocs/manual/misc/known_client_problems.html#257th-byte) can happen
+ at the 256th byte as well. Fixed. [Dean Gaudet]
+
+ *) mod_rewrite would not handle %3f properly in some situations.
+ [Ralf Engelschall]
+
+ *) Apache could generate improperly chunked HTTP/1.1 responses when
+ the bputc() or rputc() functions were used by modules (such as
+ mod_include). [Dean Gaudet]
+
+ *) #ifdef wrap a few #defines in httpd.h to make life easier on
+ some ports. [Ralf Engelschall]
+
+ *) Fix MPE compilation error in mod_usertrack.c. [Mark Bixby]
+
+ *) Quote CC='$(CC)' to improve recurse make calls. [Martin Kraemer]
+
+ *) Avoid B_ERROR redeclaration on sysvr4 systems. [Martin Kraemer]
+
+Changes with Apache 1.2.5
+
+ *) SECURITY: Fix a possible buffer overflow in logresolve. This is
+ only an issue on systems without a MAXDNAME define or where
+ the resolver returns domain names longer than MAXDNAME. [Marc Slemko]
+
+ *) Fix an improper length in an ap_snprintf call in proxy_date_canon().
+ [Marc Slemko]
+
+ *) Fix core dump in the ftp proxy when reading incorrectly formatted
+ directory listings. [Marc Slemko]
+
+ *) SECURITY: Fix possible minor buffer overflow in the proxy cache.
+ [Marc Slemko]
+
+ *) SECURITY: Eliminate possible buffer overflow in cfg_getline, which
+ is used to read various types of files such as htaccess and
+ htpasswd files. [Marc Slemko]
+
+ *) SECURITY: Ensure that the buffer returned by ht_time is always
+ properly null terminated. [Marc Slemko]
+
+ *) SECURITY: General mod_include cleanup, including fixing several
+ possible buffer overflows and a possible infinite loop. This cleanup
+ was done against 1.3 code and then backported to 1.2, the result
+ is a large difference (due to indentation cleanup in 1.3 code).
+ Users interested in seeing a smaller set of relevant differences
+ should consider comparing against src/modules/standard/mod_include.c
+ from the 1.3b3 release. Non-indentation changes to mod_include
+ between 1.2 and 1.3 were minimal. [Dean Gaudet, Marc Slemko]
+
+ *) SECURITY: Numerous changes to mod_imap in a general cleanup
+ including fixing a possible buffer overflow. This cleanup also
+ was done with 1.3 code as a basis, see the the previous note
+ about mod_include. [Dean Gaudet]
+
+ *) SECURITY: If a htaccess file can not be read due to bad
+ permissions, deny access to the directory with a HTTP_FORBIDDEN.
+ The previous behavior was to ignore the htaccess file if it could not
+ be read. This change may make some setups with unreadable
+ htaccess files stop working. PR#817 [Marc Slemko]
+
+ *) SECURITY: no2slash() was O(n^2) in the length of the input.
+ Make it O(n). This inefficiency could be used to mount a denial
+ of service attack against the Apache server. Thanks to
+ Michal Zalewski <lcamtuf@boss.staszic.waw.pl> for reporting
+ this. [Dean Gaudet]
+
+ *) mod_include used uninitialized data for some uses of && and ||.
+ [Brian Slesinsky <bslesins@wired.com>] PR#1139
+
+ *) mod_imap should decline all non-GET methods.
+ [Jay Bloodworth <jay@pathways.sde.state.sc.us>]
+
+ *) suexec.c wouldn't build without -DLOG_EXEC. [Jason A. Dour]
+
+ *) mod_userdir was modifying r->finfo in cases where it wasn't setting
+ r->filename. Since those two are meant to be in sync with each other
+ this is a bug. ["Paul B. Henson" <henson@intranet.csupomona.edu>]
+
+ *) mod_include did not properly handle all possible redirects from sub-
+ requests. [Ken Coar]
+
+ *) Inetd mode (which is buggy) uses timeouts without having setup the
+ jmpbuffer. [Dean Gaudet] PR#1064
+
+ *) Work around problem under Linux where a child will start looping
+ reporting a select error over and over.
+ [Rick Franchuk <rickf@transpect.net>] PR#1107
+
+Changes with Apache 1.2.4
+
+ *) The ProxyRemote change in 1.2.3 introduced a bug resulting in the proxy
+ always making requests with the full-URI instead of just the URI path.
+ [Marc Slemko, Roy Fielding]
+
+ *) Add -lm for AIX versions >= 4.2 to allow Apache to link properly
+ on this platform. [Marc Slemko]
+
+Changes with Apache 1.2.3
+
+ *) The request to a remote proxy was mangled if it was generated as the
+ result of a ProxyPass directive. URL schemes other than http:// were not
+ supported when ProxyRemote was used. PR#260, PR#656, PR#699, PR#713,
+ PR#812 [Lars Eilebrecht]
+
+ *) Fixed proxy-pass-through feature of mod_rewrite; Added error logging
+ information for case where proxy module is not available. [Marc Slemko]
+
+ *) Force proxy to always respond as HTTP/1.0, which it was failing to
+ do for errors and cached responses. [Roy Fielding]
+
+ *) PORT: Improved support for ConvexOS 11. [Jeff Venters]
+
+Changes with Apache 1.2.2 [not released]
+
+ *) Fixed another long-standing bug in sub_req_lookup_file where it would
+ happily skip past access checks on subdirectories looked up with relative
+ paths. (It's used by mod_dir, mod_negotiation, and mod_include.)
+ [Dean Gaudet]
+
+ *) Add lockfile name to error message printed out when
+ USE_FLOCK_SERIALIZED_ACCEPT is defined.
+ [Marc Slemko]
+
+ *) Enhanced the chunking and error handling inside the buffer functions.
+ [Dean Gaudet, Roy Fielding]
+
+ *) When merging the main server's <Directory> and <Location> sections into
+ a vhost, put the main server's first and the vhost's second. Otherwise
+ the vhost can't override the main server. [Dean Gaudet] PR#717
+
+ *) The <Directory> code would merge and re-merge the same section after
+ a match was found, possibly causing problems with some modules.
+ [Dean Gaudet]
+
+ *) Fixed an infinite loop in mod_imap for references above the server root.
+ [Dean Gaudet] PR#748
+
+ *) mod_include cleanup showed that handle_else was being used to handle
+ endif. It didn't cause problems, but it was cleaned up too.
+ [Howard Fear]
+
+ *) Last official synchronization of mod_rewrite with author version (because
+ mod_rewrite is now directly developed by the author at the Apache Group):
+ o added diff between mod_rewrite 3.0.6+ and 3.0.9
+ minus WIN32/NT stuff, but plus copyright removement.
+ In detail:
+ - workaround for detecting infinite rewriting loops
+ - fixed setting of env vars when "-" is used as subst string
+ - fixed forced response code on redirects (PR#777)
+ - fixed cases where r->args is ""
+ - kludge to disable locking on pipes under braindead SunOS
+ - fix for rewritelog in cases where remote hostname is unknown
+ - fixed totally damaged request_rec walk-back loop
+ o remove static from local data and add static to global ones.
+ o replaced ugly proxy finding stuff by simple
+ find_linked_module("mod_proxy") call.
+ o added missing negation char on rewritelog()
+ o fixed a few comment typos
+ [Ralf S. Engelschall]
+
+ *) Anonymous_LogEmail was logging on each subrequest.
+ [Dean Gaudet] PR#421, PR#868
+
+ *) "force-response-1.0" now only applies to requests which are HTTP/1.0 to
+ begin with. "nokeepalive" now works for HTTP/1.1 clients. Added
+ "downgrade-1.0" which causes Apache to pretend it received a 1.0.
+ Additionally mod_browser now triggers during translate_name to workaround
+ a deficiency in the header_parse phase.
+ [Dean Gaudet] PR#875
+
+ *) get_client_block() returns wrong length if policy is
+ REQUEST_CHUNKED_DECHUNK.
+ [Kenichi Hori <ken@d2.bs1.fc.nec.co.jp>] PR#815
+
+ *) Properly treat <files> container like other containers in mod_info.
+ [Marc Slemko] PR#848
+
+ *) The proxy didn't treat the "Host:" keyword of the host header as case-
+ insensitive. The proxy would corrupt the first line of a response from
+ an HTTP/0.9 server. [Kenichi Hori <ken@d2.bs1.fc.nec.co.jp>] PR#813,814
+
+ *) mod_include would log some bogus values occasionally.
+ [Skip Montanaro <skip@calendar.com>, Marc Slemko] PR#797
+
+ *) PORT: The slack fd changes in 1.2.1 introduced a problem with SIGHUP
+ under Solaris 2.x (up through 2.5.1). It has been fixed.
+ [Dean Gaudet] PR#832
+
+ *) API: In HTTP/1.1, whether or not a request message contains a body
+ is independent of the request method and based solely on the presence
+ of a Content-Length or Transfer-Encoding. Therefore, our default
+ handlers need to be prepared to read a body even if they don't know
+ what to do with it; otherwise, the body would be mistaken for the
+ next request on a persistent connection. discard_request_body()
+ has been added to take care of that. [Roy Fielding] PR#378
+
+ *) API: Symbol APACHE_RELEASE provides a numeric form of the Apache
+ release version number, such that it always increases along the
+ same lines as our source code branching. [Roy Fielding]
+
+ *) Minor oversight on multiple variants fixed. [Paul Sutton] PR#94
+
+Changes with Apache 1.2.1
+
+ *) SECURITY: Don't serve file system objects unless they are plain files,
+ symlinks, or directories. This prevents local users from using pipes
+ or named sockets to invoke programs for an extremely crude form of
+ CGI. [Dean Gaudet]
+
+ *) SECURITY: HeaderName and ReadmeName were settable in .htaccess and
+ could contain "../" allowing a local user to "publish" any file on
+ the system. No slashes are allowed now. [Dean Gaudet]
+
+ *) SECURITY: It was possible to violate the symlink Options using mod_dir
+ (headers, readmes, titles), mod_negotiation (type maps), or
+ mod_cern_meta (meta files). [Dean Gaudet]
+
+ *) SECURITY: Apache will refuse to run as "User root" unless
+ BIG_SECURITY_HOLE is defined at compile time. [Dean Gaudet]
+
+ *) CONFIG: If a symlink pointed to a directory then it would be disallowed
+ if it contained a .htaccess disallowing symlinks. This is contrary
+ to the rule that symlink permissions are tested with the symlink
+ options of the parent directory. [Dean Gaudet] PR#353
+
+ *) CONFIG: The LockFile directive can be used to place the serializing
+ lockfile in any location. It previously defaulted to /usr/tmp/htlock.
+ [Somehow it took four of us: Randy Terbush, Jim Jagielski, Dean Gaudet,
+ Marc Slemko]
+
+ *) Request processing now retains state of whether or not the request
+ body has been read, so that internal redirects and subrequests will
+ not try to read it twice (and block). [Roy Fielding]
+
+ *) Add a placeholder in modules/Makefile to avoid errors with certain
+ makes. [Marc Slemko]
+
+ *) QUERY_STRING was unescaped in mod_include, it shouldn't be.
+ [Dean Gaudet] PR#644
+
+ *) mod_include was not properly changing the current directory.
+ [Marc Slemko] PR#742
+
+ *) Attempt to work around problems with third party libraries that do not
+ handle high numbered descriptors (examples include bind, and
+ solaris libc). On all systems apache attempts to keep all permanent
+ descriptors above 15 (called the low slack line). Solaris users
+ can also benefit from adding -DHIGH_SLACK_LINE=256 to EXTRA_CFLAGS
+ which keeps all non-FILE * descriptors above 255. On all systems
+ this should make supporting large numbers of vhosts with many open
+ log files more feasible. If this causes trouble please report it,
+ you can disable this workaround by adding -DNO_SLACK to EXTRA_CFLAGS.
+ [Dean Gaudet] various PRs
+
+ *) Related to the last entry, network sockets are now opened before
+ log files are opened. The only known case where this can cause
+ problems is under Solaris with many virtualhosts and many Listen
+ directives. But using -DHIGH_SLACK_LINE=256 described above will
+ work around this problem. [Dean Gaudet]
+
+ *) USE_FLOCK_SERIALIZED_ACCEPT is now default for FreeBSD, A/UX, and
+ SunOS 4.
+
+ *) Improved unix error response logging. [Marc Slemko]
+
+ *) Update mod_rewrite from 3.0.5 to 3.0.6. New ruleflag
+ QSA=query_string_append. Also fixed a nasty bug in per-dir context:
+ when a URL http://... was used in conjunction with a special
+ redirect flag, e.g. R=permanent, the permanent status was lost.
+ [Ronald Tschalaer <Ronald.Tschalaer@psi.ch>, Ralf S. Engelschall]
+
+ *) If an object has multiple variants that are otherwise equal Apache
+ would prefer the last listed variant rather than the first.
+ [Paul Sutton] PR#94
+
+ *) "make clean" at the top level now removes *.o. [Dean Gaudet] PR#752
+
+ *) mod_status dumps core in inetd mode. [Marc Slemko and Roy Fielding]
+ PR#566
+
+ *) pregsub had an off-by-1 in its error checking code. [Alexei Kosut]
+
+ *) PORT: fix rlim_t problems with AIX 4.2. [Marc Slemko] PR#333
+
+ *) PORT: Update UnixWare support for 2.1.2.
+ [Lawrence Rosenman <ler@lerctr.org>] PR#511
+
+ *) PORT: NonStop-UX [Joachim Schmitz <schmitz_joachim@tandem.com>] PR#327
+
+ *) PORT: Update ConvexOS support for 11.5.
+ [David DeSimone <fox@convex.com>] PR#399
+
+ *) PORT: Support for DEC cc compiler under ULTRIX.
+ ["P. Alejandro Lopez-Valencia" <alejolo@ideam.gov.co>] PR#388
+
+ *) PORT: Support for Maxion/OS SVR4.2 Real Time Unix. [no name given] PR#383
+
+ *) PORT: Workaround for AIX 3.x compiler bug in http_bprintf.c.
+ [Marc Slemko] PR#725
+
+ *) PORT: fix problem compiling http_bprintf.c with gcc under SCO
+ [Marc Slemko] PR#695
+
+Changes with Apache 1.2
+
+Changes with Apache 1.2b11
+
+ *) Fixed open timestamp fd in proxy_cache.c [Chuck Murcko]
+
+ *) Added undocumented perl SSI mechanism for -DUSE_PERL_SSI and mod_perl.
+ [Doug MacEachern, Rob Hartill]
+
+ *) Proxy needs to use hard_timeout instead of soft_timeout when it is
+ reading from one buffer and writing to another, at least until it has
+ a custom timeout handler. [Roy Fielding and Petr Lampa]
+
+ *) Fixed problem on IRIX with servers hanging in IdentityCheck,
+ apparently due to a mismatch between sigaction and setjmp.
+ [Roy Fielding] PR#502
+
+ *) Log correct status code if we timeout before receiving a request (408)
+ or if we received a request-line that was too long to process (414).
+ [Ed Korthof and Roy Fielding] PR#601
+
+ *) Virtual hosts with the same ServerName, but on different ports, were
+ not being selected properly. [Ed Korthof]
+
+ *) Added code to return the requested IP address from proxy_host2addr()
+ if gethostbyaddr() fails due to reverse DNS lookup problems. Original
+ change submitted by Jozsef Hollosi <hollosi@sbcm.com>.
+ [Chuck Murcko] PR#614
+
+ *) If multiple requests on a single connection are used to retrieve
+ data from different virtual hosts, the virtual host list would be
+ scanned starting with the most recently used VH instead of the first,
+ causing most virtual hosts to be ignored.
+ [Paul Sutton and Martin Mares] PR#610
+
+ *) The OS/2 handling of process group was broken by a porting patch for
+ MPE, so restored prior code for OS/2. [Roy Fielding and Garey Smiley]
+
+ *) Inherit virtual server port from main server if none (or "*") is
+ given for VirtualHost. [Dean Gaudet] PR#576
+
+ *) If the lookup for a DirectoryIndex name with content negotiation
+ has found matching variants, but none are acceptable, return the
+ negotiation result if there are no more DirectoryIndex names to lookup.
+ [Petr Lampa and Roy Fielding]
+
+ *) If a soft_timeout occurs after keepalive is set, then the main child
+ loop would try to read another request even though the connection
+ has been aborted. [Roy Fielding]
+
+ *) Configure changes: Allow for whitespace at the start of a
+ Module declaration. Also, be more understanding about the
+ CC=/OPTIM= format in Configuration. Finally, fix compiler
+ flags if using HP-UX's cc compiler. [Jim Jagielski]
+
+ *) Subrequests and internal redirects now inherit the_request from the
+ original request-line. [Roy Fielding]
+
+ *) Test for error conditions before creating output header fields, since
+ we don't want the error message to include those fields. Likewise,
+ reset the content_language(s) and content_encoding of the response
+ before generating or redirecting to an error message, since the new
+ message will have its own Content-* definitions. [Dean Gaudet]
+
+ *) Restored the semantics of headers_out (headers sent only with 200..299
+ and 304 responses) and err_headers_out (headers sent with all responses).
+ Avoid the overhead of copying tables if err_headers_out is empty
+ (the usual case). [Roy Fielding]
+
+ *) Fixed a couple places where a check for the default Content-Type was
+ not properly checking both the value configured by the DefaultType
+ directive and the DEFAULT_TYPE symbol in httpd.h. Changed the value
+ of DEFAULT_TYPE to match the documented default (text/plain).
+ [Dean Gaudet] PR#506
+
+ *) Escape the HTML-sensitive characters in the Request-URI that is
+ output for each child by mod_status. [Dean Gaudet and Ken Coar] PR#501
+
+ *) Properly initialize the flock structures used by the mutex locking
+ around accept() when USE_FCNTL_SERIALIZED_ACCEPT is defined.
+ [Marc Slemko]
+
+ *) The method for determining PATH_INFO has been restored to the pre-1.2b
+ (and NCSA httpd) definition wherein it was the extra path info beyond
+ the CGI script filename. The environment variable FILEPATH_INFO has
+ been removed, and instead we supply the original REQUEST_URI to any
+ script that wants to be Apache-specific and needs the real URI path.
+ This solves a problem with existing scripts that use extra path info
+ in the ScriptAlias directive to pass options to the CGI script.
+ [Roy Fielding]
+
+ *) The _default_ change in 1.2b10 will change the behaviour on configs
+ that use multiple Listen statements for listening on multiple ports.
+ But that change is necessary to make _default_ consistent with other
+ forms of <VirtualHost>. It requires such configs to be modified
+ to use <VirtualHost _default_:*>. The documentation has been
+ updated. [Dean Gaudet] PR#530
+
+ *) If an ErrorDocument CGI script is used to respond to an error
+ generated by another CGI script which has already read the message
+ body of the request, the server would block trying to read the
+ message body again. [Rob Hartill]
+
+ *) signal() replacement conflicted with a define on QNX (and potentially
+ other platforms). Fixed. [Ben Laurie] PR#512
+
+Changes with Apache 1.2b10
+
+ *) Allow HTTPD_ROOT, SERVER_CONFIG_FILE, DEFAULT_PATH, and SHELL_PATH
+ to be configured via -D in Configuration. [Dean Gaudet] PR#449
+
+ *) <VirtualHost _default_:portnum> didn't work properly. [Dean Gaudet]
+
+ *) Added prototype for mktemp() for SUNOS4 [Marc Slemko]
+
+ *) In mod_proxy.c, check return values for proxy_host2addr() when reading
+ config, in case the hostent struct returned is trash.
+ [Chuck Murcko] PR #491
+
+ *) Fixed the fix in 1.2b9 for parsing URL query info into args for CGI
+ scripts. [Dean Gaudet, Roy Fielding, Marc Slemko]
+
+Changes with Apache 1.2b9 [never announced]
+
+ *) Reset the MODULE_MAGIC_NUMBER to account for the unsigned port
+ changes and in anticipation of 1.2 final release. [Roy Fielding]
+
+ *) Fix problem with scripts not receiving a SIGPIPE when client drops
+ the connection (e.g., when user presses Stop). Apache will now stop
+ trying to send a message body immediately after an error from write.
+ [Roy Fielding and Nathan Kurz] PR#335
+
+ *) Rearrange Configuration.tmpl so that mod_rewrite has higher priority
+ than mod_alias, and mod_alias has higher priority than mod_proxy;
+ rearranged other modules to enhance understanding of their purpose
+ and relative order (and maybe even reduce some overhead).
+ [Roy Fielding and Sameer Parekh]
+
+ *) Fix graceful restart. Eliminate many signal-related race
+ conditions in both forms of restart, and in SIGTERM. See
+ htdocs/manual/stopping.html for details on stopping and
+ restarting the parent. [Dean Gaudet]
+
+ *) Fix memory leaks in mod_rewrite, mod_browser, mod_include. Tune
+ memory allocator to avoid a behaviour that required extra blocks to
+ be allocated. [Dean Gaudet]
+
+ *) Allow suexec to access files relative to current directory but not
+ above. (Excluding leading / or any .. directory.) [Ken Coar]
+ PR#269, 319, 395
+
+ *) Fix suexec segfault when group doesn't exist. [Gregory Neil Shapiro]
+ PR#367, 368, 354, 453
+
+ *) Fix the above fix: if suexec is enabled, avoid destroying r->url
+ while obtaining the /~user and save the username in a separate data
+ area so that it won't be overwritten by the call to getgrgid(), and
+ fix some misuse of the pool string allocation functions. Also fixes
+ a general problem with parsing URL query info into args for CGI scripts.
+ [Roy Fielding] PR#339, 367, 354, 453
+
+ *) Fix IRIX warning about bzero undefined. [Marc Slemko]
+
+ *) Fix problem with <Directory proxy:...>. [Martin Kraemer] PR#271
+
+ *) Corrected spelling of "authoritative". AuthDBAuthoratative became
+ AuthDBAuthoritative. [Marc Slemko] PR#420
+
+ *) MaxClients should be at least 1. [Lars Eilebrecht] PR#375
+
+ *) The default handler now logs invalid methods or URIs (i.e. PUT on an
+ object that can't be PUT, or FOOBAR for some method FOOBAR that
+ apache doesn't know about at all). Log 404s that occur in mod_include.
+ [Paul Sutton, John Van Essen]
+
+ *) If a soft timeout (or lingerout) occurs while trying to flush a
+ buffer or write inside buff.c or fread'ing from a CGI's output,
+ then the timeout would be ignored. [Roy Fielding] PR#373
+
+ *) Work around a bug in Netscape Navigator versions 2.x, 3.x and 4.0b2's
+ parsing of headers. If the terminating empty-line CRLF occurs starting
+ at the 256th or 257th byte of output, then Navigator will think a normal
+ image is invalid. We are guessing that this is because their initial
+ read of a new request uses a 256 byte buffer. We check the bytes written
+ so far and, if we are about to tickle the bug, we instead insert a
+ padding header of eminent bogosity. [Roy Fielding and Dean Gaudet] PR#232
+
+ *) Fixed SIGSEGV problem when a DirectoryIndex file is also the source
+ of an external redirection. [Roy Fielding and Paul Sutton]
+
+ *) Configure would create a broken Makefile if the configuration file
+ contained a commented-out Rule. [Roy Fielding]
+
+ *) Promote per_dir_config and subprocess_env from the subrequest to the
+ main request in mod_negotiation. In particular this fixes a bug
+ where <Files> sections wouldn't properly apply to negotiated content.
+ [Dean Gaudet]
+
+ *) Fix a potential deadlock in mod_cgi script_err handling.
+ [Ralf S. Engelschall]
+
+ *) rotatelogs zero-pads the logfile names to improve alphabetic sorting.
+ [Mitchell Blank Jr]
+
+ *) Updated mod_rewrite to 3.0.4: Fixes HTTP redirects from within
+ .htaccess files because the RewriteBase was not replaced correctly.
+ Updated mod_rewrite to 3.0.5: Fixes problem with rewriting inside
+ <Directory> sections missing a trailing /. [Ralf S. Engelschall]
+
+ *) Clean up Linux settings in conf.h by detecting 2.x versus 1.x. For
+ 1.x the settings are those of pre-1.2b8. For 2.x we include
+ USE_SHMGET_SCOREBOARD (scoreboard in shared memory rather than file) and
+ HAVE_SYS_RESOURCE_H (enable the RLimit commands).
+ [Dean Gaudet] PR#336, PR#340
+
+ *) Redirect did not preserve ?query_strings when present in the client's
+ request. [Dean Gaudet]
+
+ *) Configure was finding non-modules on EXTRA_LIBS. [Frank Cringle] PR#380
+
+ *) Use /bin/sh5 on ULTRIX. [P. Alejandro Lopez-Valencia] PR#369
+
+ *) Add UnixWare compile/install instructions. [Chuck Murcko]
+
+ *) Add mod_example (illustration of API techniques). [Ken Coar]
+
+ *) Add macro for memmove to conf.h for SUNOS4. [Marc Slemko]
+
+ *) Improve handling of directories when filenames have spaces in them.
+ [Chuck Murcko]
+
+ *) For hosts with multiple IP addresses, try all additional addresses if
+ necessary to get a connect. Fail only if hostent address list is
+ exhausted. [Chuck Murcko]
+
+ *) More signed/unsigned port fixes. [Dean Gaudet]
+
+ *) HARD_SERVER_LIMIT can be defined in the Configuration file now.
+ [Dean Gaudet]
+
+Changes with Apache 1.2b8
+
+ *) suexec.c doesn't close the log file, allowing CGIs to continue writing
+ to it. [Marc Slemko]
+
+ *) The addition of <Location> and <File> directives made the
+ sub_req_lookup_simple() function bogus, so we now handle
+ the special cases directly. [Dean Gaudet]
+
+ *) We now try to log where the server is dumping core when a fatal
+ signal is received. [Ken Coar]
+
+ *) Improved lingering_close by adding a special timeout, removing the
+ spurious log messages, removing the nonblocking settings (they
+ are not needed with the better timeout), and adding commentary
+ about the NO_LINGCLOSE and USE_SO_LINGER issues. NO_LINGCLOSE is
+ now the default for SunOS4, UnixWare, NeXT, and IRIX. [Roy Fielding]
+
+ *) Send error messages about setsockopt failures to the server error
+ log instead of stderr. [Roy Fielding]
+
+ *) Fix loopholes in proxy cache expiry vis a vis alarms. [Brian Moore]
+
+ *) Stopgap solution for CGI 3-second delay with server-side includes: if
+ processing a subrequest, allocate memory from r->main->pool instead
+ of r->pool so that we can avoid waiting for free_proc_chain to cleanup
+ in the middle of an SSI request. [Dean Gaudet] PR #122
+
+ *) Fixed status of response when POST is received for a nonexistent URL
+ (was sending 405, now 404) and when any method is sent with a
+ full-URI that doesn't match the server and the server is not acting
+ as a proxy (was sending 501, now 403). [Roy Fielding]
+
+ *) Host port changed to unsigned short. [Ken Coar] PR #276
+
+ *) Fix typo in command definition of AuthAuthoritative. [Ken Coar] PR #246
+
+ *) Defined USE_SHMGET_SCOREBOARD for shared memory on Linux. [Dean Gaudet]
+
+ *) Report extra info from errno with many errors that cause httpd to exit.
+ spawn_child, popenf, and pclosef now have valid errno returns in the
+ event of an error. Correct problems where errno was stomped on
+ before being reported. [Dean Gaudet]
+
+ *) In the proxy, if the cache filesystem was full, garbage_coll() was
+ never called, and thus the filesystem would remain full indefinitely.
+ We now also remove incomplete cache files left if the origin server
+ didn't send a Content-Length header and either the client has aborted
+ transfer or bwrite() to client has failed. [Petr Lampa]
+
+ *) Fixed the handling of module and script-added header fields.
+ Improved the interface for sending header fields and reduced
+ the duplication of code between sending okay responses and errors.
+ We now always send both headers_out and err_headers_out, and
+ ensure that the server-reserved fields are not being overridden,
+ while not overriding those that are not reserved. [Roy Fielding]
+
+ *) Moved transparent content negotiation fields to err_headers_out
+ to reflect above changes. [Petr Lampa]
+
+ *) Fixed the determination of whether or not we should make the
+ connection persistent for all of the cases where some other part
+ of the server has already indicated that we should not. Also
+ improved the ordering of the test so that chunked encoding will
+ be set whenever it is desired instead of only when KeepAlive
+ is enabled. Added persistent connection capability for most error
+ responses (those that do not indicate a bad input stream) when
+ accessed by an HTTP/1.1 client. [Roy Fielding]
+
+ *) Added missing timeouts for sending header fields, error responses,
+ and the last chunk of chunked encoding, each of which could have
+ resulted in a process being stuck in write forever. Using soft_timeout
+ requires that the sender check for an aborted connection rather than
+ continuing after an EINTR. Timeouts that used to be initiated before
+ send_http_header (and never killed) are now initiated only within or
+ around the routines that actually do the sending, and not allowed to
+ propagate above the caller. [Roy Fielding]
+
+ *) mod_auth_anon required an @ or a . in the email address, not both.
+ [Dirk vanGulik]
+
+ *) per_dir_defaults weren't set correctly until directory_walk for
+ name-based vhosts. This fixes an obscure bug with the wrong config
+ info being used for vhosts that share the same ip as the server.
+ [Dean Gaudet]
+
+ *) Improved generation of modules/Makefile to be more generic for
+ new module directories. [Ken Coar, Chuck Murcko, Roy Fielding]
+
+ *) Generate makefile dependency for Configuration based on the actual
+ name given when running the Configure process. [Dean Gaudet]
+
+ *) Fixed problem with vhost error log not being set prior to
+ initializing virtual hosts. [Dean Gaudet]
+
+ *) Fixed infinite loop when a trailing slash is included after a type map
+ file URL (extra path info). [Petr Lampa]
+
+ *) Fixed server status updating of per-connection counters. [Roy Fielding]
+
+ *) Add documentation for DNS issues (reliability and security), and try
+ to explain the virtual host matching process. [Dean Gaudet]
+
+ *) Try to continue gracefully by disabling the vhost if a DNS lookup
+ fails while parsing the configuration file. [Dean Gaudet]
+
+ *) Improved calls to setsockopt. [Roy Fielding]
+
+ *) Negotiation changes: Don't output empty content-type in variant list;
+ Output charset in variant list; Return sooner from handle_multi() if
+ no variants found; Add handling of '*' wildcard in Accept-Charset.
+ [Petr Lampa and Paul Sutton]
+
+ *) Fixed overlaying of request/sub-request notes and headers in
+ mod_negotiation. [Dean Gaudet]
+
+ *) If two variants' charset quality are equal and one is the default
+ charset (iso-8859-1), then prefer the variant that was specifically
+ listed in Accept-Charset instead of the default. [Petr Lampa]
+
+ *) Memory allocation problem in push_array() -- it would corrupt memory
+ when nalloc==0. [Kai Risku <krisku@tf.hut.fi> and Roy Fielding]
+
+ *) invoke_handler() doesn't handle mime arguments in content-type
+ [Petr Lampa] PR#160
+
+ *) Reduced IdentityCheck timeout to 30 seconds, as per RFC 1413 minimum.
+ [Ken Coar]
+
+ *) Fixed problem with ErrorDocument not working for virtual hosts
+ due to one of the performance changes in 1.2b7. [Dean Gaudet]
+
+ *) Log an error message if we get a request header that is too long,
+ since it may indicate a buffer overflow attack. [Marc Slemko]
+
+ *) Made is_url() allow "[-.+a-zA-Z0-9]+:" as a valid scheme and
+ not reject URLs without a double-slash, as per RFC2068 section 3.2.
+ [Ken Coar] PR #146, #187
+
+ *) Added table entry placeholder for new header_parser callback
+ in all of the distributed modules. [Ken Coar] PR #191
+
+ *) Allow for cgi files without the .EXE extension on them under OS/2.
+ [Garey Smiley] PR #59
+
+ *) Fixed error message when resource is not found and URL contains
+ path info. [Petr Lampa and Dean Gaudet] PR #40
+
+ *) Fixed user and server confusion over what should be a virtual host
+ and what is the main server, resulting in access to something
+ other than the name defined in the virtualhost directive (but
+ with the same IP address) failing. [Dean Gaudet]
+
+ *) Updated mod_rewrite to version 3.0.2, which: fixes compile error on
+ AIX; improves the redirection stuff to enable the users to generally
+ redirect to http, https, gopher and ftp; added TIME variable for
+ RewriteCond which expands to YYYYMMDDHHMMSS strings and added the
+ special patterns >STRING, <STRING and =STRING to RewriteCond, which
+ can be used in conjunction with %{TIME} or other variables to create
+ time-dependent rewriting rules. [Ralf S. Engelschall]
+
+ *) bpushfd() no longer notes cleanups for the file descriptors it is handed.
+ Module authors may need to adjust their code for proper cleanup to take
+ place (that is, call note_cleanups_for_fd()). This change fixes problems
+ with file descriptors being erroneously closed when the proxy module was
+ in use. [Ben Laurie]
+
+ *) Fix bug in suexec reintroduced by changes in 1.2b7 which allows
+ initgroups() to hose the group information needed for later
+ comparisons. [Randy Terbush]
+
+ *) Remove unnecessary call to va_end() in create_argv() which
+ caused a SEGV on some systems.
+
+ *) Use proper MAXHOSTNAMELEN symbol for limiting length of server name.
+ [Dean Gaudet]
+
+ *) Clear memory allocated for listeners. [Randy Terbush]
+
+ *) Improved handling of IP address as a virtualhost address and
+ introduced "_default_" as a synonym for the default vhost config.
+ [Dean Gaudet] PR #212
+
+Changes with Apache 1.2b7
+
+ *) Port to UXP/DS(V20) [Toshiaki Nomura <nom@yk.fujitsu.co.jp>]
+
+ *) unset Content-Length if chunked (RFC-2068) [Petr Lampa]
+
+ *) mod_negotiation fixes [Petr Lampa] PR#157, PR#158, PR#159
+ - replace protocol response numbers with symbols
+ - save variant-list into main request notes
+ - free allocated memory from subrequests
+ - merge notes, headers_out and err_headers_out
+
+ *) changed status check mask in proxy_http.c from "HTTP/#.# ### *" to
+ "HTTP/#.# ###*" to be more lenient about what we accept.
+ [Chuck Murcko]
+
+ *) more proxy FTP bug fixes:
+ - Changed send_dir() to remove user/passwd from displayed URL.
+ - Changed login error messages to be more descriptive.
+ - remove setting of SO_DEBUG socket option
+ - Make ftp_getrc() more lenient about multiline responses,
+ specifically, 230 responses which don't have continuation 230-
+ on each line). These seem to be all NT FTP servers, and while
+ perhaps questionable, they appear to be legal by RFC 959.
+ - Add missing kill_timeout() after transfer to user completes.
+ [Chuck Murcko]
+
+ *) Fixed problem where a busy server could hang when restarting
+ after being sent a SIGHUP due to child processes not exiting.
+ [Marc Slemko]
+
+ *) Modify mod_include escaping so a '\' only signifies an escaped
+ character if the next character is one that needs
+ escaping. [Ben Laurie]
+
+ *) Eliminated possible infinite loop in mod_imap when relative URLs are
+ used with a 'base' directive that does not have a '/' in it.
+ [Marc Slemko, reported by Onno Witvliet <onno@tc.hsa.nl>]
+
+ *) Reduced the default timeout from 1200 seconds to 300, and the
+ one in the sample configfile from 400 to 300. [Marc Slemko]
+
+ *) Stop vbprintf from crashing if given a NULL string pointer;
+ print (null) instead. [Ken Coar]
+
+ *) Don't disable Nagle algorithm if system doesn't have TCP_NODELAY.
+ [Marc Slemko and Roy Fielding]
+
+ *) Fixed problem with mod_cgi-generated internal redirects trying to
+ read the request message-body twice. [Archie Cobbs and Roy Fielding]
+
+ *) Reduced timeout on lingering close, removed possibility of a blocked
+ read causing the child to hang, and stopped logging of errors if
+ the socket is not connected (reset by client). [Roy Fielding]
+
+ *) Rearranged main child loop to remove duplication of code in
+ select/accept and keep-alive requests, fixed several bugs regarding
+ checking scoreboard_image for exit indication and failure to
+ account for all success conditions and trap all error conditions,
+ prevented multiple flushes before closing the socket; close the entire
+ socket buffer instead of just one descriptor, prevent logging of
+ EPROTO and ECONNABORTED on platforms where supported, and generally
+ improved readability. [Roy Fielding]
+
+ *) Extensive performance improvements. Cleaned up inefficient use of
+ auto initializers, multiple is_matchexp calls on a static string,
+ and excessive merging of response_code_strings. [Dean Gaudet]
+
+ *) Added double-buffering to mod_include to improve performance on
+ server-side includes. [Marc Slemko]
+
+ *) Several fixes for suexec wrapper. [Randy Terbush]
+ - Make wrapper work for files on NFS filesystem.
+ - Fix portability problem of MAXPATHLEN.
+ - Fix array overrun problem in clean_env().
+ - Fix allocation of PATH environment variable
+
+ *) Removed extraneous blank line is description of mod_status chars.
+ [Kurt Kohler]
+
+ *) Logging of errors from the call_exec routine simply went nowhere,
+ since the logfile fd has been closed, so now we send them to stderr.
+ [Harald T. Alvestrand]
+
+ *) Fixed core dump when DocumentRoot is a CGI.
+ [Ben Laurie, reported by geddis@tesserae.com]
+
+ *) Fixed potential file descriptor leak in mod_asis; updated it and
+ http_core to use pfopen/pfclose instead of fopen/fclose.
+ [Randy Terbush and Roy Fielding]
+
+ *) Fixed handling of unsigned ints in ap_snprintf() on some chips such
+ as the DEC Alpha which is 64-bit but uses 32-bit ints.
+ [Dean Gaudet and Ken Coar]
+
+ *) Return a 302 response code to the client when sending a redirect
+ due to a missing trailing '/' on a directory instead of a 301; now
+ it is cacheable. [Markus Gyger]
+
+ *) Fix condition where, if a bad directive occurs in .htaccess, and
+ sub_request() goes first to this directory, then log_reason() will
+ SIGSEGV because it doesn't have initialized r->per_dir_config.
+ [PR#162 from Petr Lampa, fix by Marc Slemko and Dean Gaudet]
+
+ *) Fix handling of lang_index in is_variant_better(). This was
+ causing problems which resulted in the server sending the
+ wrong language document in some cases. [Petr Lampa]
+
+ *) Remove free() from clean_env() in suexec wrapper. This was nuking
+ the clean environment on some systems.
+
+ *) Tweak byteserving code (e.g. serving PDF files) to work around
+ bugs in Netscape Navigator and Microsoft Internet Explorer.
+ Emit Content-Length header when sending multipart/byteranges.
+ [Alexei Kosut]
+
+ *) Port to HI-UX/WE2. [Nick Maclaren]
+
+ *) Port to HP MPE operating system for HP 3000 machines
+ [Mark Bixby <markb@cccd.edu>]
+
+ *) Fixed bug which caused a segmentation fault if only one argument
+ given to RLimit* directives. [Ed Korthof]
+
+ *) Continue persistent connection after 204 or 304 response. [Dean Gaudet]
+
+ *) Improved buffered output to the client by delaying the flush decision
+ until the BUFF code is actually about to read the next request.
+ This fixes a problem introduced in 1.2b5 with clients that send
+ an extra CRLF after a POST request. Also improved chunked output
+ performance by combining writes using writev() and removing as
+ many bflush() calls as possible. NOTE: Platforms without writev()
+ must add -DNO_WRITEV to the compiler CFLAGS, either in Configuration
+ or Configure, unless we have already done so. [Dean Gaudet]
+
+ *) Fixed mod_rewrite bug which truncated the rewritten URL [Marc Slemko]
+
+ *) Fixed mod_info output corruption bug introduced by buffer overflow
+ fixes. [Dean Gaudet]
+
+ *) Fixed http_protocol to correctly output all HTTP/1.1 headers, including
+ for the special case of a 304 response. [Paul Sutton]
+
+ *) Improved handling of TRACE method by bypassing normal method handling
+ and header parsing routines; fixed Allow response to always allow TRACE.
+ [Dean Gaudet]
+
+ *) Fixed compiler warnings in the regex library. [Dean Gaudet]
+
+ *) Cleaned-up some of the generated HTML. [Ken Coar]
+
+Changes with Apache 1.2b6
+
+ *) Allow whitespace in imagemap mapfile coordinates. [Marc Slemko]
+
+ *) Fix typo introduced in fix for potential infinite loop around
+ accept() in child_main(). This change caused the rev to 1.2b6.
+ 1.2b5 was never a public beta.
+
+Changes with Apache 1.2b5
+
+ *) Change KeepAlive semantics (On|Off instead of a number), add
+ MaxKeepAliveRequests directive. [Alexei Kosut]
+
+ *) Various NeXT compilation patches, as well as a change in
+ regex/regcomp.c since that file also used a NEXT define.
+ [Andreas Koenig]
+
+ *) Allow * to terminate the end of a directory match in mod_dir.
+ Allows /~* to match for both /~joe and /~joe/. [David Bronder]
+
+ *) Don't call can_exec() if suexec_enabled. Calling this requires
+ scripts executed by the suexec wrapper to be world executable, which
+ defeats one of the advantages of running the wrapper. [Randy Terbush]
+
+ *) Portability Fix: IRIX complained with 'make clean' about *pure* (removed)
+ [Jim Jagielski]
+
+ *) Migration from sprintf() to snprintf() to avoid buffer
+ overflows. [Marc Slemko]
+
+ *) Provide portable snprintf() implementation (ap_snprintf)
+ as well as *cvt family. [Jim Jagielski]
+
+ *) Portability Fix: NeXT lacks unistd.h so we wrap it's inclusion
+ [Jim Jagielski]
+
+ *) Remove mod_fastcgi.c from the distribution. This module appears
+ to be maintained more through the Open Market channels and should
+ continue to be easily available at http://www.fastcgi.com/
+
+ *) Fixed bug in modules/Makefile that wouldn't allow building in more
+ than one subdirectory (or cleaning, either). [Jeremy Laidman]
+
+ *) mod_info assumed that the config files were relative to ServerRoot.
+ [Ken the Rodent]
+
+ *) CGI scripts called as an error document resulting from failed
+ CGI execution would hang waiting for POST'ed data. [Rob Hartill]
+
+ *) Log reason when mod_dir returns access HTTP_FORBIDDEN
+ [Ken the Rodent]
+
+ *) Properly check errno to prevent display of a directory index
+ when server receives a long enough URL to confuse stat().
+ [Marc Slemko]
+
+ *) Several security enhancements to suexec wrapper. It is _highly_
+ recommended that previously installed versions of the wrapper
+ be replaced with this version. [Randy Terbush, Jason Dour]
+
+ - ~user execution now properly restricted to ~user's home
+ directory and below.
+ - execution restricted to UID/GID > 100
+ - restrict passed environment to known variables
+ - call setgid() before initgroups() (portability fix)
+ - remove use of setenv() (portability fix)
+
+ *) Add HTTP/1.0 response forcing. [Ben Laurie]
+
+ *) Add access control via environment variables. [Ben Laurie]
+
+ *) Add rflush() function. [Alexei Kosut]
+
+ *) remove duplicate pcalloc() call in new_connection().
+
+ *) Fix incorrect comparison which could allow number of children =
+ MaxClients + 1 if less than HARD_SERVER_LIMIT. Also fix potential
+ problem if StartServers > HARD_SERVER_LIMIT. [Ed Korthof]
+
+ *) Updated support for OSes (MachTen, ULTRIX, Paragon, ISC, OpenBSD
+ AIX PS/2, CONVEXOS. [Jim Jagielski]
+
+ *) Replace instances of inet_ntoa() with inet_addr() for ProxyBlock.
+ It's more portable. [Martin Kraemer]
+
+ *) Replace references to make in Makefile.tmpl with $(MAKE).
+ [Chuck Murcko]
+
+ *) Add ProxyBlock directive w/IP address caching. Add IP address
+ caching to NoCache directive as well. ProxyBlock works with all
+ handlers; NoCache now also works with FTP for anonymous logins.
+ Still more code cleanup. [Chuck Murcko]
+
+ *) Add "header parse" API hook [Ben Laurie]
+
+ *) Fix byte ordering problems for REMOTE_PORT [Chuck Murcko]
+
+ *) suEXEC wrapper was freeing memory that had not been malloc'ed.
+
+ *) Correctly allow access and auth directives in <Files> sections in
+ server config files. [Alexei Kosut]
+
+ *) Fix bug with ServerPath that could cause certain files to be not
+ found by the server. [Alexei Kosut]
+
+ *) Fix handling of ErrorDocument so that it doesn't remove a trailing
+ double-quote from text and so that it properly checks for unsupported
+ status codes using the new index_of_response interface. [Roy Fielding]
+
+ *) Multiple fixes to the lingering_close code in order to avoid being
+ interrupted by a stray timeout, to avoid lingering on a connection
+ that has already been aborted or never really existed, to ensure that
+ we stop lingering as soon as any error condition is received, and to
+ prevent being stuck indefinitely if the read blocks. Also improves
+ reporting of error conditions. [Marc Slemko and Roy Fielding]
+
+ *) Fixed initialization of parameter structure for sigaction.
+ [mgyger@itr.ch, Adrian Filipi-Martin]
+
+ *) Fixed reinitializing the parameters before each call to accept and
+ select, and removed potential for infinite loop in accept.
+ [Roy Fielding, after useful PR from adrian@virginia.edu]
+
+ *) Fixed condition where, if a child fails to fork, the scoreboard would
+ continue to say SERVER_STARTING forever. Eventually, the main process
+ would refuse to start new children because count_idle_servers() will
+ count those SERVER_STARTING entries and will always report that there
+ are enough idle servers. [Phillip Vandry]
+
+ *) Fixed bug in bcwrite regarding failure to account for partial writes.
+ Avoided calling bflush() when the client is pipelining requests.
+ Removed unnecessary flushes from http_protocol. [Dean Gaudet]
+
+ *) Added description of "." mode in server-status [Jim Jagielski]
+
+Changes with Apache 1.2b4
+
+ *) Fix possible race condition in accept_mutex_init() that
+ could leave a small security hole open allowing files to be
+ overwritten in cases where the server UID has write permissions.
+ [Marc Slemko]
+
+ *) Fix awk compatibilty problem in Configure. [Jim Jagielski]
+
+ *) Fix portablity problem in util_script where ARG_MAX may not be
+ defined for some systems.
+
+ *) Add changes to allow compilation on Machten 4.0.3 for PowerPC.
+ [Randal Schwartz]
+
+ *) OS/2 changes to support an MMAP style scoreboard file and UNIX
+ style magic #! token for better script portability. [Garey Smiley]
+
+ *) Fix bug in suexec wrapper introduced in b3 that would cause failed
+ execution for ~userdir CGI. [Jason Dour]
+
+ *) Fix initgroups() business in suexec wrapper. [Jason Dour]
+
+ *) Fix month off by one in suexec wrapper logging.
+
+Changes with Apache 1.2b3:
+
+ *) Fix error in mod_cgi which could cause resources not to be properly
+ freed, or worse. [Dean Gaudet]
+
+ *) Fix find_string() NULL pointer dereference. [Howard Fear]
+
+ *) Add set_flag_slot() at the request of Dirk and others.
+ [Dirk vanGulik]
+
+ *) Sync mod_rewrite with patch level 10. [Ralf Engelschall]
+
+ *) Add changes to improve the error message given for invalid
+ ServerName parameters. [Dirk vanGulik]
+
+ *) Add "Authoritative" directive for Auth modules that don't
+ currently have it. This gives admin control to assign authoritative
+ control to an authentication scheme and allow "fall through" for
+ those authentication modules that aren't "Authoritative" thereby
+ allowing multiple authentication mechanisms to be chained.
+ [Dirk vanGulik]
+
+ *) Remove requirement for ResourceConfig/AccessConfig if not using
+ the three config file layout. [Randy Terbush]
+
+ *) Add PASV mode to mod_proxy FTP handler. [Chuck Murcko]
+
+ *) Changes to suexec wrapper to fix the following problems:
+ 1. symlinked homedirs will kill ~userdirs.
+ 2. initgroups() on Linux 2.0.x clobbers gr->grid.
+ 3. CGI command lines paramters problems
+ 4. pw-pwdir for "docroot check" still the httpd user's pw record.
+ [Randy Terbush, Jason Dour]
+
+ *) Change create_argv() to accept variable arguments. This fixes
+ a problem where arguments were not getting passed to the CGI via
+ argv[] when the suexec wrapper was active. [Randy Terbush, Jake Buchholz]
+
+ *) Collapse multiple slashes in path URLs to properly apply
+ handlers defined by <Location>. [Alexei Kosut]
+
+ *) Define a sane set of DEFAULT_USER and DEFAULT_GROUP values for AIX.
+
+ *) Improve the accuracy of request duration timings by setting
+ r->request_time in read_request_line() instead of read_request().
+ [Dean Gaudet]
+
+ *) Reset timeout while reading via get_client_block() in mod_cgi.c
+ Fixes problem with timed out transfers of large files. [Rasmus Lerdorf]
+
+ *) Add the ability to pass different Makefile.tmpl files to Configure
+ using the -make flag. [Rob Hartill]
+
+ *) Fix coredump triggered when sending a SIGHUP to the server caused
+ by an assertion failure, in turn caused by an uninitialised field in a
+ listen_rec.
+ [Ben Laurie]
+
+ *) Add FILEPATH_INFO variable to CGI environment, which is equal to
+ PATH_INFO from previous versions of Apache (in certain situations,
+ Apache 1.2's PATH_INFO will be different than 1.1's). [Alexei Kosut]
+ [later removed in 1.2b11]
+
+ *) Add rwrite() function to API to allow for sending strings of
+ arbitrary length. [Doug MacEachern]
+
+ *) Remove rlim_t typedef for NetBSD. Do older versions need this?
+
+ *) Defined rlim_t and WANTHSREGEX=yes and fixed waitpid() substitute for
+ NeXT. [Jim Jagielski]
+
+ *) Removed recent modification to promote the status code on internal
+ redirects, since the correct fix was to change the default log format
+ in mod_log_config so that it outputs the original status. [Rob Hartill]
+
+Changes with Apache 1.2b2:
+
+ *) Update set_signals() to use sigaction() for setting handlers.
+ This appears to fix a re-entrant problem in the seg_fault()
+ bus_error() handlers. [Randy Terbush]
+
+ *) Changes to allow mod_status compile for OS/2 [Garey Smiley]
+
+ *) changes for DEC AXP running OSF/1 v3.0. [Marc Evans]
+
+ *) proxy_http.c bugfixes: [Chuck Murcko]
+ 1) fixes possible NULL pointer reference w/NoCache
+ 2) fixes NoCache behavior when using ProxyRemote (ProxyRemote
+ host would cache nothing if it was in the local domain,
+ and the local domain was in the NoCache list)
+ 3) Adds Host: header when not available
+ 4) Some code cleanup and clarification
+
+ *) mod_include.c bugfixes:
+ 1) Fixed an ommission that caused include variables to not
+ be parsed in config errmsg directives [Howard Fear]
+ 2) Remove HAVE_POSIX_REGEX cruft [Alexei Kosut]
+ 3) Patch to fix compiler warnings [perrot@lal.in2p3.fr]
+ 4) Allow backslash-escaping to all quoted text
+ [Ben Yoshino <ben@wiliki.eng.hawaii.edu>]
+ 5) Pass variable to command line if not set in XSSI's env
+ [Howard Fear]
+
+ *) Fix infinite loop when processing Content-language lines in
+ type-map files. [Alexei Kosut]
+
+ *) Closed file-globbing hole in test-cgi script. [Brian Behlendorf]
+
+ *) Fixed problem in set_[user|group] that prevented CGI execution
+ for non-virtualhosts when suEXEC was enabled. [Randy Terbush]
+
+ *) Added PORTING information file. [Jim Jagielski]
+
+ *) Added definitions for S_IWGRP and S_IWOTH to conf.h [Ben Laurie]
+
+ *) Changed default group to "nogroup" instead of "nobody" [Randy Terbush]
+
+ *) Fixed define typo of FCNTL_SERIALIZED_ACCEPT where
+ USE_FCNTL_SERIALIZED_ACCEPT was intended.
+
+ *) Fixed additional uses of 0xffffffff where INADDR_NONE was intended,
+ which caused problems of systems where socket s_addr is >32bits.
+
+ *) Added comment to explain (r->chunked = 1) side-effect in
+ http_protocol.c [Roy Fielding]
+
+ *) Replaced use of index() in mod_expires.c with more appropriate
+ and portable isdigit() test. [Ben Laurie]
+
+ *) Updated Configure for ...
+ OS/2 (DEF_WANTHSREGEX=yes, other code changes)
+ *-dg-dgux* (bad pattern match)
+ QNX (DEF_WANTHSREGEX=yes)
+ *-sunos4* (DEF_WANTHSREGEX=yes, -DUSEBCOPY)
+ *-ultrix (new)
+ *-unixware211 (new)
+ and added some user diagnostic info. [Ben Laurie]
+
+ *) In helpers/CutRule, replaced "cut" invocation with "awk" invocation
+ for better portability. [Jim Jagielski]
+
+ *) Updated helpers/GuessOS for ...
+ SCO 5 (recognize minor releases)
+ SCO UnixWare (braindamaged uname, whatever-whatever-unixware2)
+ SCO UnixWare 2.1.1 (requires a separate set of #defines in conf.h)
+ IRIX64 (-sgi-irix64)
+ ULTRIX (-unknown-ultrix)
+ SINIX (-whatever-sysv4)
+ NCR Unix (-ncr-sysv4)
+ and fixed something in helpers/PrintPath [Ben Laurie]
+
+Changes with Apache 1.2b1
+
+ *) Not listed. See <http://www.apache.org/docs/new_features_1_2.html>
+
+Changes with Apache 1.1.1
+
+ *) Fixed bug where Cookie module would make two entries in the
+ logfile for each access [Mark Cox]
+
+ *) Fixed bug where Redirect in .htaccess files would cause memory
+ leak. [Nathan Neulinger]
+
+ *) MultiViews now works correctly with AddHandler [Alexei Kosut]
+
+ *) Problems with mod_auth_msql fixed [Dirk vanGulik]
+
+ *) Fix misspelling of "Anonymous_Authorative" directive in mod_auth_anon.
+
+Changes with Apache 1.1.0
+
+ *) Bring NeXT support up to date. [Takaaki Matsumoto]
+
+ *) Bring QNX support up to date. [Ben Laurie]
+
+ *) Make virtual hosts default to main server keepalive parameters.
+ [Alexei Kosut, Ben Laurie]
+
+ *) Allow ScanHTMLTitles to work with lowercase <title> tags. [Alexei Kosut]
+
+ *) Fix missing address family for connect, also remove unreachable statement
+ in mod_proxy. [Ben Laurie]
+
+ *) mod_env now turned on by default in Configuration.tmpl.
+
+ *) Bugs which were fixed:
+ a) yet more mod_proxy bugs [Ben Laurie]
+ b) CGI works again with inetd [Alexei Kosut]
+ c) Leading colons were stripped from passwords [osm@interguide.com]
+ d) Another fix to multi-method Limit problem [jk@tools.de]
+
+Changes with Apache 1.1b4
+
+ *) r->bytes_sent variable restored. [Robert Thau]
+
+ *) Previously broken multi-method <Limit> parsing fixed. [Robert Thau]
+
+ *) More possibly unsecure programs removed from the support directory.
+
+ *) More mod_auth_msql authentication improvements.
+
+ *) VirtualHosts based on Host: headers no longer conflict with the
+ Listen directive.
+
+ *) OS/2 compatibility enhancements. [Gary Smiley]
+
+ *) POST now allowed to directory index CGI scripts.
+
+ *) Actions now work with files of the default type.
+
+ *) Bugs which were fixed:
+ a) more mod_proxy bugs
+ b) early termination of inetd requests
+ c) compile warnings on several systems
+ d) problems when scripts stop reading output early
+
+Changes with Apache 1.1b3
+
+ *) Much of cgi-bin and all of cgi-src has been removed, due to
+ various security holes found and that we could no longer support
+ them.
+
+ *) The "Set-Cookie" header is now special-cased to not merge multiple
+ instances, since certain popular browsers can not handle multiple
+ Set-Cookie instructions in a single header. [Paul Sutton]
+
+ *) rprintf() added to buffer code, occurrences of sprintf removed.
+ [Ben Laurie]
+
+ *) CONNECT method for proxy module, which means tunneling SSL should work.
+ (No crypto needed) Also a NoCache config directive.
+
+ *) Several API additions: pstrndup(), table_unset() and get_token()
+ functions now available to modules.
+
+ *) mod_imap fixups, in particular Location: headers are now complete
+ URL's.
+
+ *) New "info" module which reports on installed module set through a
+ special URL, a la mod_status.
+
+ *) "ServerPath" directive added - allows for graceful transition
+ for Host:-header-based virtual hosts.
+
+ *) Anonymous authentication module improvements.
+
+ *) MSQL authentication module improvements.
+
+ *) Status module design improved - output now table-based. [Ben Laurie]
+
+ *) htdigest utility included for use with digest authentication
+ module.
+
+ *) mod_negotiation: Accept values with wildcards to be treated with
+ less priority than those without wildcards at the same quality
+ value. [Alexei Kosut]
+
+ *) Bugs which were fixed:
+ a) numerous mod_proxy bugs
+ b) CGI early-termination bug [Ben Laurie]
+ c) Keepalives not working with virtual hosts
+ d) RefererIgnore problems
+ e) closing fd's twice in mod_include (causing core dumps on
+ Linux and elsewhere).
+
+Changes with Apache 1.1b2
+
+ *) Bugfixes:
+ a) core dumps in mod_digest
+ b) truncated hostnames/ip address in the logs
+ c) relative URL's in mod_imap map files
+
+Changes with Apache 1.1b1
+
+ *) Not listed. See <http://www.apache.org/docs/new_features_1_1.html>
+
+Changes with Apache 1.0.3
+
+ *) Internal redirects which occur in mod_dir.c now preserve the
+ query portion of a request (the bit after the question mark).
+ [Adam Sussman]
+
+ *) Escape active characters '<', '>' and '&' in html output in
+ directory listings, error messages and redirection links.
+ [David Robinson]
+
+ *) Apache will now work with LynxOS 2.3 and later [Steven Watt]
+
+ *) Fix for POSIX compliance in waiting for processes in alloc.c.
+ [Nick Williams]
+
+ *) setsockopt no longer takes a const declared argument [Martijn Koster]
+
+ *) Reset timeout timer after each successful fwrite() to the network.
+ This patch adds a reset_timeout() procedure that is called by
+ send_fd() to reset the timeout ever time data is written to the net.
+ [Nathan Schrenk]
+
+ *) timeout() signal handler now checks for SIGPIPE and reports
+ lost connections in a more user friendly way. [Rob Hartill]
+
+ *) Location of the "scoreboard" file which used to live in /tmp is
+ now configurable (for OSes that can't use mmap) via ScoreBoardFile
+ which works similar to PidFile (in httpd.conf) [Rob Hartill]
+
+ *) Include sys/resource.h in the correct place for SunOS4 [Sameer Parekh]
+
+ *) the pstrcat call in mod_cookies.c didn't have an ending NULL,
+ which caused a SEGV with cookies enabled
+
+ *) Output warning when MinSpareServers is set to <= 0 and change it to 1
+ [Rob Hartill]
+
+ *) Log the UNIX textual error returned by some system calls, in
+ particular errors from accept() [David Robinson]
+
+ *) Add strerror function to util.c for SunOS4 [Randy Terbush]
+
+Changes with Apache 1.0.2
+
+ *) patch to get Apache compiled on UnixWare 2.x, recommended as
+ a temporary measure, pending rewrite of rfc931.c. [Chuck Murcko]
+
+ *) Fix get_basic_auth_pw() to set the auth_type of the request.
+ [David Robinson]
+
+ *) past changes to http_config.c to only use the
+ setrlimit function on systems defining RLIMIT_NOFILE
+ broke the feature on SUNOS4. Now defines HAVE_RESOURCE
+ for SUNOS and prototypes the needed functions.
+
+ *) Remove uses of MAX_STRING_LEN/HUGE_STRING_LEN from several routines.
+ [David Robinson]
+
+ *) Fix use of pointer to scratch memory. [Cliff Skolnick]
+
+ *) Merge multiple headers from CGI scripts instead of taking last
+ one. [David Robinson]
+
+ *) Add support for SCO 5. [Ben Laurie]
+
+Changes with Apache 1.0.1
+
+ *) Silence mod_log_referer and mod_log_agent if not configured
+ [Randy Terbush]
+
+ *) Recursive includes can occur if the client supplies PATH_INFO data
+ and the server provider uses relative links; as file.html
+ relative to /doc.shtml/pathinfo is /doc.shtml/file.html. [David Robinson]
+
+ *) The replacement for initgroups() did not call {set,end}grent(). This
+ had two implications: if anything else used getgrent(), then
+ initgroups() would fail, and it was consuming a file descriptor.
+ [Ben Laurie]
+
+ *) On heavily loaded servers it was possible for the scoreboard to get
+ out of sync with reality, as a result of a race condition.
+ The observed symptoms are far more Apaches running than should
+ be, and heavy system loads, generally followed by catastrophic
+ system failure. [Ben Laurie]
+
+ *) Fix typo in license. [David Robinson]
+
+Changes with Apache 1.0.0 23 Nov 1995
+
+ *) Not listed. See <http://www.apache.org/docs/new_features_1_0.html>
+
+Changes with Apache 0.8.16 05 Nov 1995
+
+ *) New man page for 'httpd' added to support directory [David Robinson]
+
+ *) .htgroup files can have more than one line giving members for a
+ given group (each must have the group name in front), for NCSA
+ back-compatibility [Robert Thau]
+
+ *) Mutual exclusion around accept() is on by default for SVR4 systems
+ generally, since they generally can't handle multiple processes in
+ accept() on the same socket. This should cure flaky behavior on
+ a lot of those systems. [David Robinson]
+
+ *) AddType, AddEncoding, and AddLanguage directives take multiple
+ extensions on a single command line [David Robinson]
+
+ *) UserDir can be disabled for a given virtual host by saying
+ "UserDir disabled" in the <VirtualHost> section --- it was a bug
+ that this didn't work. [David Robinson]
+
+ *) Compiles on QNX [Ben Laurie]
+
+ *) Corrected parsing of ctime time format [David Robinson]
+
+ *) httpd does a perror() before exiting if it can't log its pid
+ to the PidFile, to make diagnosing the error a bit easier.
+ [David Robinson]
+
+ *) <!--#include file="..."--> can no longer include files in the
+ parent directory, for NCSA back-compatibility. [David Robinson]
+
+ *) '~' is *not* escaped in URIs generated for directory listings
+ [Roy Fielding]
+
+ *) Eliminated compiler warning in the imagemap module [Randy Terbush]
+
+ *) Fixed bug involving handling URIs with escaped %-characters
+ in redirects [David Robinson]
+
+Changes with Apache 0.8.15 14 Oct 1995
+
+ *) Switched to new, simpler license
+
+ *) Eliminated core dumps with improperly formatted DBM group files [Mark Cox]
+
+ *) Don't allow requests for ordinary files to have PATH_INFO [Ben Laurie]
+
+ *) Reject paths containing %-escaped '%' or null characters [David Robinson]
+
+ *) Correctly handles internal redirects to files with names containing '%'
+ [David Robinson]
+
+ *) Repunctuated some error messages [Aram Mirzadeh, Andrew Wilson]
+
+ *) Use geteuid() rather than getuid() to see if we have root privilege,
+ so that server correctly resets privilege if run setuid root. [Andrew
+ Wilson]
+
+ *) Handle ftp: and telnet: URLs correctly in imagemaps (built-in module)
+ [Randy Terbush]
+
+ *) Fix relative URLs in imagemap files [Randy Terbush]
+
+ *) Somewhat better fix for the old "Alias /foo/ /bar/" business
+ [David Robinson]
+
+ *) Don't repeatedly open the ErrorLog if a bunch of <VirtualHost>
+ entries all name the same one. [David Robinson]
+
+ *) Fix directory listings with filenames containing unusual characters
+ [David Robinson]
+
+ *) Better URI-escaping for generated URIs in directories with filenames
+ containing unusual characters [Ben Laurie]
+
+ *) Fixed potential FILE* leak in http_main.c [Ben Laurie]
+
+ *) Unblock alarms on error return from spawn_child() [David Robinson]
+
+ *) Sample Config files have extra note for SCO users [Ben Laurie]
+
+ *) Configuration has note for HP-UX users [Rob Hartill]
+
+ *) Eliminated some bogus Linux-only #defines in conf.h [Aram Mirzadeh]
+
+ *) Nuked bogus #define in httpd.h [David Robinson]
+
+ *) Better test for whether a system has setrlimit() [David Robinson]
+
+ *) Calls update_child_status() after reopen_scoreboard() [David Robinson]
+
+ *) Doesn't send itself SIGHUP on startup when run in the -X debug-only mode
+ [Ben Laurie]
+
+Changes with Apache 0.8.14 19 Sep 1995
+
+ *) Compiles on SCO ODT 3.0 [Ben Laurie]
+
+ *) AddDescription works (better) [Ben Laurie]
+
+ *) Leaves an intelligible error diagnostic when it can't set group
+ privileges on standalone startup [Andrew Wilson]
+
+ *) Compiles on NeXT again --- the 0.8.13 RLIMIT patch was failing on
+ that machine, which claims to be BSD but does not support RLIMIT.
+ [Randy Terbush]
+
+ *) gcc -Wall no longer complains about an unused variable when util.c
+ is compiled with -DMINIMAL_DNS [Andrew Wilson]
+
+ *) Nuked another compiler warning for -Wall on Linux [Aram Mirzadeh]
+
+Changes with Apache 0.8.13 07 Sep 1995
+
+ *) Make IndexIgnore *work* (ooops) [Jarkko Torppa]
+
+ *) Have built-in imagemap code recognize & honor Point directive [James
+ Cloos]
+
+ *) Generate cleaner directory listings in directories with a mix of
+ long and short filenames [Rob Hartill]
+
+ *) Properly initialize dynamically loaded modules [Royston Shufflebotham]
+
+ *) Properly default ServerName for virtual servers [Robert Thau]
+
+ *) Rationalize handling of BSD in conf.h and elsewhere [Randy Terbush,
+ Paul Richards and a cast of thousands...]
+
+ *) On self-identified BSD systems (we don't try to guess any more),
+ allocate a few extra file descriptors per virtual host with setrlimit,
+ if we can, to avoid running out. [Randy Terbush]
+
+ *) Write 22-character lock file name into buffer with enough space
+ on startup [Konstantin Olchanski]
+
+ *) Use archaic setpgrp() interface on NeXT, which requires it [Brian
+ Pinkerton]
+
+ *) Suppress -Wall warning by casting const away in util.c [Aram Mirzadeh]
+
+ *) Suppress -Wall warning by initializing variable in negotiation code
+ [Tobias Weingartner]
+
+Changes with Apache 0.8.12 31 Aug 1995
+
+ *) Doesn't pause three seconds after including a CGI script which is
+ too slow to die off (this is done by not even trying to kill off
+ subprocesses, including the SIGTERM/pause/SIGKILL routine, until
+ after the entire document has been processed). [Robert Thau]
+
+ *) Doesn't do SSI if Options Includes is off. (Ooops). [David Robinson]
+
+ *) Options IncludesNoExec allows inclusion of at least text/* [Roy Fielding]
+
+ *) Allows .htaccess files to override <Directory> sections naming the
+ same directory [David Robinson]
+
+ *) Removed an efficiency hack in sub_req_lookup_uri which was
+ causing certain extremely marginal cases (e.g., ScriptAlias of a
+ *particular* index.html file) to fail. [David Robinson]
+
+ *) Doesn't log an error when the requested URI requires
+ authentication, but no auth header line was supplied by the
+ client; this is a normal condition (the client doesn't no auth is
+ needed here yet). [Robert Thau]
+
+ *) Behaves more sanely when the name server loses its mind [Sean Welch]
+
+ *) RFC931 code compiles cleanly on old BSDI releases [Randy Terbush]
+
+ *) RFC931 code no longer passes out name of prior clients on current
+ requests if the current request came from a server that doesn't
+ do RFC931. [David Robinson]
+
+ *) Configuration script accepts "Module" lines with trailing whitespace.
+ [Robert Thau]
+
+ *) Cleaned up compiler warning from mod_access.c [Robert Thau]
+
+ *) Cleaned up comments in mod_cgi.c [Robert Thau]
+
+Changes with Apache 0.8.11 24 Aug 1995
+
+ *) Wildcard <Directory> specifications work. [Robert Thau]
+
+ *) Doesn't loop for buggy CGI on Solaris [Cliff Skolnick]
+
+ *) Symlink checks (FollowSymLinks off, or SymLinkIfOwnerMatch) always check
+ the file being requested itself, in addition to the directories leading
+ up to it. [Robert Thau]
+
+ *) Logs access failures due to symlink checks or invalid client address
+ in the error log [Roy Fielding, Robert Thau]
+
+ *) Symlink checks deal correctly with systems where lstat of
+ "/path/to/some/link/" follows the link. [Thau, Fielding]
+
+ *) Doesn't reset DirectoryIndex to 'index.html' when
+ other directory options are set in a .htaccess file. [Robert Thau]
+
+ *) Clarified init code and nuked bogus warning in mod_access.c
+ [Florent Guillaume]
+
+ *) Corrected several directives in sample srm.conf
+ --- includes corrections to directory indexing icon-related directives
+ (using unknown.gif rather than unknown.xbm as the DefaultIcon, doing
+ icons for encodings right, and turning on AddEncoding by default).
+ [Roy Fielding]
+
+ *) Corrected descriptions of args to AddIcon and AddAlt in command table
+ [James Cloos]
+
+ *) INSTALL & README mention "contributed modules" directory [Brian
+ Behlendorf]
+
+ *) Fixed English in the license language... "for for" --> "for".
+ [Roy Fielding]
+
+ *) Fixed ScriptAlias/Alias interaction by moving ScriptAlias handling to
+ mod_alias.c, merging it almost completely with handling of Alias, and
+ adding a 'notes' field to the request_rec which allows the CGI module
+ to discover whether the Alias module has put this request through
+ ScriptAlias (which it needs to know for back-compatibility, as the old
+ NCSA code did not check Options ExecCGI in ScriptAlias directories).
+ [Robert Thau]
+
+Changes with Apache 0.8.10 18 Aug 1995
+
+ *) AllowOverride applies to the named directory, and not just
+ subdirectories. [David Robinson]
+
+ *) Do locking for accept() exclusion (on systems that need it)
+ using a special file created for the purpose in /usr/tmp, and
+ not the error log; using the error log causes real problems
+ if it's NFS-mounted; this is known to be the cause of a whole
+ lot of "server hang" problems with Solaris. [David Robinson;
+ thanks to Merten Schumann for help diagnosing the problem].
+
+Changes with Apache 0.8.9 12 Aug 1995
+
+ *) Compiles with -DMAXIMUM_DNS ---- ooops! [Henrik Mortensen]
+
+ *) Nested includes see environment variables of the including document,
+ for NCSA bug-compatibility (some sites have standard footer includes
+ which try to print out the last-modified date). [Eric Hagberg/Robert
+ Thau]
+
+ *) <!--exec cgi="/some/uri/here"--> always treats the item named by the
+ URI as a CGI script, even if it would have been treated as something
+ else if requested directly, for NCSA back-compatibility. (Note that
+ this means that people who know the name of the script can see the
+ code just by asking for it). [Robert Thau]
+
+ *) New version of dbmmanage script included in support directory as
+ dbmmanage.new.
+
+ *) Check if scoreboard file couldn't be opened, and say so, rather
+ then going insane [David Robinson]
+
+ *) POST to CGI works on A/UX [Jim Jagielski]
+
+ *) AddIcon and AddAlt commands work properly [Rob Hartill]
+
+ *) NCSA server push works properly --- the Arena bug compatibility
+ workaround, which broke it, is gone (use -DARENA_BUG_WORKAROUND
+ if you still want the workaround). [Rob Hartill]
+
+ *) If client didn't submit any Accept-encodings, ignore encodings in
+ content negotiation. (NB this will all have to be reworked anyway
+ for the new HTTP draft). [Florent Guillaume]
+
+ *) Don't dump core when trying to log timed-out requests [Jim Jagielski]
+
+ *) Really honor CacheNegotiatedDocs [Florent Guillaume]
+
+ *) Give Redirect priority over Alias, for NCSA bug compatibility
+ [David Robinson]
+
+ *) Correctly set PATH_TRANSLATED in all cases from <!--#exec cmd=""-->,
+ paralleling earlier bug fix for CGI [David Robinson]
+
+ *) If DBM auth is improperly configured, report a server error and don't
+ dump core.
+
+ *) Deleted FCNTL_SERIALIZED_ACCEPTS from conf.h entry for A/UX;
+ it seems to work well enough without it (even in a 10 hits/sec
+ workout), and the overhead for the locking under A/UX is
+ alarmingly high (though it is very low on other systems).
+ [Eric Hagberg, Jim Jagielski]
+
+ *) Fixed portability problems with mod_cookies.c [Cliff Skolnick]
+
+ *) Further de-Berklize mod_cookies.c; change the bogus #include. [Brian
+ Behlendorf/Eric Hagberg]
+
+ *) More improvements to default Configuration for A/UX [Jim Jagielski]
+
+ *) Compiles clean on NEXT [Rob Hartill]
+
+ *) Compiles clean on SGI [Robert Thau]
+
+Changes with Apache 0.8.8 08 Aug 1995
+
+ *) SunOS library prototypes now never included unless explicitly
+ requested in the configuration (via -DSUNOS_LIB_PROTOTYPES);
+ people using GNU libc on SunOS are screwed by prototypes for the
+ standard library.
+
+ (Those who wish to compile clean with gcc -Wall on a standard
+ SunOS setup need the prototypes, and may obtain them using
+ -DSUNOS_LIB_PROTOTYPES. Those wishing to use -Wall on a system
+ with nonstandard libraries are presumably competent to make their
+ own arrangements).
+
+ *) Strips trailing '/' characters off both args to the Alias command,
+ to make 'Alias /foo/ /bar/' work.
+
+Changes with Apache 0.8.7 03 Aug 1995
+
+ *) Don't hang when restarting with a child from 'TransferLog "|..."' running
+ [reported by David Robinson]
+
+ *) Compiles clean on OSF/1 [David Robinson]
+
+ *) Added some of the more recent significant changes (AddLanguage stuff,
+ experimental LogFormat support) to CHANGES file in distribution root
+ directory
+
+Changes with Apache 0.8.6 02 Aug 1995
+
+ *) Deleted Netscape reload workaround --- it's in violation of HTTP specs.
+ (If you actually wanted a conditional GET which bypassed the cache, you
+ couldn't get it). [Reported by Roy Fielding]
+
+ *) Properly terminate headers on '304 Not Modified' replies to conditional
+ GETs --- no browser we can find cares much, but the CERN proxy chokes.
+ [Reported by Cliff Skolnick; fix discovered independently by Rob Hartill]
+
+ *) httpd -v doesn't call itself "Shambhala". [Reported by Chuck Murcko]
+
+ *) SunOS lib-function prototypes in conf.h conditionalized on __GNUC__,
+ not __SUNPRO_C (they're needed to quiet gcc -Wall, but acc chokes on 'em,
+ and older versions don't set the __SUNPRO_C preprocessor variable). On
+ all other systems, these are never used anyway. [Reported by Mark Cox].
+
+ *) Scoreboard file (/tmp/htstatus.*) no longer publically writable.
+
+Changes with Apache 0.8.5 01 Aug 1995
+
+ *) Added last-minute configurable log experiment, as optional module
+
+ *) Correctly set r->bytes_sent for HTTP/0.9 requests, so they get logged
+ properly. (One-line fix to http_protocol.c).
+
+ *) Work around bogus behavior when reloading from Netscape.
+ It's Netscape's bug --- for some reason they expect a request with
+ If-modified-since: to not function as a conditional GET if it also
+ comes with Pragma: no-cache, which is way out of line with the HTTP
+ spec (according to Roy Fielding, the redactor).
+
+ *) Added parameter to set maximum number of server processes.
+
+ *) Added patches to make it work on A/UX. A/UX is *weird*. [Eric Hagberg,
+ Jim Jagielski]
+
+ *) IdentityCheck bugfix [Chuck Murcko].
+
+ *) Corrected cgi-src/Makefile entry for new imagemap script. [Alexei Kosut]
+
+ *) More sample config file corrections; add extension to AddType for
+ *.asis, move AddType generic description to its proper place, and
+ fix miscellaneous typos. [ Alexei Kosut ]
+
+ *) Deleted the *other* reference to the regents from the Berkeley
+ legal disclaimer (everyplace).
+
+ *) Nuked Shambhala name from src/README; had already cleaned it out
+ of everywhere else.
+
+Changes with Apache 0.8.4
+
+ *) Changes to server-pool management parms --- renamed current
+ StartServers to MinSpareServers, created separate StartServers
+ parameter which means what it says, and renamed MaxServers to
+ MaxSpareServers (though the old name still works, for NCSA 1.4
+ back-compatibility). The old names were generally regarded as
+ too confusing. Also altered "docs" in sample config files.
+
+ *) More improvements to default config files ---
+ sample directives (commented out) for XBitHack, BindAddress,
+ CacheNegotiatedDocs, VirtualHost; decent set of AddLanguage
+ defaults, AddTypes for send-as-is and imagemap magic types, and
+ improvements to samples for DirectoryIndex [Alexei Kosut]
+
+ *) Yet more improvements to default config files --- changes to
+ Alexei's sample AddLanguage directives, and sample LanguagePriority
+ [ Florent Guillaume ]
+
+ *) Set config file locations properly if not set in httpd.conf
+ [ David Robinson ]
+
+ *) Don't escape URIs in internal redirects multiple times; don't
+ do that when translating PATH_INFO to PATH_TRANSLATED either.
+ [ David Robinson ]
+
+ *) Corrected spelling of "Required" in 401 error reports [Andrew Wilson]
+
+Changes with Apache 0.8.3
+
+ *) Edited distribution README to *briefly* summarize installation
+ procedures, and give a pointer to the INSTALL file in the src/
+ directory.
+
+ *) Upgraded imagemap script in cgi-bin to 1.8 version from more
+ recent NCSA distributions.
+
+ *) Bug fix to previous bug fix --- if .htaccess file and <Directory>
+ exist for the same directory, use both and don't segfault. [Reported
+ by David Robinson]
+
+ *) Proper makefile dependencies [David Robinson]
+
+ *) Note (re)starts in error log --- reported by Rob Hartill.
+
+ *) Only call no2slash() after get_path_info() has been done, to
+ preserve multiple slashes in the PATH_INFO [NCSA compatibility,
+ reported by Andrew Wilson, though this one is probably a real bug]
+
+ *) Fixed mod_imap.c --- relative paths with base_uri referer don't
+ dump core when Referer is not supplied. [Randy Terbush]
+
+ *) Lightly edited sample config files to refer people to our documentation
+ instead of NCSA's, and to list Rob McCool as *original* author (also
+ deleted his old, and no doubt non-functional email address). Would be
+ nice to have examples of new features...
+
+Changes with Apache 0.8.2 19 Jul 1995
+
+ *) Added AddLanuage code [Florent Guillaume]
+
+ *) Don't say "access forbidden" when a CGI script is not found. [Mark Cox]
+
+ *) All sorts of problems when MultiViews finds a directory. It would
+ be nice if mod_dir.c was robust enough to handle that, but for now,
+ just punt. [reported by Brian Behlendorf]
+
+ *) Wait for all children on restart, to make sure that the old socket
+ is gone and we can reopen it. [reported by Randy Terbush]
+
+ *) Imagemap module is enabled in default Configuration
+
+ *) RefererLog and UserAgentLog modules properly default the logfile
+ [Randy Terbush]
+
+ *) Mark Cox's mod_cookies added to the distribution as an optional
+ module (commented out in the default Configuration, and noted as
+ an experiment, along with mod_dld). [Mark Cox]
+
+ *) Compiles on ULTRIX (a continuing battle...). [Robert Thau]
+
+ *) Fixed nasty bug in SIGTERM handling [reported by Randy Terbush]
+
+ *) Changed "Shambhala" to "Apache" in API docs. [Robert Thau]
+
+ *) Added new, toothier legal disclaimer. [Robert Thau; copied from BSD
+ license]
+
+Changes with Apache 0.8.1
+
+ *) New imagemap module [Randy Terbush]
+
+ *) Replacement referer log module with NCSA-compatible RefererIgnore
+ [Matthew Gray again]
+
+ *) Don't mung directory listings with very long filenames.
+ [Florent Guillaume]
+
+Changes with Apache 0.8.0 (nee Shambhala 0.6.2) 16 Jul 1995
+
+ *) New config script. See INSTALL for info. [Robert Thau]
+
+ *) Scoreboard mechanism for regulating the number of extant server
+ processes. MaxServers and StartServers defaults are the same as
+ for NCSA, but the meanings are slightly different. (Actually,
+ I should probably lower the MaxServers default to 10).
+
+ Before asking for a new connection, each server process checks
+ the number of other servers which are also waiting for a
+ connection. If there are more than MaxServers, it quietly dies
+ off. Conversely, every second, the root, or caretaker, process
+ looks to see how many servers are waiting for a new connection;
+ if there are fewer than StartServers, it starts a new one. This
+ does not depend on the number of server processes already extant.
+ The accounting is arranged through a "scoreboard" file, named
+ /tmp/htstatus.*, on which each process has an independent file
+ descriptor (they need to seek without interference).
+
+ The end effect is that MaxServers is the maximum number of
+ servers on an *inactive* server machine, but more will be forked
+ off to handle unusually heavy loads (or unusually slow clients);
+ these will die off when they are no longer needed --- without
+ reverting to the overhead of full forking operation. There is a
+ hard maximum of 150 server processes compiled in, largely to
+ avoid forking out of control and dragging the machine down.
+ (This is arguably too high).
+
+ In my server endurance tests, this mechanism did not appear to
+ impose any significant overhead, even after I forced it to put the
+ scoreboard file on a normal filesystem (which might have more
+ overhead than tmpfs). [Robert Thau]
+
+ *) Set HTTP_FOO variables for SSI <!--#exec cmd-->s, not just CGI scripts.
+ [Cliff Skolnick]
+
+ *) Read .htaccess files even in directory with <Directory> section.
+ (Former incompatibility noted on mailing list, now fixed). [Robert
+ Thau]
+
+ *) "HEAD /" gives the client a "Bad Request" error message, rather
+ than trying to send no body *and* no headers. [Cliff Skolnick].
+
+ *) Don't produce double error reports for some very obscure cases
+ mainly involving auth configuration (the "all modules decline to
+ handle" case which is a sure sign of a server bug in most cases,
+ but also happens when authentication is badly misconfigured).
+ [Robert Thau]
+
+ *) Moved FCNTL_SERIALIZED_ACCEPT defines into conf.h (that's what
+ it's *for*, and this sort of thing really shouldn't be cluttering
+ up the Makefile). [Robert Thau]
+
+ *) Incidental code cleanups in http_main.c --- stop dragging
+ sa_client around; just declare it where used. [Robert Thau]
+
+ *) Another acc-related fix. (It doesn't like const char
+ in some places...). [Mark Cox]
+
+Changes with Shambhala 0.6.1 13 Jul 1995
+
+ *) Fixed auth_name-related typos in http_core.c [Brian Behlendorf]
+ Also, fixed auth typo in http_protocol.c unmasked by this fix.
+
+ *) Compiles clean with acc on SunOS [Paul Sutton]
+
+ *) Reordered modules in modules.c so that Redirect takes priority
+ over ScriptAlias, for NCSA bug-compatibility [Rob Hartill] ---
+ believe it or not, he has an actual site with a ScriptAlias and
+ a Redirect declared for the *exact same directory*. Even *my*
+ compatibility fetish wouldn't motivate me to fix this if the fix
+ required any effort, but it doesn't, so what the hey.
+
+ *) Fixed to properly default several server_rec fields for virtual
+ servers from the corresponding fields in the main server_rec.
+ [Cliff Skolnick --- 'port' was a particular irritant].
+
+ *) No longer kills off nph- child processes before they are
+ finished sending output. [Matthew Gray]
+
+Changes with Shambhala 0.6.0 10 Jul 1995
+
+ *) Two styles of timeout --- hard and soft. soft_timeout()s just put
+ the connection to the client in an "aborted" state, but otherwise
+ allow whatever handlers are running to clean up. hard_timeout()s
+ abort the request in progress completely; anything not tied to some
+ resource pool cleanup will leak. They're still around because I
+ haven't yet come up with a more elegant way of handling
+ timeouts when talking to something that isn't the client. The
+ default_handler and the dir_handler now use soft timeouts, largely
+ so I can test the feature. [Robert Thau]
+
+ *) TransferLog "| my_postprocessor ..." seems to be there. Note that
+ the case of log handlers dying prematurely is probably handled VERY
+ gracelessly at this point, and if the logger stops reading input,
+ the server will hang. (It is known to correctly restart the
+ logging process on server restart; this is (should be!) going through
+ the same SIGTERM/pause/SIGKILL routine used to ding an errant CGI
+ script). [Robert Thau]
+
+ *) asis files supported (new module). [Robert Thau]
+
+ *) IdentityCheck code is compiled in, but has not been tested. (I
+ don't know anyone who runs identd). [Robert Thau]
+
+ *) PATH_INFO and PATH_TRANSLATED are not set unless some real PATH_INFO
+ came in with the request, for NCSA bug-compatibility. [Robert Thau]
+
+ *) Don't leak the DIR * on HEAD request for a directory. [Robert Thau]
+
+ *) Deleted the block_alarms() stuff from dbm_auth; no longer necessary,
+ as timeouts are not in scope. [Robert Thau]
+
+ *) quoted-string args in config files now handled correctly (doesn't drop
+ the last character). [Robert Thau; reported by Randy Terbush]
+
+ *) Fixed silly typo in http_main.c which was suddenly fatal in HP-UX.
+ How the hell did it ever work? [Robert Thau; reported by Rob Hartill]
+
+ *) mod_core.c --- default_type returns DEFAULT_TYPE (the compile-time
+ default default type); the former default default behavior when all
+ type-checkers defaulted had been a core dump. [Paul Sutton]
+
+ *) Copy filenames out of the struct dirent when indexing
+ directories. (On Linux, readdir() returns a pointer to the same
+ memory area every time). Fix is in mod_dir.c. [Paul Sutton]
+
+Changes with Shambhala 0.5.3 [not released]
+
+ *) Default response handler notes "file not found" in the error log,
+ if the file was not found. [Cliff Skolnick].
+
+ *) Another Cliff bug --- "GET /~user" now properly redirects (the userdir
+ code no longer sets up bogus PATH_INFO which fakes out the directory
+ handler). [Cliff Skolnick]
+
+Changes with Shambhala 0.5.2 06 Jul 1995
+
+ *) Changes to http_main.c --- root server no longer plays silly
+ games with SIGCHLD, and so now detects and replaces dying
+ children. Child processes just die on SIGTERM, without taking
+ the whole process group with them. Potential problem --- if any
+ child process refuses to die, we hang in restart.
+ MaxRequestsPerChild may still not work, but it certainly works
+ better than it did before this! [Robert Thau]
+
+ *) mod_dir.c bug fixes: ReadmeName and HeaderName
+ work (or work better, at least); over-long description lines
+ properly terminated. [Mark Cox]
+
+ *) http_request.c now calls unescape_url() more places where it
+ should [Paul Sutton].
+
+ *) More directory handling bugs (reported by Cox)
+ Parent Directory link is now set correctly. [Robert Thau]
+
+Changes with Shambhala 0.5.1 04 Jul 1995
+
+ *) Generalized cleanup interface in alloc.c --- any function can be
+ registered with alloc.c as a cleanup for a resource pool;
+ tracking of files and file descriptors has been reimplemented in
+ terms of this interface, so I can give it some sort of a test.
+ [Robert Thau]
+
+ *) More changes in alloc.c --- new cleanup_for_exec() function,
+ which tracks down and closes all file descriptors which have been
+ registered with the alloc.c machinery before the server exec()s a
+ child process for CGI or <!--#exec-->. CGI children now get
+ started with exactly three file descriptors open. Hopefully,
+ this cures the problem Rob H. was having with overly persistent
+ CGI connections. [Robert Thau]
+
+ *) Mutual exclusion around the accept() in child_main() --- this is
+ required on at least SGI, Solaris and Linux, and is #ifdef'ed in
+ by default on those systems only (-DFCNTL_SERIALIZED_ACCEPT).
+ This uses fcntl(F_SETLK,...) on the error log descriptor because
+ flock() on that descriptor won't work on systems which have BSD
+ flock() semantics, including (I think) Linux 1.3 and Solaris.
+
+ This does work on SunOS (when the server is idle, only one
+ process in the pool is waiting on accept()); it *ought* to work
+ on the other systems. [Robert Thau]
+
+ *) FreeBSD and BSDI portability tweaks [Chuck Murcko]
+
+ *) sizeof(*sa_client) bugfix from [Rob Hartill]
+
+ *) pstrdup(..., NULL) returns NULL, [Randy Terbush]
+
+ *) block_alarms() to avoid leaking the DBM* in dbm auth (this should
+ be unnecessary if I go to the revised timeout-handling scheme).
+ [Robert Thau]
+
+ *) For NCSA bug-compatibility, set QUERY_STRING env var (to a null
+ string) even if none came in with the request. [Robert Thau]
+
+ *) CHANGES file added to distribution ;-).
+
+Changes with Shambhala 0.4.5
+
+ *) mod_dld --- early dynamic loading support [rst]
+ *) Add wildcard content handlers for XBITHACK; default_hander now
+ invoked with that mechanism (as a handler hanging off mod_core) [rst]
+ *) XBITHACK supported as a wildcard content-handler, and
+ configurable at run-time (not just at compile time, as in the
+ "patchy server" releases) [rst]
+
+Changes with Shambhala 0.4.4 30 Jun 1995
+
+ *) Fixed basic thinkos in mod_dbm_auth.c [rst, reported by Mark Cox]
+ *) Handle Addtype x/y .z [rst, reported by Cox]
+
+Changes with Shambhala 0.4.3
+
+ *) Fixed very dumb bug in mod_alias; "Alias" and "Redirect" are not
+ synonymous [rst, terbush]
+
+Changes with Shambhala 0.4.1 28 Jun 1995
+
+ *) First-cut virtual host implementation; some refit in the config
+ reading code, and log management, was necessary to support this [rst]
+ *) Sub-pool machinery, originally added to avoid excessive storage
+ allocation on listings of large directories (which turned out to
+ be the problem that the 0.3 storage accounting was added to
+ find). Subrequests and mod_dir changed to use subpools. [rst]
+ *) More memory debugging --- free list consistency checks. [rst]
+ *) Added err_headers to request_rec, with support elsewhere [rst]
+ *) Other fixes to minor bugs in mod_dir and mod_includes [rst, terbush]
+
+Changes with Shambhala 0.3 19 Jun 1995
+
+ *) Switch ONE_PROCESS to a runtime command-line option (-X)
+ *) Don't compile in mod_ai_backcompat by default
+ *) Switch name of server from Apache to Shambhala in Makefile
+ *) Add some accounting routines to track memory usage in the pools,
+ for debugging
+
+Changes with Shambhala 0.2
+
+ *) Set DOCUMENT_ROOT CGI variable
+ *) Add single-process debugging, as a compile-time option (ONE_PROCESS)
+ *) Add critical section protection to handling of cleanup structures
+ in alloc.c [rst]
+ *) Significant code reorg within the server core to group related
+ functions together [rst]
+ *) Correctly handle clients that hang up before sending any request
+ [rst]
+ *) Replace dying child processes. [rst]
+
+Changes with Shambhala 0.1 12 Jun 1995
+
+ Major rewrite of the pre-existing "patchy server" codebase, by
+ Robert Thau (rst). Significant portions of the server code, such
+ as configuration-file handling and HTTP authentication support,
+ were ripped out and rewritten from scratch. Code that was not
+ completely rewritten was significantly altered.
+
+ Major changes with this release include:
+
+ *) Introduction of the module API; in request handling, the central
+ machinery just dispatches to various modules, which actually do
+ most of the work. Configuration handling is similar --- modules
+ declare their own commands, and the central machinery just
+ dispatches to them.
+
+ API features from shambhala/0.1 were substantially unchanged in
+ Apache 1.0 and 1.1. (1.0 API features not yet present in this
+ release, such as wildcard handlers and subpools, were added in
+ subsequent Shambhala releases, and were also generally rst's
+ work).
+
+ *) This release included the following modules:
+
+ mod_access (access control --- allow and deny directives),
+ mod_alias (Alias and Redirect commands),
+ mod_auth (straight HTTP authentication, based on flat-files)
+ mod_auth_dbm (same, with dbm files)
+ mod_cgi (CGI scripts and, in this release, ScriptAlias)
+ mod_common_log (CLF access logs; later renamed mod_log_common)
+ mod_dir (directory indexing)
+ mod_include (server-side includes)
+ mod_mime (AddType directives)
+ mod_negotiation (content negotiation)
+ mod_userdir (support for users' public_html directories)
+
+ It also included a mod_ai_backcompat, which was a private hack
+ for back-compatibility with rst's own AI-lab servers.
+
+ All of these modules were substantially complete, and functional
+ or nearly so (a few, which implemented features not in use at
+ Thau's site, required patches of a few lines).
+
+ *) sub-request machinery, to allow modules to determine how other
+ modules would assign MIME types to a given file, or optionally
+ serve its content (this is heavily used by mod_dir, mod_include
+ and mod_negotiation).
+
+ *) Resource pool system for keeping track of memory allocated and
+ files opened in service of a particular request. Much of the
+ code in the modules (when they weren't rewrites) was adjusted to
+ replace a pervasive convention of using fixed-size buffers on
+ the stack with an equally pervasive convention of using memory
+ allocated with palloc().
+
+ *) Reorganization of data structures associated with a given
+ request to eliminate use of global variables and the troublesome
+ unmunge_name function (used in NCSA and early Apache releases to
+ attempt to determine the URI which mapped to a given filename
+ --- a difficult proposition, given that it is easy to produce
+ setups in which multiple URIs map to the same file).
+
+ *) Source files renamed and rearranged
+
+ *) Very simple pre-forking behavior --- parent process forked off a
+ fixed number of children, and then just waited for SIGHUP.
+
+ *) Other more minor changes too numerous to list.
+
+ This release included modified versions of a lot of code from the
+ Apache 0.6.4 public release, plus an early pre-forking patch
+ codeveloped by Robert Thau and Rob Hartill.
+
+Changes with Apache 0.7.3 20 Jun 1995
+
+ *) There were a bunch of changes between Apache 0.6.4 and 0.7.3 that
+ were incorporated by Rob Hartill on the main branch while Robert Thau
+ worked on the Shambhala rewrite above. Most were merged into the
+ Shambala architecture after Apache 0.8.0.
+
+Changes with Apache 0.6.4 13 May 1995
+
+ *) Patches by Rob Hartill, Cliff Skolnick, Randy Terbush, Robert Thau,
+ and others.
+
+Changes with Apache 0.5.1 10 Apr 1995
+
+Changes with Apache 0.4 02 Apr 1995
+
+ *) Patches by Brian Behlendorf, Andrew Wilson, Robert Thau,
+ and Rob Hartill.
+
+Changes with Apache 0.3 24 Mar 1995
+
+ *) Patches by Robert Thau, David Robinson, Rob Hartill, and
+ Carlos Varela.
+
+Changes with Apache 0.2 18 Mar 1995
+
+ *) Based on NCSA httpd 1.3 by Rob McCool and patches by CERT,
+ Roy Fielding, Robert Thau, Nicolas Pioch, David Robinson,
+ Brian Behlendorf, Rob Hartill, and Cliff Skolnick.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000000..ba598eb212
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,449 @@
+
+ APACHE INSTALLATION
+
+ Introduction
+ ============
+
+ Apache 2.0's configuration and installation environment has changed
+ completely from Apache 1.3. Apache 1.3 used a custom set of scripts
+ to achieve easy installation. Apache 2.0 now uses libtool and autoconf
+ to create an environment that looks like many other Open Source projects.
+
+
+ Installing the Apache 1.3 HTTP server with APACI
+ ================================================
+
+ 1. Overview for the impatient
+ --------------------------
+
+ $ ./buildconf
+ $ ./configure --prefix=PREFIX
+ $ make
+ $ make install
+ $ PREFIX/bin/apachectl start
+
+ NOTE: PREFIX is not the string "PREFIX". Instead use the Unix
+ filesystem path under which Apache should be installed. For
+ instance use "/usr/local/apache" for PREFIX above.
+
+ 2. Requirements
+ ------------
+
+ The following requirements exist for building Apache:
+
+ o Disk Space:
+
+ Make sure you have approximately 12 MB of temporary free disk space
+ available. After installation Apache occupies approximately 5 MB of
+ disk space (the actual required disk space depends on the amount of
+ compiled in third party modules, etc).
+
+ o ANSI-C Compiler:
+
+ Make sure you have an ANSI-C compiler installed. The GNU C compiler
+ (GCC) from the Free Software Foundation (FSF) is recommended (version
+ 2.7.2 is fine). If you don't have GCC then at least make sure your
+ vendors compiler is ANSI compliant. You can find the homepage of GNU
+ at http://www.gnu.org/ and the GCC distribution under
+ http://www.gnu.org/order/ftp.html .
+
+ o Libtool 1.3.3:
+
+ Make sure that you have libtool 1.3.3 or later installed before
+ trying to configure and build Apache 2.0. Libtool can be downloaded
+ from the Free Software Foundation (FSF), at
+ http://www.gnu.org/order/ftp.html.
+
+ o Autoconf 2.13:
+
+ Make sure that you have autoconf 2.13 or later installed before
+ trying to configure and build Apache 2.0. Autoconf can be
+ downloaded from the Free Software Foundation (FSF), at
+ http://www.gnu.org/order/ftp.html.
+
+ o Perl 5 Interpreter [OPTIONAL]:
+
+ For some of the support scripts like `apxs' or `dbmmanage' (which are
+ written in Perl) the Perl 5 interpreter is required (versions 5.003
+ and 5.004 are fine). If no such interpreter is found by APACI's
+ `configure' script this is no harm. Of course, you still can build
+ and install Apache 1.3. Only those support scripts cannot be used. If
+ you have multiple Perl interpreters installed (perhaps a Perl 4 from
+ the vendor and a Perl 5 from your own), then it is recommended to use
+ the --with-perl option (see below) to make sure the correct one is
+ selected by APACI.
+
+ o Dynamic Shared Object (DSO) support [OPTIONAL]:
+
+ To provide maximum flexibility Apache now is able to load modules
+ under runtime via the DSO mechanism by using the pragmatic
+ dlopen()/dlsym() system calls. These system calls are not available
+ under all operating systems therefore you cannot use the DSO mechanism
+ on all platforms. And Apache currently has only limited built-in
+ knowledge on how to compile shared objects because this is heavily
+ platform-dependent. The current state is this:
+
+ o Out-of-the-box supported platforms are (Not all of these will
+ work currently. DSO support is currently available on most
+ of these platforms however):
+ - Linux - SunOS - UnixWare - Darwin/Mac OS
+ - FreeBSD - Solaris - AIX - OpenStep/Mach
+ - OpenBSD - IRIX - SCO - DYNIX/ptx
+ - NetBSD - HPUX - ReliantUNIX
+ - BSDI - Digital Unix - DGUX
+
+ o Entirely unsupported platforms are:
+ - Ultrix
+
+ If your system is not on these lists but has the dlopen-style
+ interface, you either have to provide the appropriate compiler and
+ linker flags (see CFLAGS_SHLIB, LDFLAGS_SHLIB and LDFLAGS_SHLIB_EXPORT
+ below) manually or at least make sure a Perl 5 interpreter is
+ installed from which Apache can guess the options.
+
+ 3. Configuring the source tree
+ ---------------------------
+
+ Setup:
+
+ The first step in compiling Apache 2.0 is to setup the source tree so
+ that it can be built. This is done by running:
+
+ ./buildconf
+
+ This script ensures that all required programs are installed on the
+ currently machine, and creates the ./configure script. If you are
+ using a package downloaded from apache.org then this step is not
+ necessary.
+
+ Introduction:
+
+ The next step is to configure the Apache source tree for your particular
+ platform and personal requirements. The most important setup here is the
+ location prefix where Apache is to be installed later, because Apache has
+ to be configured for this location to work correctly. But there are a lot
+ of other options available for your pleasure.
+
+ For a short impression of what possibilities you have, here is a typical
+ example which compiles Apache for the installation tree /sw/pkg/apache
+ with a particular compiler and flags plus the two additional modules
+ mod_rewrite and mod_speling for later loading through the DSO mechanism:
+
+ $ CC="pgcc" OPTIM="-O2" \
+ ./configure --prefix=/sw/pkg/apache \
+ --enable-rewrite=shared \
+ --enable-speling=shared
+
+ The easiest way to find all of the configuration flags for Apache 2.0
+ is to run ./configure --help. What follows is a brief description of
+ most of the arguments.
+
+ Reference:
+
+ $ [CC=...] [CFLAGS_SHLIB=...] [TARGET=...]
+ [OPTIM=...] [LD_SHLIB=...]
+ [CFLAGS=...] [LDFLAGS_SHLIB=...]
+ [INCLUDES=...] [LDFLAGS_SHLIB_EXPORT=...]
+ [LDFLAGS=...] [RANLIB=...]
+ [LIBS=...] [DEPS=...]
+ ./configure
+ [--quiet] [--prefix=DIR] [--enable-NAME=(shared)]
+ [--verbose] [--exec-prefix=PREFIX] [--disable-NAME]
+ [--shadow[=DIR]] [--bindir=EPREFIX] [--with-mpm=NAME]
+ [--show-layout] [--sbindir=DIR]
+ [--help] [--libexecdir=DIR]
+ [--mandir=DIR]
+ [--sysconfdir=DIR]
+ [--datadir=DIR]
+ [--includedir=DIR]
+ [--localstatedir=DIR]
+ [--runtimedir=DIR] [--enable-suexec]
+ [--logfiledir=DIR] [--suexec-caller=UID]
+ [--proxycachedir=DIR] [--suexec-docroot=DIR]
+ [--with-layout=[FILE:]ID] [--suexec-logfile=FILE]
+ [--suexec-userdir=DIR]
+ [--with-perl=FILE] [--suexec-uidmin=UID]
+ [--without-support] [--suexec-gidmin=GID]
+ [--without-confadjust] [--suexec-safepath=PATH]
+ [--without-execstrip]
+ [--server-uid=UID] [--with-maintainter-mode]
+ [--server-gid=GID]
+
+ Use the CC, OPTIM, CFLAGS, INCLUDES, LDFLAGS, LIBS, CFLAGS_SHLIB,
+ LD_SHLIB, LDFLAGS_SHLIB, LDFLAGS_SHLIB_EXPORT, RANLIB, DEPS and TARGET
+ environment variables to override the corresponding default entries in
+ the src/Configuration.tmpl file (see there for more information about
+ their usage).
+
+ Use the --prefix=PREFIX and --exec-prefix=EPREFIX options to configure
+ Apache to use a particular installation prefix. The default is
+ PREFIX=/usr/local/apache and EPREFIX=PREFIX.
+
+ Use the --bindir=DIR, --sbindir=DIR, --libexecdir=DIR, --mandir=DIR,
+ --sysconfdir=DIR, --datadir=DIR, --includedir=DIR, --localstatedir=DIR,
+ --runtimedir=DIR, --logfiledir=DIR and proxycachedir=DIR option to change
+ the paths for particular subdirectories of the installation tree.
+ Defaults are bindir=EPREFIX/bin, sbindir=EPREFIX/sbin,
+ libexecdir=EPREFIX/libexec, mandir=PREFIX/man, sysconfdir=PREFIX/etc,
+ datadir=PREFIX/share, includedir=PREFIX/include,
+ localstatedir=PREFIX/var, runtimedir=PREFIX/var/run,
+ logfiledir=PREFIX/var/log and proxycachedir=PREFIX/var/proxy.
+
+ Note: To reduce the pollution of shared installation locations
+ (like /usr/local/ or /etc) with Apache files to a minimum the
+ string ``/apache'' is automatically appended to 'libexecdir',
+ 'sysconfdir', 'datadir', 'localstatedir' and 'includedir' if
+ (and only if) the following points apply for each path
+ individually:
+
+ 1. the path doesn't already contain the word ``apache''
+ 2. the path was not directly customized by the user
+
+ Keep in mind that per default these paths are derived from
+ 'prefix' and 'exec-prefix', so usually its only a matter
+ whether these paths contain ``apache'' or not. Although the
+ defaults were defined with experience in mind you always should
+ make sure the paths fit your situation by checking the finally
+ chosen paths via the --layout option.
+
+ Use the --with-layout=[F:]ID option to select a particular installation
+ path base-layout. You always _HAVE_ to select a base-layout. There are
+ currently two layouts pre-defined in the file config.layout: `Apache' for
+ the classical Apache path layout and `GNU' for a path layout conforming
+ to the GNU `standards' document. When you want to use your own custom
+ layout FOO, either add a corresponding "<Layout FOO>...</Layout>" section
+ to config.layout and use --with-layout=FOO or place it into your own
+ file, say config.mypaths, and use --with-layout=config.mypaths:FOO.
+
+ Use the --show-layout option to check the final installation path layout
+ while fiddling with the options above.
+
+ Use the --enable-rule=NAME and --disable-rule=NAME options to enable or
+ disable a particular Rule from the Apache src/Configuration.tmpl file. The
+ defaults (yes=enabled, no=disabled) can either be seen when running
+ `./configure --help' or manually looked up in the src/Configuration.tmpl
+ file.
+
+ Use the --enable-NAME=(shared) and --disable-NAME options to enable
+ or disable a particular already distributed module from the Apache
+ package.
+
+ Use the --with-mpm=NAME option to determine which MPM should be built
+ for your server.
+ _________________________________________________________________________
+ LIST OF AVAILABLE MODULES
+
+ Environment creation
+ (+) mod_env .......... Set environment variables for CGI/SSI scripts
+ (+) mod_setenvif ..... Set environment variables based on HTTP headers
+ (-) mod_unique_id .... Generate unique identifiers for request
+ Content type decisions
+ (+) mod_mime ......... Content type/encoding determination (configured)
+ (-) mod_mime_magic ... Content type/encoding determination (automatic)
+ (+) mod_negotiation .. Content selection based on the HTTP Accept* headers
+ URL mapping
+ (+) mod_alias ........ Simple URL translation and redirection
+ (-) mod_rewrite ...... Advanced URL translation and redirection
+ (+) mod_userdir ...... Selection of resource directories by username
+ (-) mod_speling ...... Correction of misspelled URLs
+ Directory Handling
+ (+) mod_dir .......... Directory and directory default file handling
+ (+) mod_autoindex .... Automated directory index file generation
+ Access Control
+ (+) mod_access ....... Access Control (user, host, network)
+ (+) mod_auth ......... HTTP Basic Authentication (user, passwd)
+ (-) mod_auth_dbm ..... HTTP Basic Authentication via Unix NDBM files
+ (-) mod_auth_db ...... HTTP Basic Authentication via Berkeley-DB files
+ (-) mod_auth_anon .... HTTP Basic Authentication for Anonymous-style users
+ (-) mod_digest ....... HTTP Digest Authentication
+ HTTP response
+ (-) mod_headers ...... Arbitrary HTTP response headers (configured)
+ (-) mod_cern_meta .... Arbitrary HTTP response headers (CERN-style files)
+ (-) mod_expires ...... Expires HTTP responses
+ (+) mod_asis ......... Raw HTTP responses
+ Scripting
+ (+) mod_include ...... Server Side Includes (SSI) support
+ (+) mod_cgi .......... Common Gateway Interface (CGI) support
+ (+) mod_cgid ......... Common Gateway Interface (CGI) support for
+ multi-threaded MPMs
+ (+) mod_actions ...... Map CGI scripts to act as internal `handlers'
+ Internal Content Handlers
+ (+) mod_status ....... Content handler for server run-time status
+ (-) mod_info ......... Content handler for server configuration summary
+ Request Logging
+ (+) mod_log_config ... Customizable logging of requests
+ (-) mod_log_agent .... Specialized HTTP User-Agent logging (deprecated)
+ (-) mod_log_referer .. Specialized HTTP Referrer logging (deprecated)
+ (-) mod_usertrack .... Logging of user click-trails via HTTP Cookies
+ Miscellaneous
+ (+) mod_imap ......... Server-side Image Map support
+ (-) mod_proxy ........ Caching Proxy Module (HTTP, HTTPS, FTP)
+ (-) mod_so ........... Dynamic Shared Object (DSO) bootstrapping
+ Experimental
+ (-) mod_mmap_static .. Caching of frequently served pages via mmap()
+ Development
+ (-) mod_example ...... Apache API demonstration (developers only)
+
+ MPMs
+ mpmt_pthread ..... Mutli-process(dynamic) Multi-threaded(static)
+ Unix MPM
+ prefork .......... Preforking Unix MPM
+ dexter ........... Multi-process(static) Multi-threaded(dynamic)
+ Unix MPM
+ perchild ......... Multi-process(static) Multi-threaded(dynamic)
+ Unix MPM, that allows a User per child process
+
+ winnt ............ Multi-process(1) Multi-threaded Windows MPM
+
+ mpmt_beos ........ Multi-process Multi-threaded Beos MPM
+ beos ............. Multi-process Multi-threaded Beos MPM
+
+ spmt_os2 ......... Single-process Multi-threaded OS/2 MPM
+ _________________________________________________________________________
+ (+) = enabled per default [disable with --disable-module]
+ (-) = disabled per default [enable with --enable-module ]
+
+ Use the --enable-suexec option to enable the suEXEC feature by building
+ and installing the "suexec" support program. Use --suexec-caller=UID to
+ set the allowed caller user id, --suexec-userdir=DIR to set the user
+ subdirectory, --suexec-docroot=DIR to set the suexec root directory,
+ --suexec-uidmin=UID/--suexec-gidmin=GID to set the minimal allowed
+ UID/GID, --suexec-logfile=FILE to set the logfile and
+ --suexec-safepath=PATH to set the safe shell PATH for the suEXEC
+ feature. At least one --suexec-xxxxx option has to be provided together
+ with the --enable-suexec option to let APACI accept your request for
+ using the suEXEC feature.
+
+ CAUTION: FOR DETAILS ABOUT THE SUEXEC FEATURE WE HIGHLY RECOMMEND YOU TO
+ FIRST READ THE DOCUMENT htdocs/manual/suexec.html BEFORE USING
+ THE ABOVE OPTIONS.
+
+ USING THE SUEXEC FEATURE PROPERLY CAN REDUCE CONSIDERABLY THE
+ SECURITY RISKS INVOLVED WITH ALLOWING USERS TO DEVELOP AND RUN
+ PRIVATE CGI OR SSI PROGRAMS. HOWEVER, IF SUEXEC IS IMPROPERLY
+ CONFIGURED, IT CAN CAUSE ANY NUMBER OF PROBLEMS AND POSSIBLY
+ CREATE NEW HOLES IN YOUR COMPUTER'S SECURITY. IF YOU AREN'T
+ FAMILIAR WITH MANAGING SETUID ROOT PROGRAMS AND THE SECURITY
+ ISSUES THEY PRESENT, WE HIGHLY RECOMMEND THAT YOU NOT CONSIDER
+ USING SUEXEC AND KEEP AWAY FROM THESE OPTIONS!
+
+ Use the --shadow option to let APACI create a shadow source tree of the
+ sources for building. This is useful when you want to build for different
+ platforms in parallel (usually through a NFS, AFS or DFS mounted
+ filesystem). You may specify a directory to the --shadow option into
+ which the shadow tree will be created.
+
+ Use the --quiet option to disable all configuration verbose messages.
+
+ Use the --verbose option to enable additional verbose messages.
+
+ Use the --server-uid option to specify the user ID you want the server to run
+ as. If not specified the server will run as user nobody. If the user ID
+ specified is different than the ID of the user starting the server, you need to
+ start the server as root.
+
+ Use the --server-gid option to specify the group ID you want the server user ID to
+ be a member of. If not specified, the group ID will be #-1.
+
+ 4. Building the package
+ --------------------
+
+ Now you can build the various parts which form the Apache package by
+ simply running the command
+
+ $ make
+
+ Please be patient here, this takes approximately 2 minutes to complete
+ under a Pentium-166/FreeBSD-2.2 system, dependend on the amount of
+ modules you have enabled.
+
+ 5. Installing the package
+ ----------------------
+
+ Now its time to install the package under the configured installation
+ PREFIX (see --prefix option above) by running:
+
+ $ make install
+
+ For the paranoid hackers under us: The above command really installs under
+ prefix _only_, i.e. no other stuff from your system is touched. Even if
+ you upgrade an existing installation your configuration files in
+ PREFIX/etc/ are preserved.
+
+ 6. Testing the package
+ -------------------
+
+ Now you can fire up your Apache HTTP server by immediately running
+
+ $ PREFIX/bin/apachectl start
+
+ and then you should be able to request your first document via URL
+ http://localhost/ (when you built and installed Apache as root or at
+ least used the --without-confadjust option) or http://localhost:8080/
+ (when you built and installed Apache as a regular user). Then stop the
+ server again by running:
+
+ $ PREFIX/bin/apachectl stop
+
+ 7. Customizing the package
+ -----------------------
+
+ Finally you can customize your Apache HTTP server by editing the
+ configuration files under PREFIX/etc/.
+
+ $ vi PREFIX/etc/httpd.conf
+ $ vi PREFIX/etc/access.conf
+ $ vi PREFIX/etc/srm.conf
+
+ Have a look at the Apache manual under htdocs/manual/ or
+ http://www.apache.org/docs/ for a complete reference of available
+ configuration directives.
+
+ 8. Preparing the system
+ --------------------
+
+ Proper operation of a public HTTP server requires at least the following:
+
+ 1. A correctly working TCP/IP layer, since HTTP is implemented on top of
+ TCP/IP. Although modern Unix platforms have good networking layers,
+ always make sure you have all official vendor patches referring to the
+ network layer applied.
+
+ 2. Accurate time keeping, since elements of the HTTP protocol are
+ expressed as the time of day. So, it's time to investigate setting
+ some time synchronization facility on your system. Usually the ntpdate
+ or xntpd programs are used for this purpose which are based on the
+ Network Time Protocol (NTP). See the Usenet newsgroup
+ comp.protocols.time.ntp and the NTP homepage at
+ http://www.eecis.udel.edu/~ntp/ for more details about NTP software
+ and public time servers.
+
+ 9. Contacts
+ --------
+
+ o If you want to be informed about new code releases, bug fixes,
+ security fixes, general news and information about the Apache server
+ subscribe to the apache-announce mailing list as described under
+ http://www.apache.org/announcelist.html
+
+ o If you want freely available support for running Apache please join the
+ Apache user community by subscribing at least to the following USENET
+ newsgroup:
+ comp.infosystems.www.servers.unix
+
+ o If you want commercial support for running Apache please contact
+ one of the companies and contractors which are listed at
+ http://www.apache.org/info/support.cgi
+
+ o If you have a concrete bug report for Apache please go to the
+ Apache Group Bug Database and submit your report:
+ http://www.apache.org/bug_report.html
+
+ o If you want to participate in actively developing Apache please
+ subscribe to the `new-httpd' mailing list as described at
+ http://dev.apache.org/mailing-lists
+
+ Thanks for running Apache.
+ The Apache Group
+ http://www.apache.org/
+
diff --git a/README b/README
new file mode 100644
index 0000000000..679b207b35
--- /dev/null
+++ b/README
@@ -0,0 +1,72 @@
+
+ Apache
+ Version 1.3 (and up)
+
+ What is it?
+ -----------
+
+ Apache is an HTTP server designed as a plug-in replacement for
+ the NCSA server version 1.3 (or 1.4). It fixes numerous bugs in
+ the NCSA server and includes many frequently requested new
+ features, and has an API which allows it to be extended to meet
+ users' needs more easily.
+
+ The Latest Version
+ ------------------
+
+ Details of the latest version can be found on the Apache HTTP
+ server project page under http://httpd.apache.org/.
+
+ Documentation
+ -------------
+
+ The documentation available as of the date of this release is
+ also included, in HTML format, in the htdocs/manual/ directory.
+ For the most up-to-date documentation can be found on
+ http://httpd.apache.org/docs/.
+
+ Installation
+ ------------
+
+ Apache 2.0 uses autoconf for configuration and installation. To use
+ Apache's autoconf script, you will need libtool 1.3.3 or higher, and
+ autoconf 2.13 or newer.
+
+ To configure Apache 2.0 run the following commands.
+
+ ./buildconf
+ ./configure [autoconf arguments] [apache arguments]
+ make
+ make install
+
+ The buildconf script is very important. Just running autoconf will not
+ leave the directory tree in a usable state.
+
+ Licensing
+ ---------
+
+ Please see the file called LICENSE.
+
+ Acknowledgments
+ ----------------
+
+ We wish to acknowledge the following copyrighted works that
+ make up portions of the Apache software:
+
+ Portions of this software were developed at the National Center
+ for Supercomputing Applications (NCSA) at the University of
+ Illinois at Urbana-Champaign.
+
+ This software contains code derived from the RSA Data Security
+ Inc. MD5 Message-Digest Algorithm, including various
+ modifications by Spyglass Inc., Carnegie Mellon University, and
+ Bell Communications Research, Inc (Bellcore).
+
+ This package contains software written and copyrighted by Henry
+ Spencer. Please see the file called src/regex/COPYRIGHT.
+
+ The NT port was started with code provided to the Apache Group
+ by Ambarish Malpani of ValiCert, Inc. (http://www.valicert.com/).
+
+ Apache 2.0 relies heavily on the use of autoconf and libtool to provide
+ a build environment.
diff --git a/build/bsd_makefile b/build/bsd_makefile
new file mode 100755
index 0000000000..90ce5fee3d
--- /dev/null
+++ b/build/bsd_makefile
@@ -0,0 +1,71 @@
+#! /bin/sh
+# ====================================================================
+# The Apache Software License, Version 1.1
+#
+# Copyright (c) 2000 The Apache Software Foundation. All rights
+# reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. The end-user documentation included with the redistribution,
+# if any, must include the following acknowledgment:
+# "This product includes software developed by the
+# Apache Software Foundation (http://www.apache.org/)."
+# Alternately, this acknowledgment may appear in the software itself,
+# if and wherever such third-party acknowledgments normally appear.
+#
+# 4. The names "Apache" and "Apache Software Foundation" must
+# not be used to endorse or promote products derived from this
+# software without prior written permission. For written
+# permission, please contact apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache",
+# nor may "Apache" appear in their name, without prior written
+# permission of the Apache Software Foundation.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Software Foundation. For more
+# information on the Apache Software Foundation, please see
+# <http://www.apache.org/>.
+#
+# The build environment was provided by Sascha Schumann.
+#
+
+# cwd must be top_srcdir
+test -f build/bsd_makefile || exit 2
+
+test -f bsd_converted && exit 0
+
+tmpfile=`mktemp /tmp/$0.XXXXXX` || exit 1
+for i in build/*.mk; do
+ sed 's/^include \(.*\)/.include "\1"/' $i >$tmpfile \
+ && cp $tmpfile $i
+done
+rm -f $tmpfile
+
+touch bsd_converted
+exit 0
diff --git a/build/buildinfo.sh b/build/buildinfo.sh
new file mode 100755
index 0000000000..5c2a72d6b3
--- /dev/null
+++ b/build/buildinfo.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+##
+## buildinfo.sh -- Determine Build Information
+## Initially written by Ralf S. Engelschall <rse@apache.org>
+## for the Apache's Autoconf-style Interface (APACI)
+##
+#
+# This script falls under the Apache License.
+# See http://www.apache.org/docs/LICENSE
+
+
+#
+# argument line handling
+#
+error=no
+if [ $# -ne 1 -a $# -ne 2 ]; then
+ error=yes
+fi
+if [ $# -eq 2 -a "x$1" != "x-n" ]; then
+ error=yes
+fi
+if [ "x$error" = "xyes" ]; then
+ echo "$0:Error: invalid argument line"
+ echo "$0:Usage: $0 [-n] <format-string>"
+ echo "Where <format-string> can contain:"
+ echo " %u ...... substituted by determined username (foo)"
+ echo " %h ...... substituted by determined hostname (bar)"
+ echo " %d ...... substituted by determined domainname (.com)"
+ echo " %D ...... substituted by determined day (DD)"
+ echo " %M ...... substituted by determined month (MM)"
+ echo " %Y ...... substituted by determined year (YYYYY)"
+ echo " %m ...... substituted by determined monthname (Jan)"
+ exit 1
+fi
+if [ $# -eq 2 ]; then
+ newline=no
+ format_string="$2"
+else
+ newline=yes
+ format_string="$1"
+fi
+
+#
+# initialization
+#
+username=''
+hostname=''
+domainname=''
+time_day=''
+time_month=''
+time_year=''
+time_monthname=''
+
+#
+# determine username
+#
+username="$LOGNAME"
+if [ "x$username" = "x" ]; then
+ username="$USER"
+ if [ "x$username" = "x" ]; then
+ username="`(whoami) 2>/dev/null |\
+ awk '{ printf("%s", $1); }'`"
+ if [ "x$username" = "x" ]; then
+ username="`(who am i) 2>/dev/null |\
+ awk '{ printf("%s", $1); }'`"
+ if [ "x$username" = "x" ]; then
+ username='unknown'
+ fi
+ fi
+ fi
+fi
+
+#
+# determine hostname and domainname
+#
+hostname="`(uname -n) 2>/dev/null |\
+ awk '{ printf("%s", $1); }'`"
+if [ "x$hostname" = "x" ]; then
+ hostname="`(hostname) 2>/dev/null |\
+ awk '{ printf("%s", $1); }'`"
+ if [ "x$hostname" = "x" ]; then
+ hostname='unknown'
+ fi
+fi
+case $hostname in
+ *.* )
+ domainname=".`echo $hostname | cut -d. -f2-`"
+ hostname="`echo $hostname | cut -d. -f1`"
+ ;;
+esac
+if [ "x$domainname" = "x" ]; then
+ if [ -f /etc/resolv.conf ]; then
+ domainname="`egrep '^[ ]*domain' /etc/resolv.conf | head -1 |\
+ sed -e 's/.*domain//' \
+ -e 's/^[ ]*//' -e 's/^ *//' -e 's/^ *//' \
+ -e 's/^\.//' -e 's/^/./' |\
+ awk '{ printf("%s", $1); }'`"
+ if [ "x$domainname" = "x" ]; then
+ domainname="`egrep '^[ ]*search' /etc/resolv.conf | head -1 |\
+ sed -e 's/.*search//' \
+ -e 's/^[ ]*//' -e 's/^ *//' -e 's/^ *//' \
+ -e 's/ .*//' -e 's/ .*//' \
+ -e 's/^\.//' -e 's/^/./' |\
+ awk '{ printf("%s", $1); }'`"
+ fi
+ fi
+fi
+
+#
+# determine current time
+#
+time_day="`date '+%d' | awk '{ printf("%s", $1); }'`"
+time_month="`date '+%m' | awk '{ printf("%s", $1); }'`"
+time_year="`date '+%Y' 2>/dev/null | awk '{ printf("%s", $1); }'`"
+if [ "x$time_year" = "x" ]; then
+ time_year="`date '+%y' | awk '{ printf("%s", $1); }'`"
+ case $time_year in
+ [5-9][0-9]) time_year="19$time_year" ;;
+ [0-4][0-9]) time_year="20$time_year" ;;
+ esac
+fi
+case $time_month in
+ 1|01) time_monthname='Jan' ;;
+ 2|02) time_monthname='Feb' ;;
+ 3|03) time_monthname='Mar' ;;
+ 4|04) time_monthname='Apr' ;;
+ 5|05) time_monthname='May' ;;
+ 6|06) time_monthname='Jun' ;;
+ 7|07) time_monthname='Jul' ;;
+ 8|08) time_monthname='Aug' ;;
+ 9|09) time_monthname='Sep' ;;
+ 10) time_monthname='Oct' ;;
+ 11) time_monthname='Nov' ;;
+ 12) time_monthname='Dec' ;;
+esac
+
+#
+# create result string
+#
+if [ "x$newline" = "xyes" ]; then
+ echo $format_string |\
+ sed -e "s;%u;$username;g" \
+ -e "s;%h;$hostname;g" \
+ -e "s;%d;$domainname;g" \
+ -e "s;%D;$time_day;g" \
+ -e "s;%M;$time_month;g" \
+ -e "s;%Y;$time_year;g" \
+ -e "s;%m;$time_monthname;g"
+else
+ echo "${format_string}&" |\
+ sed -e "s;%u;$username;g" \
+ -e "s;%h;$hostname;g" \
+ -e "s;%d;$domainname;g" \
+ -e "s;%D;$time_day;g" \
+ -e "s;%M;$time_month;g" \
+ -e "s;%Y;$time_year;g" \
+ -e "s;%m;$time_monthname;g" |\
+ awk '-F&' '{ printf("%s", $1); }'
+fi
+
diff --git a/build/default.pl b/build/default.pl
new file mode 100644
index 0000000000..42c0b0ca00
--- /dev/null
+++ b/build/default.pl
@@ -0,0 +1,496 @@
+<<
+# Scandoc template file.
+#
+# This is an example set of templates that is designed to create several
+# different kinds of index files. It generates a "master index" which intended
+# for use with a frames browser; A "package index" which is the root page of
+# the index, and then "package files" containing documentation for all of the
+# classes within a single package.
+
+######################################################################
+
+## For quick and superficial customization,
+## simply change these variables
+
+$project_name = '[Apache]';
+$company_logo = '<img src="../images/ScanDocBig.jpg">'; # change this to an image tag.
+$copyright = '&copy 2000 [Apache Software Foundation]';
+$image_directory = "../images/";
+$bullet1_image = $image_directory . "ball1.gif";
+$bullet2_image = $image_directory . "ball2.gif";
+$bgcolor1 = "#FFFFFF";
+$bgcolor2 = "#FFFFFF";
+
+######################################################################
+
+## Begin generating frame index file.
+
+file "index.html";
+>><html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; iso-8859-1">
+ <title>$project_name</title>
+ </head>
+ <frameset cols="190,0%">
+ <frame src="master.html" name="Master Index" noresize>
+ <frame src="packages.html" name="Documentation">
+ <noframes>
+ <body bgcolor="$bgcolor2" stylesrc="index.html">
+ <p>Some Documentation</p>
+ </body>
+ </noframes>
+ </frameset>
+</html>
+<<
+
+######################################################################
+
+## Begin generating master index file (left-hand frame).
+
+file "master.html";
+>><html>
+ <head>
+ <title>Master Index</title>
+ </head>
+ <body bgcolor="$bgcolor1" text=#0000ff link=#0020ff vlink=#0020ff>
+ <center><img src="${image_directory}ScanDocSmall.jpg" border="0" /></center>
+ <p>
+ <a href="packages.html" target="Documentation">Master Index</a>
+ </p>
+ <p>
+ <font size="2">
+ <nobr>
+<<
+
+## For each package, generate an index entry.
+
+foreach $p (packages()) {
+ $_ = $p->url;
+ s/\s/%20/g;
+ >><a href="$_" target="Documentation"><b>$(p.name)</b></a><br>
+ <dir>
+ <<
+ foreach $e ($p->classes()) {
+ $_ = $e->url;
+ s/\s/%20/g;
+ >><li><a href="$_" target="Documentation">$(e.fullname)</a>
+ <<
+ }
+ foreach $e ($p->globals()) {
+ $_ = $e->url;
+ s/\s/%20/g;
+ >><li><a href="$_" target="Documentation">$(e.fullname)</a>
+ <<
+ }
+ >></dir><<
+}
+
+>>
+ <a href="to-do.html" target="Documentation"><b>To-Do List</b></a><br>
+ </nobr>
+ </font>
+ </p>
+ </body>
+</html>
+<<
+
+######################################################################
+
+## Begin generating package index file
+
+file "packages.html";
+>><html>
+ <head>
+ <title>$project_name -- Packages</title>
+ </head>
+ <body bgcolor="$bgcolor2">
+
+ <center>$company_logo
+ <h1>Documentation for $project_name</h1>
+ </center>
+ <h2>Package List</h2>
+<<
+
+## For each package, generate an index entry.
+
+foreach $p (packages()) {
+ $_ = $p->url;
+ s/\s/%20/g;
+ >><a href = "$_">$(p.name)</a><br>
+ <<
+}
+
+>>
+ <p>
+ <hr size=4>
+ $copyright<br>
+ Generated by <a href="$scandocURL"><b>ScanDoc $majorVersion.$minorVersion</b></a><br>
+ Last Updated: $date<br>
+ </body>
+</html>
+
+<<
+
+######################################################################
+
+## Generate "To-do list"
+
+file "to-do.html";
+>><html>
+ <head>
+ <title>$project_name -- To-Do list</title>
+ </head>
+ <body bgcolor="$bgcolor2">
+
+ $company_logo
+
+ <h1>To-do list for $project_name</h1>
+<<
+
+if (&todolistFiles()) {
+ >><hr size=4><p>
+ <<
+ foreach $f (&todolistFiles()) {
+ my @m = &todolistEntries( $f );
+ if ($f =~ /([^\/]+)$/) { $f = $1; }
+ >><b>$f:</b><ul>
+ <<
+ foreach $text (@m) {
+ if ($text) {
+ print "<li>", &processDescription( $text ), "\n";
+ }
+ }
+ >></ul>
+ <<
+ }
+}
+
+>>
+ <hr size=4>
+ $copyright<br>
+ Generated by <a href="$scandocURL"><b>ScanDoc $majorVersion.$minorVersion</b></a><br>
+ Last Updated: $date<br>
+ </body>
+</html>
+<<
+
+######################################################################
+
+## Generate individual files for each package.
+
+my $p;
+foreach $p (packages()) {
+ file $p->name() . ".html";
+ >><html>
+ <head>
+ <title>$project_name -- $(p.name)</title>
+ </head>
+ <body bgcolor="$bgcolor2">
+ <center>
+ <font size=6><b>$project_name</b></font>
+ <hr size=4><p>
+ </center>
+
+ <h2>Package Name: $(p.name)</h2>
+ <b>
+<<
+
+## Generate class and member index at the top of the file.
+
+foreach $c ($p->classes()) {
+ >><h3><img src="$bullet1_image" width=18 height=17 align=texttop>
+ <a href="$(c.url)">$(c.fullname)</h3></a>
+ <ul>
+ <<
+ foreach $m ($c->members()) {
+ >><li><a href="$(m.url)">$(m.longname)</a>
+ <<
+ }
+ >></ul>
+ <<
+}
+
+>>
+</b>
+<<
+
+## Generate detailed class documentation
+foreach $c ($p->classes()) {
+ ## Output searchable keyword list
+ if ($c->keywords()) {
+ print "<!-- ", $c->keywords(), " -->\n";
+ }
+
+ >><hr size="4">
+ <a name="$(c.anchor)"></a>
+ <h1>$(c.fullname)</h1>
+ <table bgcolor="ffffff" border="0" cellspacing="4">
+ <tr>
+ <th align=center colspan=2>
+ </th>
+ </tr>
+ <<
+
+ # Output author tag
+ if ($c->author()) {
+ >><tr><th width=20% align=right>Author:</th><<
+ >><td>$(c.author)</td></tr><<
+ }
+
+ # Output package version
+ if ($c->version()) {
+ >><tr><th width=20% align=right>Version:</th><<
+ >><td>$(c.version)</td></tr><<
+ }
+
+ # Output Source file
+ if ($c->sourcefile()) {
+ >><tr><th width=20% align=right>Source:</th><<
+ >><td>$(c.sourcefile)</td></tr><<
+ }
+
+ # Output base class list
+ if ($c->baseclasses()) {
+ >><tr><th width=20% align=right>Base classes:</th>
+ <td><<
+ my @t = ();
+ foreach $b ($c->baseclasses()) {
+ my $name = $b->name();
+ if ($url = $b->url()) {
+ push @t, "<a href=\"$url\">$name</a>";
+ }
+ else { push @t, $name; }
+ }
+ print join( ', ', @t );
+ >></td></tr>
+ <<
+ }
+
+ # Output subclasses list
+ if ($c->subclasses()) {
+ >><tr><th width=20% align=right>Subclasses:</th>
+ <td><<
+ my @t = ();
+ foreach $s ($c->subclasses()) {
+ my $name = $s->name();
+ if ($url = $s->url()) {
+ push @t, "<a href=\"$url\">$name</a>";
+ }
+ else { push @t, $name; }
+ }
+ print join( ', ', @t );
+ >></td></tr><<
+ }
+
+ # Output main class description
+ >></tr>
+ </table>
+ <p>
+ <<
+ print &processDescription( $c->description() );
+
+ # Output "see also" information
+ if ($c->seealso()) {
+ >><p><dt><b>See Also</b><dd>
+ <<
+ my @r = ();
+ foreach $a ($c->seealso()) {
+ my $name = $a->name();
+ if ($url = $a->url()) {
+ push @r, "<a href=\"$url\">$name</a>";
+ }
+ else { push @r, $name; }
+ }
+ print join( ',', @r );
+ >><p>
+ <<
+ }
+
+ # Output class member index
+ if ($c->members()) {
+ print "<h2>Member Index</h2>\n";
+ print "<ul>";
+ foreach $m ($c->members()) {
+ >><li><a href="$(m.url)">$(m.fullname)</a>
+ <<
+ }
+ >></ul><<
+ }
+
+ # Output class member variable documentation
+ if ($c->membervars()) {
+ print "<h2>Class Variables</h2>\n";
+ print "<blockquote>\n";
+ foreach $m ($c->membervars()) { &variable( $m ); }
+ print "</blockquote>\n";
+ }
+
+ # Output class member function documentation
+ if ($c->memberfuncs()) {
+ print "<h2>Class Methods</h2>\n";
+ print "<blockquote>\n";
+ foreach $m ($c->memberfuncs()) { &function( $m ); }
+ print "</blockquote>\n";
+ }
+}
+
+# Output global variables
+if ($p->globalvars()) {
+ >><h2>Global Variables</h2>
+ <blockquote>
+ <<
+ foreach $m ($p->globalvars()) { &variable( $m ); }
+ print "</blockquote>\n";
+}
+
+# Output global functions
+if ($p->globalfuncs()) {
+ >><h2>Global Functions</h2>
+ <blockquote>
+ <<
+ foreach $m ($p->globalfuncs()) { &function( $m ); }
+ print "</blockquote>\n";
+}
+
+>>
+ <hr size=4>
+ $copyright<br>
+ Generated by <a href="$scandocURL"><b>ScanDoc $majorVersion.$minorVersion</b></a><br>
+ Last Updated: $date<br>
+ </body>
+</html>
+<<
+} # end of foreach (packages) loop
+
+######################################################################
+
+## Subroutine to generate documentation for a member function or global function
+
+sub function {
+ local ($f) = @_;
+
+ if ($f->keywords()) {
+ >><!-- $(f.keywords) -->
+ <<
+ }
+ >>
+ <a name="$(f.anchor)"></a>
+ <dl>
+ <dt>
+ <b><img src="$bullet2_image" width=19 height=17 align=texttop>$(f.fullname);</b>
+ <dd>
+ <<
+ print &processDescription( $f->description() );
+ >>
+ <p><dl>
+ <<
+ if ($f->params()) {
+ >>
+ <dt><b>Parameters</b><dd>
+ <table width="85%">
+ <<
+ foreach $a ($f->params()) {
+ >><tr valign=top><th align=right>
+ $(a.name)</th><td><<
+ print &processDescription( $a->description() );
+ >></td></tr>
+ <<
+ }
+ >></table>
+ <<
+ }
+
+ if ($f->returnValue()) {
+ >><dt><b>Return Value</b>
+ <dd><<
+ print &processDescription( $f->returnValue() );
+ >><p><<
+ }
+
+ if ($f->exceptions()) {
+ >><dt><b>Exceptions</b><dd>
+ <table width=85%><tr><td colspan=2><hr size=3></td></tr>
+ <<
+ foreach $a ($f->exceptions()) {
+ >><tr valign=top><th align=right>
+ $(a.name)</th><td><<
+ print &processDescription( $a->description() );
+ >></td></tr>
+ <<
+ }
+ >><tr><td colspan=2><hr size=3></td></tr></table>
+ <<
+ }
+
+ if ($f->seealso()) {
+ >><dt><b>See Also</b><dd>
+ <<
+ my @r = ();
+ foreach $a ($f->seealso()) {
+ my $name = $a->name();
+ if ($url = $a->url()) {
+ push @r, "<a href=\"$url\">$name</a>";
+ }
+ else { push @r, $name; }
+ }
+ print join( ',', @r );
+ >><p><<
+ }
+ >></dl></dl>
+ <<
+}
+
+######################################################################
+
+## Subroutine to generate documentation for a member variable or global variable.
+
+sub variable {
+ local ($v) = @_;
+
+ if ($v->keywords()) {
+ print "<!-- $(v.keywords) -->";
+ }
+
+ >>
+ <a name="$(v.name)"></a>
+ <dl><dt>
+ <b><img src="$bullet2_image" width=19 height=17 align=texttop>$(v.fullname);</b>
+ <dd>
+ <<print &processDescription( $v->description() );>>
+ <p><dl>
+ <<
+ if ($v->seealso()) {
+ >><dt><b>See Also</b><dd>
+ <<
+ $comma = 0;
+ foreach $a ($v->seealso()) {
+ if ($comma) { print ","; }
+ $comma = 1;
+ >><a href="$(a.url)">$(a.name)</a>
+ <<
+ }
+ >><p>
+ <<
+ }
+ >></dl></dl>
+ <<
+}
+
+######################################################################
+
+sub processDescription {
+ local ($_) = @_;
+
+ s/^\s+//; # Remove whitespace from beginning
+ s/\s+$/\n/; # Remove whitespace from end
+ s/\n\n/<p>\n/g; # Replace multiple CR's with paragraph markers
+ s:\@heading(.*)\n:<p><h2>$1</h2>:; # Handle heading text
+
+ # Handle embedded image tags
+ s:\@caution:<p><img src=\"${image_directory}/caution.gif\" align=left>:;
+ s:\@warning:<p><img src=\"${image_directory}/warning.gif\" align=left>:;
+ s:\@bug:<p><img src=\"${image_directory}/bug.gif\">:;
+ s:\@tip:<p><img src=\"${image_directory}/tip.gif\">:;
+
+ return $_;
+}
diff --git a/build/httpd_roll_release b/build/httpd_roll_release
new file mode 100755
index 0000000000..9930ec0a6d
--- /dev/null
+++ b/build/httpd_roll_release
@@ -0,0 +1,115 @@
+#!/bin/sh
+
+if [ "x$1" = "xhelp" ]; then
+ echo "Usage: ./httpd_roll_release tag log_name [user]"
+ echo "tag the tag to use when checking out the repository"
+ echo "log_name the name of a file to log the results to."
+ echo "user An optional user name to use when siging the release"
+ exit
+else
+ TAG=$1
+fi
+
+LOG_NAME=`pwd`/$2
+
+USER=$3
+
+REPO="httpd-2.0"
+WORKING_DIR=`echo "$REPO" | sed -e 's/[\-\.]/_/g'`
+WORKING_TAG=`echo "$TAG" | sed -e 's/APACHE_2_0_/_/'`
+WORKING_DIR="$WORKING_DIR$WORKING_TAG"
+
+START_DIR=`echo "$PWD"`
+
+# Check out the correct repositories.
+echo "Checking out repository $REPO into $WORKING_DIR using tag $TAG"
+
+umask 022
+echo Checking out httpd-2.0 > $LOG_NAME
+cvs checkout -r $TAG -d $WORKING_DIR $REPO >> $LOG_NAME
+cd $WORKING_DIR/srclib
+echo "Checking out apr and apr-util" >> $LOG_NAME
+cvs checkout -r $TAG apr apr-util >> $LOG_NAME
+cd $START_DIR/$WORKING_DIR
+
+# Make sure the master site's FAQ is up-to-date. It doesn't hurt to do this
+# all the time. :-)
+echo "Updating the site's FAQ"
+(cd /www/www.apache.org/docs-2.0/misc ; cvs update)
+
+# Now update the FAQ in the tarball
+rm -f docs/manual/misc/FAQ*.html
+links -source http://www.apache.org/docs-2.0/misc/FAQ.html > docs/manual/misc/FAQ.html
+
+# Create the configure scripts
+echo "Creating the configure script"
+cd $START_DIR/$WORKING_DIR
+
+echo >> $LOG_NAME
+echo "Running ./buildconf" >> $LOG_NAME
+./buildconf >> $LOG_NAME
+rm -f ltconfig ltmain.sh config.sub config.guess
+cp /usr/local/share/libtool/ltconfig .
+cp /usr/local/share/libtool/ltmain.sh .
+cp /usr/local/share/libtool/config.sub .
+cp /usr/local/share/libtool/config.guess .
+
+# Remove any files we don't distribute with our code
+rm -f STATUS
+
+echo >> $LOG_NAME
+echo "Removing files that we don't distribute" >> $LOG_NAME
+find . -name ".cvsignore" -exec rm {} \; >> $LOG_NAME
+find . -type d -name "test" | xargs rm -rf >> $LOG_NAME
+find . -type d -name "CVS" | xargs rm -rf >> $LOG_NAME
+
+# expand SSI directives in the manual
+echo "Making sure people can read the manual (expanding SSI's)"
+
+echo >> $LOG_NAME
+echo "Making sure people can read the manual (expanding SSI's)" >> $LOG_NAME
+( cd docs/manual ; chmod +x expand.pl ; ./expand.pl ; rm ./expand.pl ) >> $LOG_NAME
+
+# Time to roll the tarball
+echo "Rolling the tarballs"
+
+cd $START_DIR
+echo >> $LOG_NAME
+echo "Rolling the tarball" >> $LOG_NAME
+tar cvf $WORKING_DIR-alpha.tar $WORKING_DIR >> $LOG_NAME
+cp -p $WORKING_DIR-alpha.tar x$WORKING_DIR-alpha.tar
+gzip -9 $WORKING_DIR-alpha.tar
+mv x$WORKING_DIR-alpha.tar $WORKING_DIR-alpha.tar
+compress $WORKING_DIR-alpha.tar
+
+# Test the tarballs
+echo "Testing the tarball"
+
+echo >> $LOG_NAME
+echo "Testing the tarball $WORKING_DIR-alpha.tar.gz" >> $LOG_NAME
+gunzip -c $WORKING_DIR-alpha.tar.gz | tar tvf - >> $LOG_NAME
+zcat $WORKING_DIR-alpha.tar.Z | tar tvf - >> $LOG_NAME
+
+# remember the CHANGES file
+echo "Copying the CHANGES file to this directory"
+cp $WORKING_DIR/CHANGES .
+
+# cleanup
+echo "Cleaning up my workspace"
+rm -fr $WORKING_DIR
+
+if [ "x$USER" != "x" ]; then
+ USER="-u $USER"
+fi
+
+echo Tagging the tarballs
+
+echo "Taggig the tarballs" >> $LOG_NAME
+pgp -sba $WORKING_DIR-alpha.tar.gz $USER
+pgp -sba $WORKING_DIR-alpha.tar.Z $USER
+
+pgp $WORKING_DIR-alpha.tar.gz.asc $WORKING_DIR-alpha.tar.gz >> $LOG_NAME
+pgp $WORKING_DIR-alpha.tar.Z.asc $WORKING_DIR-alpha.tar.Z >> $LOG_NAME
+
+echo "Don't forget to make the tarballs available by copying them to the"
+echo "/www/dev.apache.org/dist directory."
diff --git a/build/sysv_makefile b/build/sysv_makefile
new file mode 100755
index 0000000000..ef37030dde
--- /dev/null
+++ b/build/sysv_makefile
@@ -0,0 +1,71 @@
+#! /bin/sh
+# ====================================================================
+# The Apache Software License, Version 1.1
+#
+# Copyright (c) 2000 The Apache Software Foundation. All rights
+# reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. The end-user documentation included with the redistribution,
+# if any, must include the following acknowledgment:
+# "This product includes software developed by the
+# Apache Software Foundation (http://www.apache.org/)."
+# Alternately, this acknowledgment may appear in the software itself,
+# if and wherever such third-party acknowledgments normally appear.
+#
+# 4. The names "Apache" and "Apache Software Foundation" must
+# not be used to endorse or promote products derived from this
+# software without prior written permission. For written
+# permission, please contact apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache",
+# nor may "Apache" appear in their name, without prior written
+# permission of the Apache Software Foundation.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Software Foundation. For more
+# information on the Apache Software Foundation, please see
+# <http://www.apache.org/>.
+#
+# The build environment was provided by Sascha Schumann.
+#
+
+# cwd must be top_srcdir
+test -f build/sysv_makefile || exit 2
+
+test -f bsd_converted || exit 1
+
+tmpfile=`mktemp /tmp/sysv_makefile.XXXXXX` || exit 1
+for i in build/*.mk; do
+ sed 's/^\.include "\(.*\)"/include \1/' $i >$tmpfile \
+ && cp $tmpfile $i
+done
+rm -f $tmpfile
+
+rm bsd_converted
+exit 0
diff --git a/config.layout b/config.layout
new file mode 100644
index 0000000000..d79741e234
--- /dev/null
+++ b/config.layout
@@ -0,0 +1,251 @@
+##
+## config.layout -- APACI Pre-defined Installation Path Layouts
+##
+## Hints:
+## - layouts can be loaded with APACI's --with-layout=ID option
+## - when no --with-layout option is given, the default layout is `Apache'
+## - a trailing plus character (`+') on paths is replaced with a
+## `/<target>' suffix where <target> is the the argument from
+## option --target (defaults to `httpd').
+##
+
+# Classical Apache path layout.
+<Layout Apache>
+ prefix: /usr/local/apache
+ exec_prefix: $prefix
+ bindir: $exec_prefix/bin
+ sbindir: $exec_prefix/bin
+ libexecdir: $exec_prefix/libexec
+ mandir: $prefix/man
+ sysconfdir: $prefix/conf
+ datadir: $prefix
+ iconsdir: $datadir/icons
+ htdocsdir: $datadir/htdocs
+ cgidir: $datadir/cgi-bin
+ includedir: $prefix/include
+ localstatedir: $prefix
+ runtimedir: $localstatedir/logs
+ logfiledir: $localstatedir/logs
+ proxycachedir: $localstatedir/proxy
+</Layout>
+
+# GNU standards conforming path layout.
+# See FSF's GNU project `make-stds' document for details.
+<Layout GNU>
+ prefix: /usr/local
+ exec_prefix: $prefix
+ bindir: $exec_prefix/bin
+ sbindir: $exec_prefix/sbin
+ libexecdir: $exec_prefix/libexec
+ mandir: $prefix/man
+ sysconfdir: $prefix/etc+
+ datadir: $prefix/share+
+ iconsdir: $datadir/icons
+ htdocsdir: $datadir/htdocs
+ cgidir: $datadir/cgi-bin
+ includedir: $prefix/include+
+ localstatedir: $prefix/var+
+ runtimedir: $localstatedir/run
+ logfiledir: $localstatedir/log
+ proxycachedir: $localstatedir/proxy
+</Layout>
+
+# Apache binary distribution path layout
+<Layout BinaryDistribution>
+ prefix: /usr/local/apache
+ exec_prefix:
+ bindir: bin
+ sbindir: bin
+ libexecdir: libexec
+ mandir: man
+ sysconfdir: conf
+ datadir:
+ iconsdir: icons
+ htdocsdir: htdocs
+ cgidir: cgi-bin
+ includedir: include
+ localstatedir:
+ runtimedir: logs
+ logfiledir: logs
+ proxycachedir: proxy
+</Layout>
+
+# Mac OS X Server (Rhapsody)
+<Layout Mac OS X Server>
+ prefix: /Local/Library/WebServer
+ exec_prefix: /usr
+ bindir: $exec_prefix/bin
+ sbindir: $exec_prefix/sbin
+ libexecdir: /System/Library/Apache/Modules
+ mandir: $exec_prefix/share/man
+ sysconfdir: $prefix/Configuration
+ datadir: $prefix
+ iconsdir: /System/Library/Apache/Icons
+ htdocsdir: $datadir/Documents
+ cgidir: $datadir/CGI-Executables
+ includedir: /System/Library/Frameworks/Apache.framework/Versions/1.3/Headers
+ localstatedir: /var
+ runtimedir: $prefix/Logs
+ logfiledir: $prefix/Logs
+ proxycachedir: $prefix/ProxyCache
+</Layout>
+
+# Darwin/Mac OS Layout
+<Layout Darwin>
+ prefix: /usr
+ exec_prefix: $prefix
+ bindir: $exec_prefix/bin
+ sbindir: $exec_prefix/sbin
+ libexecdir: $exec_prefix/libexec+
+ mandir: $prefix/share/man
+ datadir: /Library/WebServer
+ sysconfdir: /etc+
+ iconsdir: $prefix/share/httpd/icons
+ htdocsdir: $datadir/Documents
+ cgidir: $datadir/CGI-Executables
+ includedir: $prefix/include+
+ localstatedir: /var
+ runtimedir: $localstatedir/run
+ logfiledir: $localstatedir/log+
+ proxycachedir: $runtimedir/proxy
+</Layout>
+
+# RedHat 5.x layout
+<Layout RedHat>
+ prefix: /usr
+ exec_prefix: $prefix
+ bindir: $prefix/bin
+ sbindir: $prefix/sbin
+ libexecdir: $prefix/lib/apache
+ mandir: $prefix/man
+ sysconfdir: /etc/httpd/conf
+ datadir: /home/httpd
+ iconsdir: $datadir/icons
+ htdocsdir: $datadir/html
+ cgidir: $datadir/cgi-bin
+ includedir: $prefix/include/apache
+ localstatedir: /var
+ runtimedir: $localstatedir/run
+ logfiledir: $localstatedir/log/httpd
+ proxycachedir: $localstatedir/cache/httpd
+</Layout>
+
+# According to the /opt filesystem conventions
+<Layout opt>
+ prefix: /opt/apache
+ exec_prefix: $prefix
+ bindir: $exec_prefix/bin
+ sbindir: $exec_prefix/sbin
+ libexecdir: $exec_prefix/libexec
+ mandir: $prefix/man
+ sysconfdir: /etc$prefix
+ datadir: $prefix/share
+ iconsdir: $datadir/icons
+ htdocsdir: $datadir/htdocs
+ cgidir: $datadir/cgi-bin
+ includedir: $prefix/include
+ localstatedir: /var$prefix
+ runtimedir: $localstatedir/run
+ logfiledir: $localstatedir/logs
+ proxycachedir: $localstatedir/proxy
+</Layout>
+
+# BeOS layout...
+<Layout beos>
+ prefix: /boot/home/apache
+ exec_prefix: $prefix
+ bindir: $exec_prefix/bin
+ sbindir: $exec_prefix/bin
+ libexecdir: $exec_prefix/libexec
+ mandir: $prefix/man
+ sysconfdir: $prefix/conf
+ datadir: $prefix
+ iconsdir: $datadir/icons
+ htdocsdir: $datadir/htdocs
+ cgidir: $datadir/cgi-bin
+ includedir: $prefix/include
+ localstatedir: $prefix
+ runtimedir: $localstatedir/logs
+ logfiledir: $localstatedir/logs
+ proxycachedir: $localstatedir/proxy
+</Layout>
+
+# SuSE 6.x layout
+<Layout SuSE>
+ prefix: /usr
+ exec_prefix: $prefix
+ bindir: $prefix/bin
+ sbindir: $prefix/sbin
+ libexecdir: $prefix/lib/apache
+ mandir: $prefix/man
+ sysconfdir: /etc/httpd
+ datadir: /usr/local/httpd
+ iconsdir: $datadir/icons
+ htdocsdir: $datadir/htdocs
+ cgidir: $datadir/cgi-bin
+ includedir: $prefix/include/apache
+ localstatedir: /var
+ runtimedir: $localstatedir/run
+ logfiledir: $localstatedir/log/httpd
+ proxycachedir: $localstatedir/cache/httpd
+</Layout>
+
+# BSD/OS layout
+<Layout BSDI>
+ prefix: /var/www
+ exec_prefix: /usr/contrib
+ bindir: $exec_prefix/bin
+ sbindir: $exec_prefix/bin
+ libexecdir: $exec_prefix/libexec/apache
+ mandir: $exec_prefix/man
+ sysconfdir: $prefix/conf
+ datadir: $prefix
+ iconsdir: $datadir/icons
+ htdocsdir: $datadir/htdocs
+ cgidir: $datadir/cgi-bin
+ includedir: $exec_prefix/include/apache
+ localstatedir: /var
+ runtimedir: $localstatedir/run
+ logfiledir: $localstatedir/log/httpd
+ proxycachedir: $localstatedir/proxy
+</Layout>
+
+# Solaris 8 Layout
+<Layout Solaris>
+ prefix: /usr/apache
+ exec_prefix: $prefix
+ bindir: $exec_prefix/bin
+ sbindir: $exec_prefix/bin
+ libexecdir: $exec_prefix/libexec
+ mandir: $exec_prefix/man
+ sysconfdir: /etc/apache
+ datadir: /var/apache
+ iconsdir: $datadir/icons
+ htdocsdir: $datadir/htdocs
+ cgidir: $datadir/cgi-bin
+ includedir: $exec_prefix/include
+ localstatedir: $prefix
+ runtimedir: /var/run
+ logfiledir: $datadir/logs
+ proxycachedir: $datadir/proxy
+</Layout>
+
+# OpenBSD Layout
+<Layout OpenBSD>
+ prefix: /var/www
+ exec_prefix: /usr
+ bindir: $exec_prefix/bin
+ sbindir: $exec_prefix/sbin
+ libexecdir: $exec_prefix/lib/apache/modules
+ mandir: $exec_prefix/share/man
+ sysconfdir: $prefix/conf
+ datadir: $prefix
+ iconsdir: $prefix/icons
+ htdocsdir: $prefix/htdocs
+ cgidir: $prefix/cgi-bin
+ includedir: $exec_prefix/lib/apache/include
+ localstatedir: $prefix
+ runtimedir: $prefix/logs
+ logfiledir: $prefix/logs
+ proxycachedir: $prefix/proxy
+</Layout>
diff --git a/docs/STATUS b/docs/STATUS
new file mode 100644
index 0000000000..d9b26c2f9a
--- /dev/null
+++ b/docs/STATUS
@@ -0,0 +1,76 @@
+Apache HTTP Server 2.0 Documentation Status File.
+
+If you are interested in helping accomplish some of the tasks on this
+list or otherwise improving the documentation, please join the
+apache-docs mailing list by mailing to
+apache-docs-subscribe@apache.org
+
+- man pages
+ - Some of the man pages need to be updated for 2.0. At least
+ the httpd man page appears to be outdated, and perhaps other.
+ After this is done, the manual/programs/ versions can be
+ regenerated.
+
+- XHTML conversion
+ Status: James A Sutherland <jas88@cam.ac.uk> was going
+ to spearhead this, but we haven't heard anything
+ lately. Some work is still needed
+ to determine what the SSIs should look like.
+
+- MPM documentation
+ - Each MPM needs to have a documentation file in manual/mod/
+ which lists the directives it provides, and some details
+ about its operation.
+ Status: Initial outlines done. Much more details need to be
+ filled in.
+ - Non unix/windows MPMs still need to be completed.
+ - perchild MPM needs some docs.
+
+- Merging of changes in 1.3.
+ - There have been many changes in the 1.3 docs which haven't
+ been propagated into 2.0.
+ Things which need to be merged: ???
+
+- Cleaning.
+ - We could use a list of all the docs that can be axed out of 2.0
+ because they are redundant or irrelevant.
+ Status:
+ Some suggestions on files to axe. Feel free to comment.
+ manual/cgi-path.html (should be documented in mod_cgi.html instead)
+ manual/process-model.html (documented in MPMs, eventually)
+ platform/perf-* (are these relevant anymore?)
+
+
+ - Individual docs will need some cleanup.
+ Status: What docs still need to be touched here?
+ - invoking.html has had a first-pass cleaning done.
+ - misc/perf-tuning.html - needs major rewrite for 2.0
+ - misc/FAQ.html - some old questions could be remove
+ - is a new format in order?
+ - misc/tutorials.html - mostly not relevant to 2.0
+ - misc/stopping.html
+ - dso.html
+ - misc/rewriteguide.html - needs cleaning in 1.3 and 2.0
+ - misc/known_client_problems.html - mostly ancient
+ - howto/* - some syntax has changed in 2.0 due to
+ filtering and mod_cgid
+
+- New build process.
+ - The new build process is autoconf based, so manual/install.html
+ will need to be completely rewritten. Someone with enough
+ patience could probably figure out most of the important stuff
+ by trial and error.
+ Status: Ryan's "Building and Installing Apache 2.0" might be
+ a starting place.
+
+- Documentation of new features.
+ - This will probably require more input from new-httpd, since
+ not many people here follow the development process close
+ enough to know what is going on.
+ Status: Ryan has two ApacheToday articles which may be useful
+ for this.
+ New features which need documentation:
+ - filters !!!
+
+- Translations
+ Status: ???
diff --git a/docs/conf/highperformance-std.conf b/docs/conf/highperformance-std.conf
new file mode 100755
index 0000000000..d3a5540435
--- /dev/null
+++ b/docs/conf/highperformance-std.conf
@@ -0,0 +1,63 @@
+# Ha, you're reading this config file looking for the easy way out!
+# "how do I make my apache server go really really fast??"
+# Well you could start by reading the htdocs/manual/misc/perf-tuning.html
+# page. But, we'll give you a head start.
+#
+# This config file is small, it is probably not what you'd expect on a
+# full featured internet webserver with multiple users. But it's
+# probably a good starting point for any folks interested in testing
+# performance.
+#
+# To run this config you'll need to use something like:
+# httpd -f @@ServerRoot@@/conf/highperformance.conf
+
+Port 80
+ServerRoot @@ServerRoot@@
+DocumentRoot @@ServerRoot@@/htdocs
+
+User nobody
+# If you're not on Linux, you'll probably need to change Group
+Group nobody
+
+<IfModule prefork.c>
+MaxClients 8
+StartServers 5
+MinSpareServers 5
+MaxSpareServers 10
+</IfModule>
+
+<IfModule mpmt_pthread.c>
+MaxClients 8
+StartServers 1
+MinSpareThreads 5
+MaxSpareThreads 10
+ThreadsPerChild 20
+</IfModule>
+
+# Assume no memory leaks at all
+MaxRequestsPerChild 0
+
+# it's always nice to know the server has started
+ErrorLog logs/error_log
+
+# Some benchmarks require logging, which is a good requirement. Uncomment
+# this if you need logging.
+#TransferLog logs/access_log
+
+# Disable symlink protection and htaccess files, they chew far too much.
+<Directory />
+ AllowOverride none
+ Options FollowSymLinks
+ # If this was a real internet server you'd probably want to
+ # uncomment these:
+ #order deny,allow
+ #deny from all
+</Directory>
+
+# If this was a real internet server you'd probably want to uncomment this:
+#<Directory "@@ServerRoot@@/htdocs">
+# order allow,deny
+# allow from all
+#</Directory>
+
+# OK that's enough hints. Read the documentation if you want more.
diff --git a/docs/docroot/apache_pb2.gif b/docs/docroot/apache_pb2.gif
new file mode 100644
index 0000000000..4e67c7113c
--- /dev/null
+++ b/docs/docroot/apache_pb2.gif
Binary files differ
diff --git a/docs/docroot/apache_pb2_ani.gif b/docs/docroot/apache_pb2_ani.gif
new file mode 100644
index 0000000000..fc41c03610
--- /dev/null
+++ b/docs/docroot/apache_pb2_ani.gif
Binary files differ
diff --git a/docs/docroot/index.html.el b/docs/docroot/index.html.el
new file mode 100644
index 0000000000..371354f9c6
--- /dev/null
+++ b/docs/docroot/index.html.el
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EL">
+<HTML>
+ <HEAD>
+ <TITLE>ÄïêéìáóôéêÞ Óåëßäá ãéá ôçí ÅãêáôÜóôáóç ôïõ Apache</TITLE>
+ </HEAD>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+ <BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+ >
+
+
+<P>
+ÅÜí ìðïñåßôå íá äåßôå áõôÞ ôç óåëßäá, ôüôå ç åãêáôÜóôáóç ôïõ ëïãéóìéêïý ôïõ <A HREF="http://httpd.apache.org/">ÅîõðçñåôçôÞ WWW Apache</A> óå áõôü ôï óýóôçìá Þôáí åðéôõ÷Þò. Ìðïñåßôå ôþñá íá ðñïóèÝóåôå ðåñéå÷üìåíï óå áõôü ôïí êáôÜëïãï êáé íá áíôéêáôáóôÞóåôå áõôÞ ôç óåëßäá.
+
+<P><HR WIDTH="50%" SIZE="8">
+
+<H2 ALIGN="CENTER">ÂëÝðåôå áõôÞ ôç óåëßäá áíôß ãéá ôï äéêôõáêü ôüðï ðïõ ðåñéìÝíáôå;</H2>
+
+<P>
+ÁõôÞ ç óåëßäá âñßóêåôáé åäþ ãéáôß ï äéá÷åéñéóôÞò áõôïý ôïõ äéêôõáêïý ôüðïõ ôñïðïðïßçóå ôéò ñõèìßóåéò óôïí åîõðçñåôçôÞ Apache. Ðáñáêáëþ <STRONG>åðéêïéíùíÞóôå ìå ôïí õðåýèõíï ãéá ôç äéá÷åßñéóç ôïõ ðáñüíôïò åîõðçñåôçôÞ.</STRONG> Ôï ºäñõìá Ëïãéóìéêïý Apache (The Apache Software Foundation) Ýãñáøå ôï ëïãéóìéêü ôïõ åîõðçñåôçôÞ WWW ôï ïðïßï ÷ñçóéìïðïéåß ï äéá÷åéñéóôÞò áõôïý ôïõ äéêôõáêïý ôüðïõ. Ôï ºäñõìá üìùò äåí Ý÷åé êáìéÜ áñìïäéüôçôá ó÷åôéêÞ ìå ôç äéá÷åßñéóç áõôïý ôïõ äéêôõáêïý ôüðïõ êáé äåí ìðïñåß íá âïçèÞóåé óôçí åðßëõóç èåìÜôùí ðïõ Ý÷ïõí ó÷Ýóç ìå ôéò ñõèìßóåéò ôïõ.
+
+<P><HR WIDTH="50%" SIZE="8">
+
+
+<P>
+Ç <A HREF="manual/">ôåêìçñßùóç</A> ôïõ Apache Ý÷åé óõìðåñéëçöèåß óå áõôÞ ôç äéáíïìÞ.
+
+<P>
+Åßóôå åëåýèåñïò/ç íá ÷ñçóéìïðïéÞóåôå ôçí ðáñáêÜôù åéêüíá óå Ýíáí åîõðçñåôçôÞ ôïõ WWW ðïõ âáóßæåôáé óå Apache. Åõ÷áñéóôïýìå ðïõ ÷ñçóéìïðïéåßôå ôïí Apache!
+
+<DIV ALIGN="CENTER"><IMG SRC="apache_pb.gif" ALT=""></DIV>
+</BODY>
+</HTML>
diff --git a/docs/doxygen.conf b/docs/doxygen.conf
new file mode 100644
index 0000000000..41a6e9c4ee
--- /dev/null
+++ b/docs/doxygen.conf
@@ -0,0 +1,13 @@
+PROJECT_NAME=Apache
+
+INPUT=.
+RECURSIVE=YES
+FILE_PATTERNS=*.h
+
+OUTPUT_DIRECTORY=docs/dox
+
+MACRO_EXPANSION=YES
+EXPAND_ONLY_PREDEF=YES
+EXPAND_AS_DEFINED=AP_DECLARE
+
+OPTIMIZE_OUTPUT_FOR_C=YES
diff --git a/docs/icons/apache_pb2.gif b/docs/icons/apache_pb2.gif
new file mode 100644
index 0000000000..4e67c7113c
--- /dev/null
+++ b/docs/icons/apache_pb2.gif
Binary files differ
diff --git a/docs/icons/apache_pb2_ani.gif b/docs/icons/apache_pb2_ani.gif
new file mode 100644
index 0000000000..fc41c03610
--- /dev/null
+++ b/docs/icons/apache_pb2_ani.gif
Binary files differ
diff --git a/docs/manual/configuring.html b/docs/manual/configuring.html
new file mode 100644
index 0000000000..fbe41c2f17
--- /dev/null
+++ b/docs/manual/configuring.html
@@ -0,0 +1,169 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Configuring Apache</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<H1 ALIGN="CENTER">Configuring Apache</H1>
+
+<H2>Main Configuration Files</H2>
+
+<P>Apache is configured by placing <A HREF="mod/directives.html"
+>directives</A> in plain text configuration files. The main
+configuration file is usually called <CODE>httpd.conf</CODE>. The
+location of this file is set at compile-time, but may be overridden
+with the <CODE>-f</CODE> command line flag. Some sites also have
+<CODE>srm.conf</CODE> and <CODE>access.conf</CODE> files for <A
+HREF="http://www.apache.org/info/three-config-files.html">historical
+reasons</A>. In addition, other configuration files may be added using
+the <CODE><A HREF="mod/core.html#include">Include</A></CODE>
+directive. Any directive may be placed in any of these configuration
+files. Changes to the main configuration files are only recognized by
+Apache when it is started or restarted.
+
+<P>
+The server also reads a file containing mime document types; the
+filename is set by the <A HREF="mod/mod_mime.html#typesconfig"
+>TypesConfig</A> directive, and is <CODE>mime.types</CODE> by default.
+
+<H2>Syntax of the Configuration Files</H2>
+
+<P>Directives in the configuration files are case-insensitive, but
+arguments to directives are often case sensitive. Lines which begin
+with the hash character "#" are considered comments, and are ignored.
+Comments may <STRONG>not</STRONG> be included on a line after a configuration
+directive. White space occurring before a directive
+is ignored, so you may indent directives for clarity.
+
+<P>You can check your configuration files for syntax errors without
+starting the server by using <CODE>apachectl configtest</CODE>
+or the <CODE>-t</CODE> command line option.
+
+<H2>Modules</H2>
+
+<P>Apache is a modular server. This implies that only the most basic
+functionality is included in the core server. Extended features are
+available through <A HREF="mod/index-bytype.html">modules</A> which can
+be loaded into Apache. By default, a <A
+HREF="mod/directive-dict.html#Status">base</A> set of modules is
+included in the server at compile-time. If the server is compiled to
+use <A HREF="dso.html">dynamically loaded</A> modules, then modules
+can be compiled separately and added at any time using the <A
+HREF="mod/mod_so.html#loadmodule">LoadModule</A> directive.
+Otherwise, apache must be recompiled to add or remove modules.
+
+<P>To see which modules are currently compiled into the server,
+you can use the <CODE>-l</CODE> command line option.
+
+<H2>Scope of Directives</H2>
+
+<P>Directives placed in the main configuration files apply to the entire
+server. If you wish to change the configuration for only a part of
+the server, you can scope your directives by placing them in
+<CODE><A HREF="mod/core.html#directory">&lt;Directory&gt;</A>,
+<A HREF="mod/core.html#directorymatch">&lt;DirectoryMatch&gt;</A>,
+<A HREF="mod/core.html#files">&lt;Files&gt;</A>,
+<A HREF="mod/core.html#filesmatch">&lt;FilesMatch&gt;</A>,
+<A HREF="mod/core.html#location">&lt;Location&gt;</A>,
+</CODE> and <CODE>
+<A HREF="mod/core.html#locationmatch">&lt;LocationMatch&gt;</A>
+</CODE>
+sections. These sections limit the application of the directives
+which they enclose to particular filesystem locations or URLs. They
+can also be nested, allowing for very fine grained configuration.
+
+<P>Apache has the capability to serve many different websites
+simultaneously. This is called <A HREF="vhosts/">Virtual Hosting</A>.
+Directives can also be scoped by placing them inside
+<CODE><A HREF="mod/core.html#virtualhost">&lt;VirtualHost&gt;</A></CODE>
+sections, so that they will only apply to requests for a particular
+website.
+
+<P>Although most directives can be placed in any of these sections,
+some directives do not make sense in some contexts. For example,
+directives controlling process creation can only be placed in the main
+server context. To find which directives can be placed in which
+sections, check the <A
+HREF="mod/directive-dict.html#Context">Context</A> of the directive.
+For further information, we provide details on <A
+HREF="sections.html">How Directory, Location and Files sections
+work</A>.
+
+<H2>.htaccess Files</H2>
+
+<P>Apache allows for decentralized management of configuration via
+special files placed inside the web tree. The special files are
+usually called <CODE>.htaccess</CODE>, but any name can be specified
+in the <A HREF="mod/core.html#accessfilename"><CODE
+>AccessFileName</CODE></A> directive. Directives placed in
+<CODE>.htaccess</CODE> files apply to the directory where you place
+the file, and all sub-directories. The <CODE>.htaccess</CODE> files
+follow the same syntax as the main configuration files. Since
+<CODE>.htaccess</CODE> files are read on every request, rather than
+only at server startup, changes made in these files take immediate
+effect.
+
+<P>To find which directives can be placed in <CODE>.htaccess</CODE>
+files, check the <A HREF="mod/directive-dict.html#Context">Context</A>
+of the directive. The server administrator further controls what
+directives may be placed in <CODE>.htaccess</CODE> files by
+configuring the <A
+HREF="mod/core.html#allowoverride"><CODE>AllowOverride</CODE></A>
+directive in the main configuration files.
+
+<H2>Log files</H2>
+<H3>security warning</H3>
+Anyone who can write to the directory where Apache is writing a
+log file can almost certainly gain access to the uid that the server is
+started as, which is normally root. Do <EM>NOT</EM> give people write
+access to the directory the logs are stored in without being aware of
+the consequences; see the <A HREF="misc/security_tips.html">security tips</A>
+document for details.
+
+<H3>pid file</H3>
+
+<P>On startup, Apache saves the process id of the parent httpd process to
+the file <CODE>logs/httpd.pid</CODE>. This filename can be changed
+with the <A HREF="mod/core.html#pidfile">PidFile</A> directive. The
+process-id is for use by the administrator in restarting and
+terminating the daemon: on Unix, a HUP or USR1 signal causes the
+daemon to re-read its configuration files and a TERM signal causes it
+to die gracefully; on Windows, use the -k command line option instead.
+For more information see the <A HREF="stopping.html">Stopping and
+Restarting</A> page.
+
+<P>
+If the process dies (or is killed) abnormally, then it will be necessary to
+kill the children httpd processes.
+
+<H3>Error log</H3>
+
+<P>The server will log error messages to a log file, by default
+<CODE>logs/error_log</CODE> on Unix or <CODE>logs/error.log</CODE> on
+Windows and OS/2. The filename can be set using the <A
+HREF="mod/core.html#errorlog">ErrorLog</A> directive; different error
+logs can be set for different <A
+HREF="mod/core.html#virtualhost">virtual hosts</A>.
+
+<H3>Transfer log</H3>
+
+<P>The server will typically log each request to a transfer file, by
+default <CODE>logs/access_log</CODE> on Unix or
+<CODE>logs/access.log</CODE> on Windows and OS/2. The filename can be
+set using a <A HREF="mod/mod_log_config.html#customlog">CustomLog</A>
+directive; different transfer logs can be set for different <A
+HREF="mod/core.html#virtualhost">virtual hosts</A>.
+
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/configuring.html.en b/docs/manual/configuring.html.en
new file mode 100644
index 0000000000..fbe41c2f17
--- /dev/null
+++ b/docs/manual/configuring.html.en
@@ -0,0 +1,169 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Configuring Apache</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<H1 ALIGN="CENTER">Configuring Apache</H1>
+
+<H2>Main Configuration Files</H2>
+
+<P>Apache is configured by placing <A HREF="mod/directives.html"
+>directives</A> in plain text configuration files. The main
+configuration file is usually called <CODE>httpd.conf</CODE>. The
+location of this file is set at compile-time, but may be overridden
+with the <CODE>-f</CODE> command line flag. Some sites also have
+<CODE>srm.conf</CODE> and <CODE>access.conf</CODE> files for <A
+HREF="http://www.apache.org/info/three-config-files.html">historical
+reasons</A>. In addition, other configuration files may be added using
+the <CODE><A HREF="mod/core.html#include">Include</A></CODE>
+directive. Any directive may be placed in any of these configuration
+files. Changes to the main configuration files are only recognized by
+Apache when it is started or restarted.
+
+<P>
+The server also reads a file containing mime document types; the
+filename is set by the <A HREF="mod/mod_mime.html#typesconfig"
+>TypesConfig</A> directive, and is <CODE>mime.types</CODE> by default.
+
+<H2>Syntax of the Configuration Files</H2>
+
+<P>Directives in the configuration files are case-insensitive, but
+arguments to directives are often case sensitive. Lines which begin
+with the hash character "#" are considered comments, and are ignored.
+Comments may <STRONG>not</STRONG> be included on a line after a configuration
+directive. White space occurring before a directive
+is ignored, so you may indent directives for clarity.
+
+<P>You can check your configuration files for syntax errors without
+starting the server by using <CODE>apachectl configtest</CODE>
+or the <CODE>-t</CODE> command line option.
+
+<H2>Modules</H2>
+
+<P>Apache is a modular server. This implies that only the most basic
+functionality is included in the core server. Extended features are
+available through <A HREF="mod/index-bytype.html">modules</A> which can
+be loaded into Apache. By default, a <A
+HREF="mod/directive-dict.html#Status">base</A> set of modules is
+included in the server at compile-time. If the server is compiled to
+use <A HREF="dso.html">dynamically loaded</A> modules, then modules
+can be compiled separately and added at any time using the <A
+HREF="mod/mod_so.html#loadmodule">LoadModule</A> directive.
+Otherwise, apache must be recompiled to add or remove modules.
+
+<P>To see which modules are currently compiled into the server,
+you can use the <CODE>-l</CODE> command line option.
+
+<H2>Scope of Directives</H2>
+
+<P>Directives placed in the main configuration files apply to the entire
+server. If you wish to change the configuration for only a part of
+the server, you can scope your directives by placing them in
+<CODE><A HREF="mod/core.html#directory">&lt;Directory&gt;</A>,
+<A HREF="mod/core.html#directorymatch">&lt;DirectoryMatch&gt;</A>,
+<A HREF="mod/core.html#files">&lt;Files&gt;</A>,
+<A HREF="mod/core.html#filesmatch">&lt;FilesMatch&gt;</A>,
+<A HREF="mod/core.html#location">&lt;Location&gt;</A>,
+</CODE> and <CODE>
+<A HREF="mod/core.html#locationmatch">&lt;LocationMatch&gt;</A>
+</CODE>
+sections. These sections limit the application of the directives
+which they enclose to particular filesystem locations or URLs. They
+can also be nested, allowing for very fine grained configuration.
+
+<P>Apache has the capability to serve many different websites
+simultaneously. This is called <A HREF="vhosts/">Virtual Hosting</A>.
+Directives can also be scoped by placing them inside
+<CODE><A HREF="mod/core.html#virtualhost">&lt;VirtualHost&gt;</A></CODE>
+sections, so that they will only apply to requests for a particular
+website.
+
+<P>Although most directives can be placed in any of these sections,
+some directives do not make sense in some contexts. For example,
+directives controlling process creation can only be placed in the main
+server context. To find which directives can be placed in which
+sections, check the <A
+HREF="mod/directive-dict.html#Context">Context</A> of the directive.
+For further information, we provide details on <A
+HREF="sections.html">How Directory, Location and Files sections
+work</A>.
+
+<H2>.htaccess Files</H2>
+
+<P>Apache allows for decentralized management of configuration via
+special files placed inside the web tree. The special files are
+usually called <CODE>.htaccess</CODE>, but any name can be specified
+in the <A HREF="mod/core.html#accessfilename"><CODE
+>AccessFileName</CODE></A> directive. Directives placed in
+<CODE>.htaccess</CODE> files apply to the directory where you place
+the file, and all sub-directories. The <CODE>.htaccess</CODE> files
+follow the same syntax as the main configuration files. Since
+<CODE>.htaccess</CODE> files are read on every request, rather than
+only at server startup, changes made in these files take immediate
+effect.
+
+<P>To find which directives can be placed in <CODE>.htaccess</CODE>
+files, check the <A HREF="mod/directive-dict.html#Context">Context</A>
+of the directive. The server administrator further controls what
+directives may be placed in <CODE>.htaccess</CODE> files by
+configuring the <A
+HREF="mod/core.html#allowoverride"><CODE>AllowOverride</CODE></A>
+directive in the main configuration files.
+
+<H2>Log files</H2>
+<H3>security warning</H3>
+Anyone who can write to the directory where Apache is writing a
+log file can almost certainly gain access to the uid that the server is
+started as, which is normally root. Do <EM>NOT</EM> give people write
+access to the directory the logs are stored in without being aware of
+the consequences; see the <A HREF="misc/security_tips.html">security tips</A>
+document for details.
+
+<H3>pid file</H3>
+
+<P>On startup, Apache saves the process id of the parent httpd process to
+the file <CODE>logs/httpd.pid</CODE>. This filename can be changed
+with the <A HREF="mod/core.html#pidfile">PidFile</A> directive. The
+process-id is for use by the administrator in restarting and
+terminating the daemon: on Unix, a HUP or USR1 signal causes the
+daemon to re-read its configuration files and a TERM signal causes it
+to die gracefully; on Windows, use the -k command line option instead.
+For more information see the <A HREF="stopping.html">Stopping and
+Restarting</A> page.
+
+<P>
+If the process dies (or is killed) abnormally, then it will be necessary to
+kill the children httpd processes.
+
+<H3>Error log</H3>
+
+<P>The server will log error messages to a log file, by default
+<CODE>logs/error_log</CODE> on Unix or <CODE>logs/error.log</CODE> on
+Windows and OS/2. The filename can be set using the <A
+HREF="mod/core.html#errorlog">ErrorLog</A> directive; different error
+logs can be set for different <A
+HREF="mod/core.html#virtualhost">virtual hosts</A>.
+
+<H3>Transfer log</H3>
+
+<P>The server will typically log each request to a transfer file, by
+default <CODE>logs/access_log</CODE> on Unix or
+<CODE>logs/access.log</CODE> on Windows and OS/2. The filename can be
+set using a <A HREF="mod/mod_log_config.html#customlog">CustomLog</A>
+directive; different transfer logs can be set for different <A
+HREF="mod/core.html#virtualhost">virtual hosts</A>.
+
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/configuring.html.ja.jis b/docs/manual/configuring.html.ja.jis
new file mode 100644
index 0000000000..c1b41e5251
--- /dev/null
+++ b/docs/manual/configuring.html.ja.jis
@@ -0,0 +1,247 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>$B@_Dj%U%!%$%k(B</title>
+</head>
+<!-- English revision: 1.8 -->
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<body
+ bgcolor="#FFFFFF"
+ text="#000000"
+ link="#0000FF"
+ vlink="#000080"
+ alink="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<h1 align="CENTER">$B%U%!%$%k$N@_Dj(B</h1>
+
+<ul>
+<li><a href="#main">$B%a%$%s$N@_Dj%U%!%$%k(B</a></li>
+<li><a href="#syntax">$B@_Dj%U%!%$%k$N9=J8(B</a></li>
+<li><a href="#modules">$B%b%8%e!<%k(B</a></li>
+<li><a href="#scope">$B%G%#%l%/%F%#%V$NE,MQHO0O(B</a></li>
+<li><a href="#htaccess">.htaccess $B%U%!%$%k(B</a></li>
+<li><a href="#logs">$B%m%0%U%!%$%k(B</a></li>
+</ul>
+
+<hr>
+
+<h2><a name="main">$B%a%$%s$N@_Dj%U%!%$%k(B</a></h2>
+
+<table border="1"><tr><td valign="top">
+<strong>$B4XO"%b%8%e!<%k(B</strong><br><br>
+<a href="mod/mod_mime.html">mod_mime</a><br>
+</td>
+
+<td valign="top">
+<strong>$B4XO"%G%#%l%/%F%#%V(B</strong><br><br>
+<a href="mod/core.html#ifdefine">&lt;IfDefine&gt;</a><br>
+<a href="mod/core.html#include">Include</a><br>
+<a href="mod/mod_mime.html#typesconfig">TypesConfig</a><br>
+</td></tr></table>
+
+<p>Apache $B$O(B <a href="mod/directives.html">$B%G%#%l%/%F%#%V(B</a> $B$r(B
+$B@_Dj%U%!%$%k$KJ?J8$G=q$/$3$H$K$h$j@_Dj$7$^$9!#%a%$%s$N(B
+$B@_Dj%U%!%$%k$OIaDL$O(B <code>httpd.conf</code> $B$H$$$&L>A0$G$9!#(B
+$B$3$N%U%!%$%k$N0LCV$O%3%s%Q%$%k;~$K@_Dj$5$l$^$9$,!"%3%^%s%I%i%$%s$N(B
+<code>-f</code> $B%U%i%0$K$h$j>e=q$-$G$-$^$9!#$=$N>e!"B>$N@_Dj%U%!%$%k$,(B
+<code><a href="mod/core.html#include">Include</a></code> $B%G%#%l%/%F%#%V(B
+$B$K$h$C$FDI2C$5$l$F$$$k$+$b$7$l$^$;$s!#$I$N%G%#%l%/%F%#%V$b(B
+$B$3$l$i$N@_Dj%U%!%$%k$N$I$l$K$G$bF~$l$i$l$^$9!#(BApache $B$O5/F0;~$+(B
+$B:F5/F0;~$N$_%a%$%s@_Dj%U%!%$%k$NJQ99$rG'<1$7$^$9!#(B</p>
+
+<p>Apache 1.3.13 $B$N?7$7$$5!G=$H$7$F!"@_Dj%U%!%$%k$,<B:]$O%G%#%l%/%H%j$G(B
+$B$"$k$H$-$K$O$=$N%G%#%l%/%H%j$G8+$D$+$C$?$9$Y$F$N%U%!%$%k(B
+($B$H%5%V%G%#%l%/%H%j(B) $B$r2r@O$9$k$H$$$&$b$N$,$"$j$^$9!#MxMQK!$H$7$F$O!"(B
+$B%P!<%A%c%k%[%9%H$rDI2C$9$k$H$-$K!"$=$l$>$l$N%[%9%H$KBP$7$F>.$5$J@_Dj(B
+$B%U%!%$%k$r:n$j!"@_Dj%G%#%l%/%H%j$KCV$/$H$$$&$b$N$,5s$2$i$l$^$9!#(B
+$B$3$&$9$k$H!"C1$K%U%!%$%k$NDI2C!":o=|$r$9$k$3$H$K$h$jA4$/%U%!%$%k$r(B
+$BJT=8$9$k$3$H$J$/%P!<%A%c%k%[%9%H$NDI2C!":o=|$,$G$-$^$9!#$3$l$O<+F02=$r(B
+$B$:$C$H4JC1$K$7$^$9!#(B</p>
+
+<p>
+$B%5!<%P$O(B mime $B%I%-%e%a%s%H%?%$%W$r4^$s$G$$$k%U%!%$%k$bFI$_9~$_$^$9!#(B
+$B%U%!%$%kL>$O(B <a href="mod/mod_mime.html#typesconfig"
+>TypesConfig</a> $B$G@_Dj$5$l!"%G%U%)%k%H$G(B <code>mime.types</code>
+$B$K$J$C$F$$$^$9!#(B</p>
+<hr>
+
+<h2><a name="syntax">$B@_Dj%U%!%$%k$N9=J8(B</a></h2>
+
+<p>Apache $B@_Dj%U%!%$%k$O(B1$B9T$K(B1$B$D$N%G%#%l%/%F%#%V$+$i$J$j$^$9!#(B
+$B%P%C%/%9%i%C%7%e(B "\" $B$O%G%#%l%/%F%#%V$,<!$N9T$K7QB3$7$F$$$k$3$H$r(B
+$B<($9$?$a$K9T$N:G8e$NJ8;z$H$7$F;H$o$l$F$$$k$+$b$7$l$^$;$s!#(B
+$B9T$N:G8e$H%P%C%/%9%i%C%7%e$N4V$KB>$NJ8;z$d6uGr$,$"$C$F$O$$$1$^$;$s!#(B</p>
+
+<p>$B@_Dj%U%!%$%k$N%G%#%l%/%F%#%V$OBgJ8;z>.J8;z$r6hJL$7$^$;$s$,!"(B
+$B0z?t$K$O$7$P$7$P6hJL$9$k$b$N$,$"$j$^$9!#%O%C%7%eJ8;z(B "#" $B$G;O$^$k9T$O(B
+$B%3%a%s%H$H8+$J$5$l$FL5;k$5$l$^$9!#@_Dj%G%#%l%/%F%#%V$N8e$N9T$G$O(B
+$B%3%a%s%H$,4^$^$l$F$$$F$O(B<strong>$B$$$1$^$;$s(B</strong>$B!#%G%#%l%/%F%#%V$N(B
+$BA0$N6u9T$H6uGr$OL5;k$5$l$^$9$N$G!"$o$+$j$d$9$/$9$k$?$a$K%G%#%l%/%F%#%V$r(B
+$B%$%s%G%s%H$9$k$3$H$,$G$-$^$9!#(B</p>
+
+<p>$B@_Dj%U%!%$%k$N9=J8%(%i!<$O(B
+<code>apachectl configtest</code> $B$+%3%^%s%I%i%$%s%*%W%7%g%s(B
+<code>-t</code> $B$r;H$C$FD4$Y$i$l$^$9!#(B</p>
+
+<hr>
+
+<h2><a name="modules">$B%b%8%e!<%k(B</a></h2>
+
+<table border="1"><tr><td valign="top">
+<strong>$B4XO"%b%8%e!<%k(B</strong><br><br>
+<a href="mod/mod_so.html">mod_so</a><br>
+</td>
+<td valign="top">
+<strong>$B4XO"%G%#%l%/%F%#%V(B</strong><br><br>
+<a href="mod/core.html#addmodule">AddModule</a><br>
+<a href="mod/core.html#clearmodulelist">ClearModuleList</a><br>
+<a href="mod/core.html#ifmodule">&lt;IfModule&gt;</a><br>
+<a href="mod/mod_so.html#loadmodule">LoadModule</a><br>
+</td></tr></table>
+
+<p>Apache $B$O%b%8%e!<%k2=$5$l$?%5!<%P$G$9!#%3%"%5!<%P$K$O(B
+$B0lHV4pK\E*$J5!G=$@$1$,4^$^$l$F$$$^$9!#3HD%5!G=$O(B Apache $B$K(B
+$B%m!<%I$5$l$k(B<a href="mod/index-bytype.html">$B%b%8%e!<%k(B</a>$B$H$7$F(B
+$BMxMQ2DG=$G$9!#%G%U%)%k%H$G$O%3%s%Q%$%k;~$K%b%8%e!<%k$N(B<a
+href="mod/module-dict.html#Status">$B4pK\(B</a>$B%;%C%H$,(B
+$B%5!<%P$K4^$^$l$^$9!#%5!<%P$,(B<a href="dso.html">$BF0E*%m!<%I(B</a>$B%b%8%e!<%k$r(B
+$B;H$&$h$&$K%3%s%Q%$%k$5$l$F$$$k>l9g$O!"%b%8%e!<%k$rJL$K%3%s%Q%$%k$7$F!"(B
+$B$$$D$G$b(B <a href="mod/mod_so.html#loadmodule">LoadModule</a>
+$B%G%#%l%/%F%#%V$r;H$C$FDI2C$G$-$^$9!#$=$&$G$J$$>l9g$O!"%b%8%e!<%k$N(B
+$BDI2C$d:o=|$r$9$k$?$a$K$O(B Apache $B$r:F%3%s%Q%$%k$9$kI,MW$,$"$j$^$9!#(B
+$B@_Dj%G%#%l%/%F%#%V$O(B <a
+href="mod/core.html#ifmodule">&lt;IfModule&gt;</a> $B%V%m%C%/$K(B
+$BF~$l$k$3$H$GFCDj$N%b%8%e!<%k$,B8:_$9$k$H$-$@$1(B
+$B@_Dj%U%!%$%k$K4^$^$l$k$h$&$K$9$k$3$H$,$G$-$^$9!#(B</p>
+
+<p>$B%3%^%s%I%i%$%s%*%W%7%g%s(B <code>-l</code> $B$r;H$C$F(B
+$B8=;~E@$G$I$N%b%8%e!<%k$,%5!<%P$K%3%s%Q%$%k$5$l$F$$$k$+$r(B
+$BCN$k$3$H$,$G$-$^$9!#(B
+
+<hr>
+
+<h2><a name="scope">$B%G%#%l%/%F%#%V$NE,MQHO0O(B</a></h2>
+
+<table border="1"><tr><td valign="top">
+<strong>$B4XO"%G%#%l%/%F%#%V(B</strong><br><br>
+<a href="mod/core.html#directory">&lt;Directory&gt;</a><br>
+<a href="mod/core.html#directorymatch">&lt;DirectoryMatch&gt;</a><br>
+<a href="mod/core.html#files">&lt;Files&gt;</a><br>
+<a href="mod/core.html#filesmatch">&lt;FilesMatch&gt;</a><br>
+<a href="mod/core.html#location">&lt;Location&gt;</a><br>
+<a href="mod/core.html#locationmatch">&lt;LocationMatch&gt;</a><br>
+<a href="mod/core.html#virtualhost">&lt;VirtualHost&gt;</a><br>
+</td></tr></table>
+
+<p>$B%a%$%s@_Dj%U%!%$%k$K$"$k%G%#%l%/%F%#%V$O%5!<%PA4BN$KE,MQ$5$l$^$9!#(B
+$B%5!<%P$N0lItJ,$N@_Dj$@$1$rJQ99$7$?$$>l9g$O(B
+<code><a href="mod/core.html#directory">&lt;Directory&gt;</a>,
+<a href="mod/core.html#directorymatch">&lt;DirectoryMatch&gt;</a>,
+<a href="mod/core.html#files">&lt;Files&gt;</a>,
+<a href="mod/core.html#filesmatch">&lt;FilesMatch&gt;</a>,
+<a href="mod/core.html#location">&lt;Location&gt;</a>,
+<a href="mod/core.html#locationmatch">&lt;LocationMatch&gt;</a>
+</code>
+$B%;%/%7%g%s$NCf$KCV$/$3$H$GE,MQHO0O$r7h$a$i$l$^$9!#$3$l$i$N%;%/%7%g%s$O(B
+$B$=$NCf$K$"$k%G%#%l%/%F%#%V$NE,MQHO0O$rFCDj$N%U%!%$%k%7%9%F%`$N0LCV$d(B
+URL $B$K8BDj$7$^$9!#Hs>o$K:YN3EY$N@_Dj$r2DG=$K$9$k$?$a$K!"%;%/%7%g%s$r(B
+$BF~$l;R$K$9$k$3$H$b$G$-$^$9!#(B</p>
+
+<p>Apache $B$OF1;~$KB?$/$N0c$&%&%'%V%5%$%H$r07$&G=NO$,$"$j$^$9!#(B
+$B$3$l$O(B <a href="vhosts/">$B%P!<%A%c%k%[%9%H(B</a> $B$H8F$P$l$F$$$^$9!#(B
+$BFCDj$N%&%'%V%5%$%H$K$N$_E,MQ$5$l$k$h$&$K$9$k$?$a$K!"%G%#%l%/%F%#%V$O(B
+<code><a href="mod/core.html#virtualhost">&lt;VirtualHost&gt;</a></code>
+$B%;%/%7%g%s$NCf$KCV$/$3$H$G$bE,MQHO0O$rJQ$($k$3$H$,$G$-$^$9!#(B</p>
+
+<p>$B$[$H$s$I$N%G%#%l%/%F%#%V$O$I$N%;%/%7%g%s$K$G$b=q$1$^$9$,!"(B
+$BCf$K$O%3%s%F%-%9%H$K$h$C$F$O0UL#$r$J$5$J$$$b$N$b$"$j$^$9!#Nc$($P!"(B
+$B%W%m%;%9$N:n@.$r@)8f$7$F$$$k%G%#%l%/%F%#%V$O%a%$%s%5!<%P$N%3%s%F%-%9%H$K(B
+$B$N$_=q$/$3$H$,$G$-$^$9!#$I$N%G%#%l%/%F%#%V$r$I$N%;%/%7%g%s$K=q$/$3$H$,(B
+$B$G$-$k$+$rCN$k$?$a$K$O%G%#%l%/%F%#%V$N(B
+<a href="mod/directive-dict.html#Context">$B%3%s%F%-%9%H(B</a>$B$rD4$Y$F$/$@$5$$!#(B
+$B>\$7$$>pJs$O!"(B<a
+href="sections.html">Directory, Location, Files $B%;%/%7%g%s$NF0:nK!(B</a>
+$B$K$"$j$^$9!#(B</p>
+
+<hr>
+
+<h2><a name="htaccess">.htaccess $B%U%!%$%k(B</a></h2>
+
+<table border="1"><tr><td valign="top">
+<strong>$B4XO"%G%#%l%/%F%#%V(B</strong><br><br>
+<a href="mod/core.html#accessfilename">AccessFileName</a><br>
+<a href="mod/core.html#allowoverride">AllowOverride</a><br>
+</td></tr></table>
+
+<p>Apache $B$G$O%&%'%V%D%j!<$NCf$KCV$+$l$?FCJL$J%U%!%$%k$r;H$C$F(B
+$BHsCf1{=88"E*$J@_Dj4IM}$r$G$-$^$9!#$=$NFCJL$J%U%!%$%k$OIaDL$O(B
+<code>.htaccess</code> $B$H$$$&L>A0$G!"(B
+<a href="mod/core.html#accessfilename"><code
+>AccessFileName</code></a> $B%G%#%l%/%F%#%V$G$I$s$JL>A0$G$b;XDj$G$-$^$9!#(B
+<code>.htaccess</code> $B%U%!%$%k$K=q$+$l$?%G%#%l%/%F%#%V$O%U%!%$%k$r(B
+$BCV$$$?%G%#%l%/%H%j$H$=$NA4$F$N%5%V%G%#%l%/%H%j$KE,MQ$5$l$^$9!#(B
+<code>.htaccess</code> $B%U%!%$%k$O$9$Y$F$N%j%/%(%9%H$G(B
+$BFI$_9~$^$l$k$?$a!"JQ99$O$9$0$KH?1G$5$l$^$9!#(B
+
+<p>$B$I$N%G%#%l%/%F%#%V$,(B <code>.htaccess</code> $B%U%!%$%k$K=q$1$k$+$r(B
+$BD4$Y$k$K$O!"%G%#%l%/%F%#%V$N(B
+<a href="mod/directive-dict.html#Context">$B%3%s%F%-%9%H(B</a> $B$rD4$Y$F$/$@$5$$!#(B
+$B%5!<%P4IM}<T$O$5$i$K%a%$%s@_Dj%U%!%$%k$N(B <a
+href="mod/core.html#allowoverride"><code>AllowOverride</code></a>
+$B$r@_Dj$9$k$3$H$G$I$N%G%#%l%/%F%#%V$r(B <code>.htaccess</code> $B%U%!%$%k$K(B
+$B=q$1$k$h$&$K$9$k$+$r@)8f$9$k$3$H$,$G$-$^$9!#(B</p>
+
+<hr>
+
+<h2><a name="logs">$B%m%0%U%!%$%k(B</a></h2>
+<!-- XXX: This section should be moved to its own file -->
+<h3>$B%;%-%e%j%F%#$K4X$9$k7Y9p(B</h3>
+<p>
+Apache $B$,%m%0%U%!%$%k$r=q$$$F$$$k%G%#%l%/%H%j$K=q$-9~$a$k?M$OC/$G$b(B
+$B$[$\3N<B$K%5!<%P$,5/F0$5$l$?(B uid $B$N%"%/%;%9$r<hF@$G$-$^$9!#(B
+$B$=$7$F$=$l$OIaDL(B root $B$G$9!#$=$N7k2L$r$h$/$o$+$i$:$K%m%0$,J]4I$5$l$F$$$k(B
+$B%G%#%l%/%H%j$K=q$-9~$_8"8B$rM?$($?$j(B<em>$B$7$J$$(B</em>$B$G$/$@$5$$!#>\:Y$O(B
+<a href="misc/security_tips.html">$B%;%-%e%j%F%#>pJs(B</a>$B$r;2>H$7$F$/$@$5$$!#(B</p>
+
+<h3>pid $B%U%!%$%k(B</h3>
+
+<p>$B5/F0;~$K(B Apache $B$O?F(B httpd $B%W%m%;%9$N%W%m%;%9(B ID $B$r(B
+<code>logs/httpd.pid</code> $B%U%!%$%k$KJ]B8$7$^$9!#$3$N%U%!%$%kL>$O(B
+<a href="mod/core.html#pidfile">PidFile</a>
+$B%G%#%l%/%F%#%V$GJQ99$9$k$3$H$,$G$-$^$9!#%W%m%;%9(B ID $B$O4IM}<T$,%G!<%b%s$r(B
+$B:F5/F0$7$?$j=*N;$7$?$j$9$k$?$a$K;H$$$^$9!#(BUnix $B$G$O(B HUP $B$H(B USR1 $B%7%0%J%k$G(B
+$B%G!<%b%s$,@_Dj%U%!%$%k$r:FFI$_9~$_$7$^$9!#(BTERM $B%7%0%J%k$O%G!<%b%s$r(B
+$BM%2m$K=*N;$5$;$^$9!#(BWindows $B$G$O!"Be$o$j$K%3%^%s%I%i%$%s%*%W%7%g%s(B -k $B$r(B
+$B;H$$$^$9!#(B
+$B>\$7$$>pJs$O(B<a href="stopping.html">$B5/F0$H=*N;(B</a>$B$r;2>H$7$F$/$@$5$$!#(B</p>
+
+<p>
+$B%W%m%;%9$,0[>o=*N;$9$k(B ($B$b$7$/$O(B kill $B$5$l$k(B) $B$H!";R(B httpd $B%W%m%;%9$r(B
+kill $B$9$kI,MW$,$"$j$^$9!#(B</p>
+
+<h3>$B%(%i!<%m%0(B</h3>
+
+<p>$B%5!<%P$O%(%i!<%a%C%;!<%8$r%m%0%U%!%$%k$K5-O?$7$^$9!#%G%U%)%k%H$O!"(BUnix
+$B$G$O(B <code>logs/error_log</code> $B$G(B Windows $B$H(B OS/2 $B$G$O(B
+<code>logs/error.log</code> $B$G$9!#%U%!%$%kL>$O(B <a
+href="mod/core.html#errorlog">ErrorLog</a> $B%G%#%l%/%F%#%V$G@_Dj$G$-$^$9!#(B
+$B0c$&(B<a
+href="mod/core.html#virtualhost">$B%P!<%A%c%k%[%9%H(B</a>
+$B$K0c$&%(%i!<%m%0$r@_Dj$9$k$3$H$,$G$-$^$9!#(B</p>
+
+<h3>$BE>Aw%m%0(B</h3>
+
+<p>$B%5!<%P$OIaDL$=$l$>$l$N%j%/%(%9%H$rE>Aw%U%!%$%k$K%m%0$r<h$j$^$9!#(B
+$B%G%U%)%k%H$O(B Unix $B$G$O(B <code>logs/access_log</code> $B$G(B Windows $B$H(B
+OS/2 $B$G$O(B <code>logs/access.log</code> $B$G$9!#%U%!%$%kL>$O(B
+<a href="mod/mod_log_config.html#customlog">CustomLog</a>
+$B%G%#%l%/%F%#%V$r$G@_Dj$G$-$^$9!#0c$&(B<a
+href="mod/core.html#virtualhost">$B%P!<%A%c%k%[%9%H(B</a>$B$K(B
+$B0c$&E>Aw%m%0$r@_Dj$9$k$3$H$,$G$-$^$9!#(B</p>
+
+
+<!--#include virtual="footer.html" -->
+</body>
+</html>
diff --git a/docs/manual/developer/debugging.html b/docs/manual/developer/debugging.html
new file mode 100644
index 0000000000..2cb210f732
--- /dev/null
+++ b/docs/manual/developer/debugging.html
@@ -0,0 +1,175 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Debugging Memory Allocation in APR</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+
+<H1 ALIGN="CENTER">Debugging Memory Allocation in APR<br></H1>
+
+<p>The allocation mechanism's within APR have a number of debugging
+modes that can be used to assist in finding memory problems. This document describes
+the modes available and gives instructions on activating them.</p>
+
+<ul>
+<li><a href="#options">Available debugging options</a></li>
+<li><a href="#combo">Allowable combinations</a></li>
+<li><a href="#howto">How to activate debugging</a></li>
+</ul>
+
+<hr>
+<a name="options">
+<h2>Allocation Debugging</h2>
+
+<h3>ALLOC_DEBUG</h3>
+<p><em>Debugging support: Define this to enable code which helps detect re-use of freed memory and other such nonsense.</em></p>
+
+<p>The theory is simple. The FILL_BYTE (0xa5) is written over all malloc'd memory as we receive it, and is written over everything that we free up during a clear_pool. We check that blocks on the free list always have the FILL_BYTE in them, and we check during palloc() that the bytes still have FILL_BYTE in them. If you ever see garbage URLs or whatnot containing lots of 0xa5s then you know something used data that's been freed or uninitialized.</p>
+
+<h2>Malloc Support</h2>
+<h3>ALLOC_USE_MALLOC</h3>
+<p><em>If defined all allocations will be done with malloc and free()d appropriately at the end.
+</em></p>
+
+<p>This is intended to be used with something like Electric Fence or Purify to help detect memory problems. Note that if you're using efence then you should also add in ALLOC_DEBUG. But don't add in ALLOC_DEBUG if you're using Purify because ALLOC_DEBUG would hide all the uninitialized read errors that Purify can diagnose.</p>
+
+<h2>Pool Debugging</h2>
+<h3>POOL_DEBUG</h3>
+<p><em>This is intended to detect cases where the wrong pool is used when assigning data to an object in another pool.</em></p>
+
+<p>In particular, it causes the table_{set,add,merge}n routines to check that their arguments are safe for the ap_table_t they're being placed in. It currently only works with the unix multiprocess model, but could be extended to others.</p>
+
+<h2>Table Debugging</h2>
+<h3>MAKE_TABLE_PROFILE</h3>
+<p><em>Provide diagnostic information about make_table() calls which are possibly too small.</em></p>
+
+<p>This requires a recent gcc which supports __builtin_return_address(). The error_log output will be a message such as: </p>
+<pre>table_push: ap_table_t created by 0x804d874 hit limit of 10</pre>
+<p>Use "<em><strong>l *0x804d874</strong></em>" to find the source that corresponds to. It
+ indicates that a ap_table_t allocated by a call at that address has possibly too small an initial ap_table_t size guess.</p>
+
+<h2>Allocation Statistics</h2>
+<h3>ALLOC_STATS</h3>
+<p><em>Provide some statistics on the cost of allocations.</em></p>
+
+<p>This requires a bit of an understanding of how alloc.c works.</p>
+
+<hr>
+
+<a name="combo">
+<h2>Allowable Combinations</h2>
+
+<p>Not all the options outlined above can be activated at the same time. the following table gives more information.</p>
+
+<p align="center">
+<table width="80%">
+<tr>
+<th width="25%">Option 1</th>
+<th width="15%">ALLOC<br>DEBUG</th>
+<th width="15%">ALLOC<br>USE<br>MALLOC</th>
+<th width="15%">POOL<br>DEBUG</th>
+<th width="15%">MAKE<br>TABLE<br>PROFILE</th>
+<th width="15%">ALLOC<br>STATS</th>
+</tr>
+<tr>
+<td>ALLOC_DEBUG</td>
+<td bgcolor="#ff0000">&nbsp;</td>
+<td align="center">No</td>
+<td align="center">Yes</td>
+<td align="center">Yes</td>
+<td align="center">Yes</td>
+</tr>
+<tr>
+<td>ALLOC_USE<br>MALLOC</td>
+<td align="center">No</td>
+<td bgcolor="#ff0000">&nbsp;</td>
+<td align="center">No</td>
+<td align="center">No</td>
+<td align="center">No</td>
+</tr>
+<tr>
+<td>POOL_DEBUG</td>
+<td align="center">Yes</td>
+<td align="center">No</td>
+<td bgcolor="#ff0000">&nbsp;</td>
+<td align="center">Yes</td>
+<td align="center">Yes</td>
+</tr>
+<tr>
+<td>MAKE_TABLE<br>PROFILE</td>
+<td align="center">Yes</td>
+<td align="center">No</td>
+<td align="center">Yes</td>
+<td bgcolor="#ff0000">&nbsp;</td>
+<td align="center">Yes</td>
+</tr>
+<tr>
+<td>ALLOC_STATS</td>
+<td align="center">Yes</td>
+<td align="center">No</td>
+<td align="center">Yes</td>
+<td align="center">Yes</td>
+<td bgcolor="#ff0000">&nbsp;</td>
+</tr>
+
+</table>
+
+<p>Additionally the debugging options are not suitable for multi-threaded versions of the server. When trying to debug with these options the server should be started in single process mode.</p>
+
+<hr>
+
+<a name="howto">
+<h2>Activating Debugging Options</h2>
+<p>The various options for debugging memory are now enabled in the apr_general.h header file in APR. The various options are enabled by uncommenting the define for the option you wish to use. The section of the code currently looks like this <em>(contained in src/lib/apr/inclide/apr_general.h)</em></p>
+
+<pre>
+/*
+#define ALLOC_DEBUG
+#define POOL_DEBUG
+#define ALLOC_USE_MALLOC
+#define MAKE_TABLE_PROFILE
+#define ALLOC_STATS
+*/
+
+typedef struct ap_pool_t {
+ union block_hdr *first;
+ union block_hdr *last;
+ struct cleanup *cleanups;
+ struct process_chain *subprocesses;
+ struct ap_pool_t *sub_pools;
+ struct ap_pool_t *sub_next;
+ struct ap_pool_t *sub_prev;
+ struct ap_pool_t *parent;
+ char *free_first_avail;
+#ifdef ALLOC_USE_MALLOC
+ void *allocation_list;
+#endif
+#ifdef POOL_DEBUG
+ struct ap_pool_t *joined;
+#endif
+ int (*apr_abort)(int retcode);
+ struct datastruct *prog_data;
+}ap_pool_t;
+</pre>
+
+<p>To enable allocation debugging simply move the #define ALLOC_DEBUG above the start of the comments block and rebuild the server.</p>
+
+<h3>NB. In order to use the various options the server MUST be rebuilt after editing the header file.
+</h3>
+
+
+</body>
+</html>
+
+
+
+
+
diff --git a/docs/manual/filter.html b/docs/manual/filter.html
new file mode 100644
index 0000000000..9cdcb201d9
--- /dev/null
+++ b/docs/manual/filter.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Filters - Apache HTTPD</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<H1 ALIGN="CENTER">Filters</H1>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Modules</strong><br><br>
+
+<a href="mod/mod_ext_filter.html">mod_ext_filter</a><br>
+<A HREF="mod/mod_include.html">mod_include</A><br>
+</td>
+<td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/mod_ext_filter.html#extfilterdefine">ExtFilterDefine</a></br>
+<a href="mod/mod_ext_filter.html#extfilteroptions">ExtFilterOptions</a><br>
+<a href="mod/core.html#setinputfilter">SetInputFilter</a><br>
+<a href="mod/core.html#setoutputfilter">SetOutputFilter</a><br>
+</td>
+</tr></table>
+
+
+<p>A <em>filter</em> is a process which is applied to data that is
+sent or received by the server. Data sent by clients to the server
+is processed by <em>input filters</em> while data sent by the
+server to the client is processed by <em>output filters</em>.
+Multiple filters can be applied to the data, and the order
+of the filters can be explicitly specified. In addition,
+since filters apply to all content, they allow for flexible
+manipulation of data such as processing the output of
+CGI scripts for Server Side Includes.</p>
+
+<p>The set of filters which apply to data can be manipulated
+with the <code>SetInputFilter</code> and <code>SetOutputFilter</code>
+directives.</p>
+
+<p>The only filter currently included with the Apache distribution
+is the <code>INCLUDE</code> filter which is provided by
+<a href="mod/mod_include.html">mod_include</a> to process output
+for Server Side Includes. There is also an experimental module
+called <a href="mod/mod_ext_filter.html">mod_ext_filter</a>
+which allows for external programs to be defined as filters.</p>
+
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
+
diff --git a/docs/manual/filter.html.en b/docs/manual/filter.html.en
new file mode 100644
index 0000000000..9cdcb201d9
--- /dev/null
+++ b/docs/manual/filter.html.en
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Filters - Apache HTTPD</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<H1 ALIGN="CENTER">Filters</H1>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Modules</strong><br><br>
+
+<a href="mod/mod_ext_filter.html">mod_ext_filter</a><br>
+<A HREF="mod/mod_include.html">mod_include</A><br>
+</td>
+<td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/mod_ext_filter.html#extfilterdefine">ExtFilterDefine</a></br>
+<a href="mod/mod_ext_filter.html#extfilteroptions">ExtFilterOptions</a><br>
+<a href="mod/core.html#setinputfilter">SetInputFilter</a><br>
+<a href="mod/core.html#setoutputfilter">SetOutputFilter</a><br>
+</td>
+</tr></table>
+
+
+<p>A <em>filter</em> is a process which is applied to data that is
+sent or received by the server. Data sent by clients to the server
+is processed by <em>input filters</em> while data sent by the
+server to the client is processed by <em>output filters</em>.
+Multiple filters can be applied to the data, and the order
+of the filters can be explicitly specified. In addition,
+since filters apply to all content, they allow for flexible
+manipulation of data such as processing the output of
+CGI scripts for Server Side Includes.</p>
+
+<p>The set of filters which apply to data can be manipulated
+with the <code>SetInputFilter</code> and <code>SetOutputFilter</code>
+directives.</p>
+
+<p>The only filter currently included with the Apache distribution
+is the <code>INCLUDE</code> filter which is provided by
+<a href="mod/mod_include.html">mod_include</a> to process output
+for Server Side Includes. There is also an experimental module
+called <a href="mod/mod_ext_filter.html">mod_ext_filter</a>
+which allows for external programs to be defined as filters.</p>
+
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
+
diff --git a/docs/manual/handler.html.ja.jis b/docs/manual/handler.html.ja.jis
new file mode 100644
index 0000000000..917aabdccd
--- /dev/null
+++ b/docs/manual/handler.html.ja.jis
@@ -0,0 +1,146 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Apache $B$N%O%s%I%i$N;HMQ(B</title>
+</HEAD>
+<!-- English revision: 1.22 -->
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<body
+ bgcolor="#FFFFFF"
+ text="#000000"
+ link="#0000FF"
+ vlink="#000080"
+ alink="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<h1 align="CENTER">Apache $B$N%O%s%I%i$N;HMQ(B</h1>
+
+<ul>
+<li><a href="#definition">$B%O%s%I%i$H$O(B</a></li>
+<li><a href="#examples">$BNc(B</a></li>
+<li><a href="#programmer">$B%W%m%0%i%^8~$1$N%a%b(B</a></li>
+</ul>
+
+<hr>
+<h2><a name="definition">$B%O%s%I%i$H$O(B</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>$B4XO"%b%8%e!<%k(B</strong><br><br>
+
+<a href="mod/mod_actions.html">mod_actions</a><br>
+<a href="mod/mod_asis.html">mod_asis</a><br>
+<a href="mod/mod_cgi.html">mod_cgi</a><br>
+<a href="mod/mod_imap.html">mod_imap</a><br>
+<a href="mod/mod_info.html">mod_info</a><br>
+<a href="mod/mod_mime.html">mod_mime</a><br>
+<a href="mod/mod_negotiation.html">mod_negotiation</a><br>
+<a href="mod/mod_status.html">mod_status</a><br>
+</td>
+<td valign="top">
+<strong>$B4XO"%G%#%l%/%F%#%V(B</strong><br><br>
+
+<a href="mod/mod_actions.html#action">Action</a><br>
+<a href="mod/mod_mime.html#addhandler">AddHandler</a><br>
+<a href="mod/mod_mime.html#removehandler">RemoveHandler</a><br>
+<a href="mod/mod_mime.html#sethandler">SetHandler</a><br>
+</td>
+</tr></table>
+
+
+<p>$B!V%O%s%I%i!W$H$O!"%U%!%$%k$,8F$P$l$?$H$-$K<B9T$5$l$kF0:n$N(B Apache $B$K$*$1$k(B
+$BFbItI=8=$G$9!#DL>o!"%U%!%$%k$O%U%!%$%k7?$K4p$E$$$?0EL[$N(B
+$B%O%s%I%i$,$"$j$^$9!#IaDL$O$9$Y$F$N%U%!%$%k$OC1$K%5!<%P$K07$o$l$^$9$,!"(B
+$B%U%!%$%k%?%$%W$NCf$K$OJL$K!V%O%s%I%k!W(B ($BLuCm(B: $B07$&(B) $B$5$l$k$b$N$b$"$j$^$9!#(B</p>
+
+<p>Apache 1.1 $B$G$O!"%O%s%I%i$rL@<(E*$K;HMQ$9$k5!G=$,DI2C$5$l$^$7$?!#(B
+$B%U%!%$%k$N3HD%;R$dCV$$$F$$$k>l=j$K4p$E$$$F!"%U%!%$%k7?$H4X78$J$/%O%s%I%i$r(B
+$B;XDj$9$k$3$H$,$G$-$^$9!#$3$l$O$h$jM%2m$J2r7hK!$H$$$&E@$H!"%U%!%$%k$K(B
+$B%?%$%W(B<strong>$B$H(B</strong>$B%O%s%I%i$NN>J}$r4XO"IU$1$k$3$H$,$G$-$k$H$$$&(B
+$BE@$GM%$l$F$$$^$9!#(B (<a
+href="mod/mod_mime.html#multipleext">$BJ#?t$N3HD%;R$N$"$k%U%!%$%k(B</a>
+$B$b;2>H$7$F$/$@$5$$(B)$B!#(B</p>
+
+<p>$B%O%s%I%i$O%5!<%P$KAH$_9~$s$@$j!"%b%8%e!<%k$H$7$F4^$a$?$j!"(B
+<a href="mod/mod_actions.html#action">Action</a> $B%G%#%l%/%F%#%V$H$7$F(B
+$BDI2C$7$?$j$9$k$3$H$,$G$-$^$9!#0J2<$OI8=`G[I[$KAH$_9~$^$l$F$$$k%O%s%I%i$G$9!#(B</p>
+
+<ul>
+<li><strong>default-handler</strong>:
+ <code>default_handelr()</code> $B$r;H$C$F%U%!%$%k$rAw$j$^$9!#(B
+ $B@EE*$J%3%s%F%s%D$r07$&$H$-$K%G%U%)%k%H$G;HMQ$5$l$k%O%s%I%i$G$9!#(B
+ (core)
+<li><strong>send-as-is</strong>:
+ HTTP $B%X%C%@$N$"$k%U%!%$%k$r$=$N$^$^Aw$j$^$9!#(B
+ (<a href="mod/mod_asis.html">mod_asis</a>)
+<li><strong>cgi-script</strong>:
+ $B%U%!%$%k$r(B CGI $B%9%/%j%W%H$H$7$F07$$$^$9!#(B
+ (<a href="mod/mod_cgi.html">mod_cgi</a>)
+<li><strong>imap-file</strong>:
+ $B%$%a!<%8%^%C%W$N%k!<%k%U%!%$%k$H$7$F2r@O$7$^$9!#(B
+ (<a href="mod/mod_imap.html">mod_imap</a>)
+<li><strong>server-info</strong>:
+ $B%5!<%P$N@_Dj>pJs$r<hF@$7$^$9!#(B
+ (<a href="mod/mod_info.html">mod_info</a>)
+<li><strong>server-status</strong>:
+ $B%5!<%P$N>uBVJs9p$r<hF@$7$^$9!#(B
+ (<a href="mod/mod_status.html">mod_status</a>)
+<li><strong>type-map</strong>:
+ $B%3%s%F%s%H%M%4%7%(!<%7%g%s$N$?$a$N%?%$%W%^%C%W$H$7$F2r@O$7$^$9!#(B
+ (<a href="mod/mod_negotiation.html">mod_negotiation</a>)
+</ul>
+
+<hr>
+
+<h2><a name="examples">$BNc(B</a></h2>
+
+<h3>CGI $B%9%/%j%W%H$rMQ$$$F@EE*$J%3%s%F%s%D$rJQ99$9$k(B</h3>
+
+<p>$B0J2<$N%G%#%l%/%F%#%V$K$h$C$F!"3HD%;R$,(B <code>html</code> $B$G$"$k%U%!%$%k$O(B
+<code>footer.pl</code> CGI $B%9%/%j%W%H$r5/F0$9$k$h$&$K$J$j$^$9!#(B</p>
+
+<pre>
+ Action add-footer /cgi-bin/footer.pl
+ AddHandler add-footer .html
+</pre>
+
+<p>CGI $B%9%/%j%W%H$OK>$^$7$$=$@5$dDI2C$r9T$J$C$F!"85!9MW5a$5$l$?J8=q(B
+($B4D6-JQ?t(B <code>PATH_TRANSLATED</code> $B$G;X$5$l$F$$$^$9(B) $B$rAw$k@UG$$,$"$j$^$9!#(B</p>
+
+<h3>HTTP $B%X%C%@$N$"$k%U%!%$%k(B</h3>
+
+<p>$B0J2<$N%G%#%l%/%F%#%V$O(B <code>send-as-is</code> $B%O%s%I%i$r;HMQ$9$k(B
+$B$h$&$K;X<($7$^$9!#$3$N%O%s%I%i$O<+J,<+?H$N(B HTTP $B%X%C%@$r;}$C$F$$$k%U%!%$%k$K(B
+$B;HMQ$5$l$^$9!#$3$3$G$O!"3HD%;R$K4X$o$i$:!"(B<code>/web/htdocs/asis</code>
+$B%G%#%l%/%H%j$K$"$kA4$F$N%U%!%$%k$O(B <code>send-as-is</code> $B%O%s%I%i$K$h$C$F(B
+$B07$o$l$^$9!#(B</p>
+
+<pre>
+ &lt;Directory /web/htdocs/asis&gt;
+ SetHandler send-as-is
+ &lt;/Directory&gt;
+</pre>
+
+<hr>
+
+<h2><a name="programmer">$B%W%m%0%i%^8~$1$N%a%b(B</a></h2>
+
+<p>$B%O%s%I%i$N5!G=$r<BAu$9$k$?$a$K!"MxMQ$9$k$HJXMx$+$b$7$l$J$$$b$N$,(B
+<a href="misc/API.html">Apache API</a> $B$KDI2C$5$l$^$7$?!#(B
+$B>\$7$/8@$&$H!"(B<code>request_rec</code> $B9=B$BN$K?7$7$$%l%3!<%I$,(B
+$BDI2C$5$l$?$H$$$&$3$H$G$9!#(B</p>
+<pre>
+ char *handler
+</pre>
+<p>$B$b$7%b%8%e!<%k$,%O%s%I%i$K4X$o$j$?$$>l9g!"$d$i$J$1$l$P$J$i$J$$$3$H$O!"(B
+$B%j%/%(%9%H$,(B <code>invoke_handler</code> $B%9%F!<%8$KC#$9$k0JA0$K(B
+<code>r-&gt;handler</code> $B$r@_Dj$9$k$3$H$@$1$G$9!#(B
+$B%O%s%I%i$O%3%s%F%s%H%?%$%W$NBe$o$j$K%O%s%I%iL>$r;H$&$h$&$K$J$C$F$$$k$3$H0J30$O!"(B
+$B0JA0$HF1$8$h$&$K<BAu$5$l$F$$$^$9!#I,$:MW5a$5$l$F$$$k$o$1$G$O$"$j$^$;$s$,!"(B
+$B%a%G%#%"%?%$%W$NL>A06u4V$r?/$5$J$$$h$&$K!"%O%s%I%i$NL>A0$K$O%9%i%C%7%e(B
+$B$r4^$^$J$$!"%@%C%7%e(B ($BLuCm(B: "-") $B$GJ,N%$5$l$?L>A0$rIU$1$k=,47$K$J$C$F$$$^$9!#(B</P>
+
+<!--#include virtual="footer.html" -->
+</body>
+</html>
diff --git a/docs/manual/howto/cgi.html b/docs/manual/howto/cgi.html
new file mode 100644
index 0000000000..fadbceb41c
--- /dev/null
+++ b/docs/manual/howto/cgi.html
@@ -0,0 +1,499 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Apache Tutorial: Dynamic Content with CGI</title>
+<link rev="made" href="mailto:rbowen@rcbowen.com">
+</head>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#000080"
+alink="#FF0000">
+<!--#include virtual="header.html" -->
+<h1 align="CENTER">Dynamic Content with CGI</h1>
+
+<a name="__index__"></a> <!-- INDEX BEGIN -->
+
+
+<ul>
+<li><a href="#dynamiccontentwithcgi">Dynamic Content with
+CGI</a></li>
+
+<li><a href="#configuringapachetopermitcgi">Configuring Apache to
+permit CGI</a>
+
+<ul>
+<li><a href="#scriptalias">ScriptAlias</a></li>
+
+<li><a href="#cgioutsideofscriptaliasdirectories">CGI outside of
+ScriptAlias directories</a>
+
+<ul>
+<li><a href="#explicitlyusingoptionstopermitcgiexecution">Explicitly using
+Options to permit CGI execution</a></li>
+
+<li><a href="#htaccessfiles">.htaccess files</a></li>
+</ul>
+</li>
+</ul>
+</li>
+
+<li><a href="#writingacgiprogram">Writing a CGI program</a>
+
+<ul>
+<li><a href="#yourfirstcgiprogram">Your first CGI program</a></li>
+</ul>
+</li>
+
+<li><a href="#butitsstillnotworking">But it's still not
+working!</a>
+
+<ul>
+<li><a href="#filepermissions">File permissions</a></li>
+
+<li><a href="#pathinformation">Path information</a></li>
+
+<li><a href="#syntaxerrors">Syntax errors</a></li>
+
+<li><a href="#errorlogs">Error logs</a></li>
+</ul>
+</li>
+
+<li><a href="#whatsgoingonbehindthescenes">What's going on behind
+the scenes?</a>
+
+<ul>
+<li><a href="#environmentvariables">Environment variables</a></li>
+
+<li><a href="#stdinandstdout">STDIN and STDOUT</a></li>
+</ul>
+</li>
+
+<li><a href="#cgimoduleslibraries">CGI modules/libraries</a></li>
+
+<li><a href="#formoreinformation">For more information</a></li>
+</ul>
+
+<!-- INDEX END -->
+<hr>
+<h2><a name="dynamiccontentwithcgi">Dynamic Content with
+CGI</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Modules</strong><br><br>
+
+<a href="../mod/mod_alias.html">mod_alias</a><br>
+<a href="../mod/mod_cgi.html">mod_cgi</a><br>
+
+</td><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="../mod/mod_mime.html#addhandler">AddHandler</a><br>
+<A HREF="../mod/core.html#options">Options</a><br>
+<a href="../mod/mod_alias.html#scriptalias">ScriptAlias</a><br>
+
+</td></tr></table>
+
+<p>The CGI (Common Gateway Interface) defines a way for a web server
+to interact with external content-generating programs, which are often
+referred to as CGI programs or CGI scripts. It is the simplest, and
+most common, way to put dynamic content on your web site. This
+document will be an introduction to setting up CGI on your Apache web
+server, and getting started writing CGI programs.</p>
+
+<hr>
+<h2><a name="configuringapachetopermitcgi">Configuring Apache to
+permit CGI</a></h2>
+
+<p>In order to get your CGI programs to work properly, you'll need to
+have Apache configured to permit CGI execution. There are several ways
+to do this.</p>
+
+<h3><a name="scriptalias">ScriptAlias</a></h3>
+
+<p>The <code>ScriptAlias</code> directive tells Apache that a
+particular directory is set aside for CGI programs. Apache will assume
+that every file in this directory is a CGI program, and will attempt to
+execute it, when that particular resource is requested by a client.</p>
+
+<p>The <code>ScriptAlias</code> directive looks like:</p>
+
+<pre>
+ ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin/
+</pre>
+
+<p>The example shown is from your default <code>httpd.conf</code>
+configuration file, if you installed Apache in the default location.
+The <code>ScriptAlias</code> directive is much like the
+<code>Alias</code> directive, which defines a URL prefix that is to
+mapped to a particular directory. <code>Alias</code> and
+<code>ScriptAlias</code> are usually used for directories that are
+outside of the <code>DocumentRoot</code> directory. The difference
+between <code>Alias</code> and <code>ScriptAlias</code> is that
+<code>ScriptAlias</code> has the added meaning that everything under
+that URL prefix will be considered a CGI program. So, the example above
+tells Apache that any request for a resource beginning with
+<code>/cgi-bin/</code> should be served from the directory
+<code>/usr/local/apache/cgi-bin/</code>, and should be treated as a CGI
+program.</p>
+
+<p>For example, if the URL
+<code>http://dev.rcbowen.com/cgi-bin/test.pl</code> is requested,
+Apache will attempt to execute the file
+<code>/usr/local/apache/cgi-bin/test.pl</code> and return the output.
+Of course, the file will have to exist, and be executable, and return
+output in a particular way, or Apache will return an error message.</p>
+
+<h3><a name="cgioutsideofscriptaliasdirectories">CGI outside of
+ScriptAlias directories</a></h3>
+
+<p>CGI programs are often restricted to <code>ScriptAlias</code>'ed
+directories for security reasons. In this way, administrators can
+tightly control who is allowed to use CGI programs. However, if the
+proper security precautions are taken, there is no reason why
+CGI programs cannot be run from arbitrary directories. For example,
+you may wish to let users have web content in their home directories
+with the <code>UserDir</code> directive. If they want to have their
+own CGI programs, but don't have access to the main
+<code>cgi-bin</code> directory, they will need to be able to run CGI
+programs elsewhere.</p>
+
+<h3><a name="explicitlyusingoptionstopermitcgiexecution">Explicitly using
+Options to permit CGI execution</a></h3>
+
+<p>You could explicitly use the <code>Options</code> directive, inside
+your main server configuration file, to specify that CGI execution was
+permitted in a particular directory:</p>
+
+<pre>
+ &lt;Directory /usr/local/apache/htdocs/somedir&gt;
+ Options +ExecCGI
+ &lt;/Directory&gt;
+</pre>
+
+<p>The above directive tells Apache to permit the execution of CGI
+files. You will also need to tell the server what files are CGI files.
+The following <code>AddHandler</code> directive tells the server
+to treat all files with the <code>cgi</code> or <code>pl</code>
+extension as CGI programs:</p>
+
+<pre>
+ AddHandler cgi-script cgi pl
+</pre>
+
+<h3><a name="htaccessfiles">.htaccess files</a></h3>
+
+<p>A <code>.htaccess</code> file is a way to set configuration
+directives on a per-directory basis. When Apache serves a resource, it
+looks in the directory from which it is serving a file for a file
+called <code>.htaccess</code>, and, if it finds it, it will apply
+directives found therein. <code>.htaccess</code> files can be permitted
+with the <code>AllowOverride</code> directive, which specifies what
+types of directives can appear in these files, or if they are not
+allowed at all. To permit the directive we will need for this purpose,
+the following configuration will be needed in your main server
+configuration:</p>
+
+<pre>
+ AllowOverride Options
+</pre>
+
+<p>In the <code>.htaccess</code> file, you'll need the following
+directive:</p>
+
+<pre>
+ Options +ExecCGI
+</pre>
+
+<p>which tells Apache that execution of CGI programs is permitted in
+this directory.</p>
+
+<hr>
+<h2><a name="writingacgiprogram">Writing a CGI program</a></h2>
+
+<p>There are two main differences between ``regular'' programming, and
+CGI programming.</p>
+
+<p>First, all output from your CGI program must be preceded by a
+MIME-type header. This is HTTP header that tells the client what sort
+of content it is receiving. Most of the time, this will look like:</p>
+
+<pre>
+ Content-type: text/html
+</pre>
+
+<p>Secondly, your output needs to be in HTML, or some other format that
+a browser will be able to display. Most of the time, this will be HTML,
+but occasionally you might write a CGI program that outputs a gif
+image, or other non-HTML content.</p>
+
+<p>Apart from those two things, writing a CGI program will look a lot
+like any other program that you might write.</p>
+
+<h3><a name="yourfirstcgiprogram">Your first CGI program</a></h3>
+
+<p>The following is an example CGI program that prints one line to your
+browser. Type in the following, save it to a file called
+<code>first.pl</code>, and put it in your <code>cgi-bin</code>
+directory.</p>
+
+<pre>
+ #!/usr/bin/perl
+ print "Content-type: text/html\r\n\r\n";
+ print "Hello, World.";
+</pre>
+
+<p>Even if you are not familiar with Perl, you should be able to see
+what is happening here. The first line tells Apache (or whatever shell
+you happen to be running under) that this program can be executed by
+feeding the file to the interpreter found at the location
+<code>/usr/bin/perl</code>. The second line prints the content-type
+declaration we talked about, followed by two carriage-return newline
+pairs. This puts a blank line after the header, to indicate the end of
+the HTTP headers, and the beginning of the body. The third line prints
+the string ``Hello, World.'' And that's the end of it.</p>
+
+<p>If you open your favorite browser and tell it to get the address</p>
+
+<pre>
+ http://www.example.com/cgi-bin/first.pl
+</pre>
+
+<p>or wherever you put your file, you will see the one line
+<code>Hello, World.</code> appear in your browser window. It's not very
+exciting, but once you get that working, you'll have a good chance of
+getting just about anything working.</p>
+
+<hr>
+<h2><a name="butitsstillnotworking">But it's still not
+working!</a></h2>
+
+<p>There are four basic things that you may see in your browser when
+you try to access your CGI program from the web:</p>
+
+<dl>
+<dt>The output of your CGI program</dt>
+<dd>Great! That means everything worked fine.<br><br></dd>
+
+<dt>The source code of your CGI program or a "POST Method Not Allowed"
+message</dt>
+<dd>That means that you have not properly configured
+Apache to process your CGI program. Reread the section on <a
+href="#configuringapachetopermitcgi">configuring Apache</a> and try to
+find what you missed.<br><br></dd>
+
+<dt>A message starting with "Forbidden"</dt> <dd>That means that there
+is a permissions problem. Check the <a href="#errorlogs">Apache
+error log</a> and the section below on <a
+href="#filepermissions">file permissions</a>.<br><br></dd>
+
+<dt>A message saying "Internal Server Error"</dt> <dd>If you check the
+<a href="#errorlogs">Apache error log</a>, you will probably find
+that it says "Premature end of script headers", possibly along with an
+error message generated by your CGI program. In this case, you will
+want to check each of the below sections to see what might be preventing
+your CGI program from emitting the proper HTTP headers.</dd>
+</dl>
+
+
+<h3><a name="filepermissions">File permissions</a></h3>
+
+<p>Remember that the server does not run as you. That is, when the
+server starts up, it is running with the permissions of an unprivileged
+user - usually ``nobody'', or ``www'' - and so it will need extra
+permissions to execute files that are owned by you. Usually, the way to
+give a file sufficient permissions to be executed by ``nobody'' is to
+give everyone execute permission on the file:</p>
+
+<pre>
+ chmod a+x first.pl
+</pre>
+
+<p>Also, if your program reads from, or writes to, any other files,
+those files will need to have the correct permissions to permit
+this.</p>
+
+<p>The exception to this is when the server is configured to use <a
+href="../suexec.html">suexec</a>. This program allows CGI programs to
+be run under different user permissions, depending on which virtual
+host or user home directory they are located in. Suexec has very
+strict permission checking, and any failure in that checking will
+result in your CGI programs failing with an "Internal Server Error".
+In this case, you will need to check the suexec log file to see what
+specific security check is failing.</p>
+
+<h3><a name="pathinformation">Path information</a></h3>
+
+<p>When you run a program from your command line, you have certain
+information that is passed to the shell without you thinking about it.
+For example, you have a path, which tells the shell where it can look
+for files that you reference.</p>
+
+<p>When a program runs through the web server as a CGI program, it does
+not have that path. Any programs that you invoke in your CGI program
+(like 'sendmail', for example) will need to be specified by a full
+path, so that the shell can find them when it attempts to execute your
+CGI program.</p>
+
+<p>A common manifestation of this is the path to the script interpreter
+(often <code>perl</code>) indicated in the first line of your CGI
+program, which will look something like:</p>
+
+<pre>
+ #!/usr/bin/perl
+</pre>
+
+<p>Make sure that this is in fact the path to the interpreter.</p>
+
+<h3><a name="syntaxerrors">Syntax errors</a></h3>
+
+<p>Most of the time when a CGI program fails, it's because of a problem
+with the program itself. This is particularly true once you get the
+hang of this CGI stuff, and no longer make the above two mistakes.
+Always attempt to run your program from the command line before you
+test if via a browser. This will eliminate most of your problems.</p>
+
+<h3><a name="errorlogs">Error logs</a></h3>
+
+<p>The error logs are your friend. Anything that goes wrong generates
+message in the error log. You should always look there first. If the
+place where you are hosting your web site does not permit you access to
+the error log, you should probably host your site somewhere else. Learn
+to read the error logs, and you'll find that almost all of your
+problems are quickly identified, and quickly solved.</p>
+
+<hr>
+<h2><a name="whatsgoingonbehindthescenes">What's going on behind
+the scenes?</a></h2>
+
+<p>As you become more advanced in CGI programming, it will become
+useful to understand more about what's happening behind the scenes.
+Specifically, how the browser and server communicate with one another.
+Because although it's all very well to write a program that prints
+``Hello, World.'', it's not particularly useful.</p>
+
+<h3><a name="environmentvariables">Environment variables</a></h3>
+
+<p>Environment variables are values that float around you as you use
+your computer. They are useful things like your path (where the
+computer searches for a the actual file implementing a command when you
+type it), your username, your terminal type, and so on. For a full list
+of your normal, every day environment variables, type <code>env</code>
+at a command prompt.</p>
+
+<p>During the CGI transaction, the server and the browser also set
+environment variables, so that they can communicate with one another.
+These are things like the browser type (Netscape, IE, Lynx), the server
+type (Apache, IIS, WebSite), the name of the CGI program that is being
+run, and so on.</p>
+
+<p>These variables are available to the CGI programmer, and are half of
+the story of the client-server communication. The complete list of
+required variables is at <a href=
+"http://hoohoo.ncsa.uiuc.edu/cgi/env.html">http://hoohoo.ncsa.uiuc.edu/cgi/env.html</a></p>
+
+<p>This simple Perl CGI program will display all of the environment
+variables that are being passed around. Two similar programs are
+included in the <code>cgi-bin</code> directory of the Apache
+distribution. Note that some variables are required, while others are
+optional, so you may see some variables listed that were not in the
+official list. In addition, Apache provides many different ways for
+you to <a href="../env.html">add your own environment variables</a> to
+the basic ones provided by default.</p>
+
+<pre>
+ #!/usr/bin/perl
+ print "Content-type: text/html\n\n";
+ foreach $key (keys %ENV) {
+ print "$key --&gt; $ENV{$key}&lt;br&gt;";
+ }
+</pre>
+
+<h3><a name="stdinandstdout">STDIN and STDOUT</a></h3>
+
+<p>Other communication between the server and the client happens over
+standard input (<code>STDIN</code>) and standard output
+(<code>STDOUT</code>). In normal everyday context, <code>STDIN</code>
+means the keyboard, or a file that a program is given to act on, and
+<code>STDOUT</code> usually means the console or screen.</p>
+
+<p>When you <code>POST</code> a web form to a CGI program, the data in
+that form is bundled up into a special format and gets delivered to
+your CGI program over <code>STDIN</code>. The program then can process
+that data as though it was coming in from the keyboard, or from a
+file</p>
+
+<p>The ``special format'' is very simple. A field name and its value
+are joined together with an equals (=) sign, and pairs of values are
+joined together with an ampersand (&amp;). Inconvenient characters like
+spaces, ampersands, and equals signs, are converted into their hex
+equivalent so that they don't gum up the works. The whole data string
+might look something like:</p>
+
+<pre>
+ name=Rich%20Bowen&amp;city=Lexington&amp;state=KY&amp;sidekick=Squirrel%20Monkey
+</pre>
+
+<p>You'll sometimes also see this type of string appended to the a URL.
+When that is done, the server puts that string into the environment
+variable called <code>QUERY_STRING</code>. That's called a
+<code>GET</code> request. Your HTML form specifies whether a
+<code>GET</code> or a <code>POST</code> is used to deliver the data, by
+setting the <code>METHOD</code> attribute in the <code>FORM</code>
+tag.</p>
+
+<p>Your program is then responsible for splitting that string up into
+useful information. Fortunately, there are libraries and modules
+available to help you process this data, as well as handle other of the
+aspects of your CGI program.</p>
+
+<hr>
+<h2><a name="cgimoduleslibraries">CGI modules/libraries</a></h2>
+
+<p>When you write CGI programs, you should consider using a code
+library, or module, to do most of the grunt work for you. This leads to
+fewer errors, and faster development.</p>
+
+<p>If you're writing CGI programs in Perl, modules are available on <a
+href="http://www.cpan.org/">CPAN</a>. The most popular module for this
+purpose is CGI.pm. You might also consider CGI::Lite, which implements
+a minimal set of functionality, which is all you need in most
+programs.</p>
+
+<p>If you're writing CGI programs in C, there are a variety of options.
+One of these is the CGIC library, from <a href=
+"http://www.boutell.com/cgic/">http://www.boutell.com/cgic/</a></p>
+
+<hr>
+<h2><a name="formoreinformation">For more information</a></h2>
+
+<p>There are a large number of CGI resources on the web. You can
+discuss CGI problems with other users on the Usenet group
+comp.infosystems.www.authoring.cgi. And the -servers mailing list from
+the HTML Writers Guild is a great source of answers to your questions.
+You can find out more at <a href=
+"http://www.hwg.org/lists/hwg-servers/">http://www.hwg.org/lists/hwg-servers/</a></p>
+
+<p>And, of course, you should probably read the CGI specification,
+which has all the details on the operation of CGI programs. You can
+find the original version at the <a href=
+"http://hoohoo.ncsa.uiuc.edu/cgi/interface.html">NCSA</a> and there is
+an updated draft at the <a
+href="http://web.golux.com/coar/cgi/">Common Gateway Interface RFC
+project</a>.</p>
+
+<p>When you post a question about a CGI problem that you're having,
+whether to a mailing list, or to a newsgroup, make sure you provide
+enough information about what happened, what you expected to happen,
+and how what actually happened was different, what server you're
+running, what language your CGI program was in, and, if possible, the
+offending code. This will make finding your problem much simpler.</p>
+
+<p>Note that questions about CGI problems should <strong>never</strong>
+be posted to the Apache bug database unless you are sure you have found
+a problem in the Apache source code.</p>
+
+<!--#include virtual="footer.html" -->
+
+</body>
+</html>
+
diff --git a/docs/manual/howto/cgi.html.en b/docs/manual/howto/cgi.html.en
new file mode 100644
index 0000000000..fadbceb41c
--- /dev/null
+++ b/docs/manual/howto/cgi.html.en
@@ -0,0 +1,499 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Apache Tutorial: Dynamic Content with CGI</title>
+<link rev="made" href="mailto:rbowen@rcbowen.com">
+</head>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#000080"
+alink="#FF0000">
+<!--#include virtual="header.html" -->
+<h1 align="CENTER">Dynamic Content with CGI</h1>
+
+<a name="__index__"></a> <!-- INDEX BEGIN -->
+
+
+<ul>
+<li><a href="#dynamiccontentwithcgi">Dynamic Content with
+CGI</a></li>
+
+<li><a href="#configuringapachetopermitcgi">Configuring Apache to
+permit CGI</a>
+
+<ul>
+<li><a href="#scriptalias">ScriptAlias</a></li>
+
+<li><a href="#cgioutsideofscriptaliasdirectories">CGI outside of
+ScriptAlias directories</a>
+
+<ul>
+<li><a href="#explicitlyusingoptionstopermitcgiexecution">Explicitly using
+Options to permit CGI execution</a></li>
+
+<li><a href="#htaccessfiles">.htaccess files</a></li>
+</ul>
+</li>
+</ul>
+</li>
+
+<li><a href="#writingacgiprogram">Writing a CGI program</a>
+
+<ul>
+<li><a href="#yourfirstcgiprogram">Your first CGI program</a></li>
+</ul>
+</li>
+
+<li><a href="#butitsstillnotworking">But it's still not
+working!</a>
+
+<ul>
+<li><a href="#filepermissions">File permissions</a></li>
+
+<li><a href="#pathinformation">Path information</a></li>
+
+<li><a href="#syntaxerrors">Syntax errors</a></li>
+
+<li><a href="#errorlogs">Error logs</a></li>
+</ul>
+</li>
+
+<li><a href="#whatsgoingonbehindthescenes">What's going on behind
+the scenes?</a>
+
+<ul>
+<li><a href="#environmentvariables">Environment variables</a></li>
+
+<li><a href="#stdinandstdout">STDIN and STDOUT</a></li>
+</ul>
+</li>
+
+<li><a href="#cgimoduleslibraries">CGI modules/libraries</a></li>
+
+<li><a href="#formoreinformation">For more information</a></li>
+</ul>
+
+<!-- INDEX END -->
+<hr>
+<h2><a name="dynamiccontentwithcgi">Dynamic Content with
+CGI</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Modules</strong><br><br>
+
+<a href="../mod/mod_alias.html">mod_alias</a><br>
+<a href="../mod/mod_cgi.html">mod_cgi</a><br>
+
+</td><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="../mod/mod_mime.html#addhandler">AddHandler</a><br>
+<A HREF="../mod/core.html#options">Options</a><br>
+<a href="../mod/mod_alias.html#scriptalias">ScriptAlias</a><br>
+
+</td></tr></table>
+
+<p>The CGI (Common Gateway Interface) defines a way for a web server
+to interact with external content-generating programs, which are often
+referred to as CGI programs or CGI scripts. It is the simplest, and
+most common, way to put dynamic content on your web site. This
+document will be an introduction to setting up CGI on your Apache web
+server, and getting started writing CGI programs.</p>
+
+<hr>
+<h2><a name="configuringapachetopermitcgi">Configuring Apache to
+permit CGI</a></h2>
+
+<p>In order to get your CGI programs to work properly, you'll need to
+have Apache configured to permit CGI execution. There are several ways
+to do this.</p>
+
+<h3><a name="scriptalias">ScriptAlias</a></h3>
+
+<p>The <code>ScriptAlias</code> directive tells Apache that a
+particular directory is set aside for CGI programs. Apache will assume
+that every file in this directory is a CGI program, and will attempt to
+execute it, when that particular resource is requested by a client.</p>
+
+<p>The <code>ScriptAlias</code> directive looks like:</p>
+
+<pre>
+ ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin/
+</pre>
+
+<p>The example shown is from your default <code>httpd.conf</code>
+configuration file, if you installed Apache in the default location.
+The <code>ScriptAlias</code> directive is much like the
+<code>Alias</code> directive, which defines a URL prefix that is to
+mapped to a particular directory. <code>Alias</code> and
+<code>ScriptAlias</code> are usually used for directories that are
+outside of the <code>DocumentRoot</code> directory. The difference
+between <code>Alias</code> and <code>ScriptAlias</code> is that
+<code>ScriptAlias</code> has the added meaning that everything under
+that URL prefix will be considered a CGI program. So, the example above
+tells Apache that any request for a resource beginning with
+<code>/cgi-bin/</code> should be served from the directory
+<code>/usr/local/apache/cgi-bin/</code>, and should be treated as a CGI
+program.</p>
+
+<p>For example, if the URL
+<code>http://dev.rcbowen.com/cgi-bin/test.pl</code> is requested,
+Apache will attempt to execute the file
+<code>/usr/local/apache/cgi-bin/test.pl</code> and return the output.
+Of course, the file will have to exist, and be executable, and return
+output in a particular way, or Apache will return an error message.</p>
+
+<h3><a name="cgioutsideofscriptaliasdirectories">CGI outside of
+ScriptAlias directories</a></h3>
+
+<p>CGI programs are often restricted to <code>ScriptAlias</code>'ed
+directories for security reasons. In this way, administrators can
+tightly control who is allowed to use CGI programs. However, if the
+proper security precautions are taken, there is no reason why
+CGI programs cannot be run from arbitrary directories. For example,
+you may wish to let users have web content in their home directories
+with the <code>UserDir</code> directive. If they want to have their
+own CGI programs, but don't have access to the main
+<code>cgi-bin</code> directory, they will need to be able to run CGI
+programs elsewhere.</p>
+
+<h3><a name="explicitlyusingoptionstopermitcgiexecution">Explicitly using
+Options to permit CGI execution</a></h3>
+
+<p>You could explicitly use the <code>Options</code> directive, inside
+your main server configuration file, to specify that CGI execution was
+permitted in a particular directory:</p>
+
+<pre>
+ &lt;Directory /usr/local/apache/htdocs/somedir&gt;
+ Options +ExecCGI
+ &lt;/Directory&gt;
+</pre>
+
+<p>The above directive tells Apache to permit the execution of CGI
+files. You will also need to tell the server what files are CGI files.
+The following <code>AddHandler</code> directive tells the server
+to treat all files with the <code>cgi</code> or <code>pl</code>
+extension as CGI programs:</p>
+
+<pre>
+ AddHandler cgi-script cgi pl
+</pre>
+
+<h3><a name="htaccessfiles">.htaccess files</a></h3>
+
+<p>A <code>.htaccess</code> file is a way to set configuration
+directives on a per-directory basis. When Apache serves a resource, it
+looks in the directory from which it is serving a file for a file
+called <code>.htaccess</code>, and, if it finds it, it will apply
+directives found therein. <code>.htaccess</code> files can be permitted
+with the <code>AllowOverride</code> directive, which specifies what
+types of directives can appear in these files, or if they are not
+allowed at all. To permit the directive we will need for this purpose,
+the following configuration will be needed in your main server
+configuration:</p>
+
+<pre>
+ AllowOverride Options
+</pre>
+
+<p>In the <code>.htaccess</code> file, you'll need the following
+directive:</p>
+
+<pre>
+ Options +ExecCGI
+</pre>
+
+<p>which tells Apache that execution of CGI programs is permitted in
+this directory.</p>
+
+<hr>
+<h2><a name="writingacgiprogram">Writing a CGI program</a></h2>
+
+<p>There are two main differences between ``regular'' programming, and
+CGI programming.</p>
+
+<p>First, all output from your CGI program must be preceded by a
+MIME-type header. This is HTTP header that tells the client what sort
+of content it is receiving. Most of the time, this will look like:</p>
+
+<pre>
+ Content-type: text/html
+</pre>
+
+<p>Secondly, your output needs to be in HTML, or some other format that
+a browser will be able to display. Most of the time, this will be HTML,
+but occasionally you might write a CGI program that outputs a gif
+image, or other non-HTML content.</p>
+
+<p>Apart from those two things, writing a CGI program will look a lot
+like any other program that you might write.</p>
+
+<h3><a name="yourfirstcgiprogram">Your first CGI program</a></h3>
+
+<p>The following is an example CGI program that prints one line to your
+browser. Type in the following, save it to a file called
+<code>first.pl</code>, and put it in your <code>cgi-bin</code>
+directory.</p>
+
+<pre>
+ #!/usr/bin/perl
+ print "Content-type: text/html\r\n\r\n";
+ print "Hello, World.";
+</pre>
+
+<p>Even if you are not familiar with Perl, you should be able to see
+what is happening here. The first line tells Apache (or whatever shell
+you happen to be running under) that this program can be executed by
+feeding the file to the interpreter found at the location
+<code>/usr/bin/perl</code>. The second line prints the content-type
+declaration we talked about, followed by two carriage-return newline
+pairs. This puts a blank line after the header, to indicate the end of
+the HTTP headers, and the beginning of the body. The third line prints
+the string ``Hello, World.'' And that's the end of it.</p>
+
+<p>If you open your favorite browser and tell it to get the address</p>
+
+<pre>
+ http://www.example.com/cgi-bin/first.pl
+</pre>
+
+<p>or wherever you put your file, you will see the one line
+<code>Hello, World.</code> appear in your browser window. It's not very
+exciting, but once you get that working, you'll have a good chance of
+getting just about anything working.</p>
+
+<hr>
+<h2><a name="butitsstillnotworking">But it's still not
+working!</a></h2>
+
+<p>There are four basic things that you may see in your browser when
+you try to access your CGI program from the web:</p>
+
+<dl>
+<dt>The output of your CGI program</dt>
+<dd>Great! That means everything worked fine.<br><br></dd>
+
+<dt>The source code of your CGI program or a "POST Method Not Allowed"
+message</dt>
+<dd>That means that you have not properly configured
+Apache to process your CGI program. Reread the section on <a
+href="#configuringapachetopermitcgi">configuring Apache</a> and try to
+find what you missed.<br><br></dd>
+
+<dt>A message starting with "Forbidden"</dt> <dd>That means that there
+is a permissions problem. Check the <a href="#errorlogs">Apache
+error log</a> and the section below on <a
+href="#filepermissions">file permissions</a>.<br><br></dd>
+
+<dt>A message saying "Internal Server Error"</dt> <dd>If you check the
+<a href="#errorlogs">Apache error log</a>, you will probably find
+that it says "Premature end of script headers", possibly along with an
+error message generated by your CGI program. In this case, you will
+want to check each of the below sections to see what might be preventing
+your CGI program from emitting the proper HTTP headers.</dd>
+</dl>
+
+
+<h3><a name="filepermissions">File permissions</a></h3>
+
+<p>Remember that the server does not run as you. That is, when the
+server starts up, it is running with the permissions of an unprivileged
+user - usually ``nobody'', or ``www'' - and so it will need extra
+permissions to execute files that are owned by you. Usually, the way to
+give a file sufficient permissions to be executed by ``nobody'' is to
+give everyone execute permission on the file:</p>
+
+<pre>
+ chmod a+x first.pl
+</pre>
+
+<p>Also, if your program reads from, or writes to, any other files,
+those files will need to have the correct permissions to permit
+this.</p>
+
+<p>The exception to this is when the server is configured to use <a
+href="../suexec.html">suexec</a>. This program allows CGI programs to
+be run under different user permissions, depending on which virtual
+host or user home directory they are located in. Suexec has very
+strict permission checking, and any failure in that checking will
+result in your CGI programs failing with an "Internal Server Error".
+In this case, you will need to check the suexec log file to see what
+specific security check is failing.</p>
+
+<h3><a name="pathinformation">Path information</a></h3>
+
+<p>When you run a program from your command line, you have certain
+information that is passed to the shell without you thinking about it.
+For example, you have a path, which tells the shell where it can look
+for files that you reference.</p>
+
+<p>When a program runs through the web server as a CGI program, it does
+not have that path. Any programs that you invoke in your CGI program
+(like 'sendmail', for example) will need to be specified by a full
+path, so that the shell can find them when it attempts to execute your
+CGI program.</p>
+
+<p>A common manifestation of this is the path to the script interpreter
+(often <code>perl</code>) indicated in the first line of your CGI
+program, which will look something like:</p>
+
+<pre>
+ #!/usr/bin/perl
+</pre>
+
+<p>Make sure that this is in fact the path to the interpreter.</p>
+
+<h3><a name="syntaxerrors">Syntax errors</a></h3>
+
+<p>Most of the time when a CGI program fails, it's because of a problem
+with the program itself. This is particularly true once you get the
+hang of this CGI stuff, and no longer make the above two mistakes.
+Always attempt to run your program from the command line before you
+test if via a browser. This will eliminate most of your problems.</p>
+
+<h3><a name="errorlogs">Error logs</a></h3>
+
+<p>The error logs are your friend. Anything that goes wrong generates
+message in the error log. You should always look there first. If the
+place where you are hosting your web site does not permit you access to
+the error log, you should probably host your site somewhere else. Learn
+to read the error logs, and you'll find that almost all of your
+problems are quickly identified, and quickly solved.</p>
+
+<hr>
+<h2><a name="whatsgoingonbehindthescenes">What's going on behind
+the scenes?</a></h2>
+
+<p>As you become more advanced in CGI programming, it will become
+useful to understand more about what's happening behind the scenes.
+Specifically, how the browser and server communicate with one another.
+Because although it's all very well to write a program that prints
+``Hello, World.'', it's not particularly useful.</p>
+
+<h3><a name="environmentvariables">Environment variables</a></h3>
+
+<p>Environment variables are values that float around you as you use
+your computer. They are useful things like your path (where the
+computer searches for a the actual file implementing a command when you
+type it), your username, your terminal type, and so on. For a full list
+of your normal, every day environment variables, type <code>env</code>
+at a command prompt.</p>
+
+<p>During the CGI transaction, the server and the browser also set
+environment variables, so that they can communicate with one another.
+These are things like the browser type (Netscape, IE, Lynx), the server
+type (Apache, IIS, WebSite), the name of the CGI program that is being
+run, and so on.</p>
+
+<p>These variables are available to the CGI programmer, and are half of
+the story of the client-server communication. The complete list of
+required variables is at <a href=
+"http://hoohoo.ncsa.uiuc.edu/cgi/env.html">http://hoohoo.ncsa.uiuc.edu/cgi/env.html</a></p>
+
+<p>This simple Perl CGI program will display all of the environment
+variables that are being passed around. Two similar programs are
+included in the <code>cgi-bin</code> directory of the Apache
+distribution. Note that some variables are required, while others are
+optional, so you may see some variables listed that were not in the
+official list. In addition, Apache provides many different ways for
+you to <a href="../env.html">add your own environment variables</a> to
+the basic ones provided by default.</p>
+
+<pre>
+ #!/usr/bin/perl
+ print "Content-type: text/html\n\n";
+ foreach $key (keys %ENV) {
+ print "$key --&gt; $ENV{$key}&lt;br&gt;";
+ }
+</pre>
+
+<h3><a name="stdinandstdout">STDIN and STDOUT</a></h3>
+
+<p>Other communication between the server and the client happens over
+standard input (<code>STDIN</code>) and standard output
+(<code>STDOUT</code>). In normal everyday context, <code>STDIN</code>
+means the keyboard, or a file that a program is given to act on, and
+<code>STDOUT</code> usually means the console or screen.</p>
+
+<p>When you <code>POST</code> a web form to a CGI program, the data in
+that form is bundled up into a special format and gets delivered to
+your CGI program over <code>STDIN</code>. The program then can process
+that data as though it was coming in from the keyboard, or from a
+file</p>
+
+<p>The ``special format'' is very simple. A field name and its value
+are joined together with an equals (=) sign, and pairs of values are
+joined together with an ampersand (&amp;). Inconvenient characters like
+spaces, ampersands, and equals signs, are converted into their hex
+equivalent so that they don't gum up the works. The whole data string
+might look something like:</p>
+
+<pre>
+ name=Rich%20Bowen&amp;city=Lexington&amp;state=KY&amp;sidekick=Squirrel%20Monkey
+</pre>
+
+<p>You'll sometimes also see this type of string appended to the a URL.
+When that is done, the server puts that string into the environment
+variable called <code>QUERY_STRING</code>. That's called a
+<code>GET</code> request. Your HTML form specifies whether a
+<code>GET</code> or a <code>POST</code> is used to deliver the data, by
+setting the <code>METHOD</code> attribute in the <code>FORM</code>
+tag.</p>
+
+<p>Your program is then responsible for splitting that string up into
+useful information. Fortunately, there are libraries and modules
+available to help you process this data, as well as handle other of the
+aspects of your CGI program.</p>
+
+<hr>
+<h2><a name="cgimoduleslibraries">CGI modules/libraries</a></h2>
+
+<p>When you write CGI programs, you should consider using a code
+library, or module, to do most of the grunt work for you. This leads to
+fewer errors, and faster development.</p>
+
+<p>If you're writing CGI programs in Perl, modules are available on <a
+href="http://www.cpan.org/">CPAN</a>. The most popular module for this
+purpose is CGI.pm. You might also consider CGI::Lite, which implements
+a minimal set of functionality, which is all you need in most
+programs.</p>
+
+<p>If you're writing CGI programs in C, there are a variety of options.
+One of these is the CGIC library, from <a href=
+"http://www.boutell.com/cgic/">http://www.boutell.com/cgic/</a></p>
+
+<hr>
+<h2><a name="formoreinformation">For more information</a></h2>
+
+<p>There are a large number of CGI resources on the web. You can
+discuss CGI problems with other users on the Usenet group
+comp.infosystems.www.authoring.cgi. And the -servers mailing list from
+the HTML Writers Guild is a great source of answers to your questions.
+You can find out more at <a href=
+"http://www.hwg.org/lists/hwg-servers/">http://www.hwg.org/lists/hwg-servers/</a></p>
+
+<p>And, of course, you should probably read the CGI specification,
+which has all the details on the operation of CGI programs. You can
+find the original version at the <a href=
+"http://hoohoo.ncsa.uiuc.edu/cgi/interface.html">NCSA</a> and there is
+an updated draft at the <a
+href="http://web.golux.com/coar/cgi/">Common Gateway Interface RFC
+project</a>.</p>
+
+<p>When you post a question about a CGI problem that you're having,
+whether to a mailing list, or to a newsgroup, make sure you provide
+enough information about what happened, what you expected to happen,
+and how what actually happened was different, what server you're
+running, what language your CGI program was in, and, if possible, the
+offending code. This will make finding your problem much simpler.</p>
+
+<p>Note that questions about CGI problems should <strong>never</strong>
+be posted to the Apache bug database unless you are sure you have found
+a problem in the Apache source code.</p>
+
+<!--#include virtual="footer.html" -->
+
+</body>
+</html>
+
diff --git a/docs/manual/howto/cgi.html.ja.jis b/docs/manual/howto/cgi.html.ja.jis
new file mode 100644
index 0000000000..b6bf58c219
--- /dev/null
+++ b/docs/manual/howto/cgi.html.ja.jis
@@ -0,0 +1,495 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Apache Tutorial: CGI $B$K$h$kF0E*%3%s%F%s%D(B</title>
+</head>
+<!-- English revision: 1.6 -->
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#000080"
+alink="#FF0000">
+<!--#include virtual="header.html" -->
+<h1 align="CENTER">CGI $B$K$h$kF0E*%3%s%F%s%D(B</h1>
+
+<a name="__index__"></a> <!-- INDEX BEGIN -->
+
+
+<ul>
+<li><a href="#dynamiccontentwithcgi">CGI $B$K$h$kF0E*%3%s%F%s%D(B</a></li>
+
+<li><a href="#configuringapachetopermitcgi">CGI $B$r5v2D$9$k$h$&$K(B Apache $B$r(B
+$B@_Dj$9$k(B</a>
+
+<ul>
+<li><a href="#scriptalias">ScriptAlias</a></li>
+
+<li><a href="#cgioutsideofscriptaliasdirectories">ScriptAlias $B%G%#%l%/%H%j30$N(B
+CGI</a>
+
+<ul>
+<li><a href="#explicitlyusingoptionstopermitcgiexecution">CGI $B$N<B9T$r2DG=$K(B
+$B$9$k$?$a$K(B Options $B$rL@<(E*$K;HMQ$9$k(B</a></li>
+
+
+<li><a href="#htaccessfiles">.htaccess $B%U%!%$%k(B</a></li>
+</ul>
+</li>
+</ul>
+</li>
+
+<li><a href="#writingacgiprogram">CGI $B%W%m%0%i%`$r=q$/(B</a>
+
+<ul>
+<li><a href="#yourfirstcgiprogram">$B$"$J$?$N:G=i$N(B CGI $B%W%m%0%i%`(B</a></li>
+</ul>
+</li>
+
+<li><a href="#butitsstillnotworking">$B$7$+$7!"$^$@F0$+$J$$(B !</a>
+
+<ul>
+<li><a href="#filepermissions">$B%U%!%$%k$N%Q!<%_%C%7%g%s(B</a></li>
+
+<li><a href="#pathinformation">$B%Q%9>pJs(B</a></li>
+
+<li><a href="#syntaxerrors">$B9=J8%(%i!<(B</a></li>
+
+<li><a href="#errorlogs">$B%(%i!<%m%0(B</a></li>
+</ul>
+</li>
+
+<li><a href="#whatsgoingonbehindthescenes">$BN"$G2?$,5/$3$C$F$$$k$N$+(B?</a>
+
+<ul>
+<li><a href="#environmentvariables">$B4D6-JQ?t(B</a></li>
+
+<li><a href="#stdinandstdout">$BI8=`F~=PNO(B</a></li>
+</ul>
+</li>
+
+<li><a href="#cgimoduleslibraries">CGI $B%b%8%e!<%k(B/$B%i%$%V%i%j(B</a></li>
+
+<li><a href="#formoreinformation">$B99$J$k>pJs(B</a></li>
+</ul>
+
+<!-- INDEX END -->
+<hr>
+<h2><a name="dynamiccontentwithcgi">CGI $B$K$h$kF0E*%3%s%F%s%D(B</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>$B4XO"%b%8%e!<%k(B</strong><br><br>
+
+<a href="../mod/mod_alias.html">mod_alias</a><br>
+<a href="../mod/mod_cgi.html">mod_cgi</a><br>
+
+</td><td valign="top">
+<strong>$B4XO"%G%#%l%/%F%#%V(B</strong><br><br>
+
+<a href="../mod/mod_mime.html#addhandler">AddHandler</a><br>
+<A HREF="../mod/core.html#options">Options</a><br>
+<a href="../mod/mod_alias.html#scriptalias">ScriptAlias</a><br>
+
+</td></tr></table>
+
+<p>CGI (Common Gateway Interface) $B$O!"%&%'%V%5!<%P$,%3%s%F%s%D@8@.$r$9$k(B
+$B30It%W%m%0%i%`$H6(D4$7$FF0:n$9$k$?$a$NJ}K!$rDj5A$7$F$$$^$9!#$=$N%W%m%0%i%`$O$7$P$7$P(B
+CGI $B%W%m%0%i%`$d(B CGI $B%9%/%j%W%H$H8F$P$l$^$9!#(BCGI $B$O!"%&%'%V%5%$%H$K(B
+$BF0E*$J%3%s%F%s%D$rCV$/$?$a$N:G$b4JC1$G0lHLE*$JJ}K!$G$9!#$3$N%I%-%e%a%s%H$O!"(B
+Apache $B%&%'%V%5!<%P$G(B CGI $B$r@_Dj$7!"(BCGI $B%W%m%0%i%`$r=q$-;O$a$k$?$a$N(B
+$BF~Lg=q$H$J$k$G$7$g$&!#(B</p>
+
+<hr>
+<h2><a name="configuringapachetopermitcgi">CGI $B$r5v2D$9$k$h$&$K(B Apache $B$r(B
+$B@_Dj$9$k(B</a></h2>
+
+<p>CGI $B%W%m%0%i%`$r@5$7$/F0:n$5$;$k$K$O!"(BCGI $B$r5v2D$9$k$h$&$K(B
+Apache $B$N@_Dj$r9T$&I,MW$,$"$j$^$9!#$3$l$r9T$J$&$?$a$NJ}K!$,$$$/$D$+(B
+$B$"$j$^$9!#(B</p>
+
+<h3><a name="scriptalias">ScriptAlias</a></h3>
+
+<p><code>ScriptAlias</code> $B%G%#%l%/%F%#%V$r;HMQ$7$F!"(BCGI $B%W%m%0%i%`MQ$N(B
+$BFCJL$JJL%G%#%l%/%H%j$r(B Apache $B$K@_Dj$7$^$9!#(B
+Apache $B$O!"$3$N%G%#%l%/%H%jCf$NA4$F$N%U%!%$%k$r(B CGI $B%W%m%0%i%`$G$"$k$H(B
+$B2>Dj$7$^$9!#$=$7$F!"$3$NFCJL$J%j%=!<%9$,%/%i%$%"%s%H$+$iMW5a$5$l$k$H!"(B
+$B$=$N%W%m%0%i%`$N<B9T$r;n$_$^$9!#(B</p>
+
+<p><code>ScriptAlias</code> $B%G%#%l%/%F%#%V$O0J2<$N$h$&$K;HMQ$7$^$9(B:</p>
+
+<pre>
+ ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin/
+</pre>
+
+<p>$B%G%U%)%k%H0LCV$K(B Apache $B$r%$%s%9%H!<%k$7$?$J$i$P!"(B
+$B$3$NNc$O%G%U%)%k%H>uBV$N(B <code>httpd.conf</code> $B@_Dj%U%!%$%k(B
+$B$K4^$^$l$F$$$^$9!#(B
+<code>ScriptAlias</code> $B%G%#%l%/%F%#%V$O!"(BURL $B$NA0$KIU2C$9$k%G%#%l%/%H%j$rDj5A$9$k(B <code>Alias</code> $B%G%#%l%/%F%#%V$H$+$J$j;w$F$$$^$9!#(B
+<code>Alias</code> $B$H(B <code>ScriptAlias</code> $B$ODL>o!"(B
+<code>DocumentRoot</code> $B%G%#%l%/%H%j30$N%G%#%l%/%H%j$N$?$a$K;HMQ$5$l$^$9!#(B
+<code>Alias</code> $B$H(B <code>ScriptAlias</code> $B$H$N:9$O!"(B
+<code>ScriptAlias</code> $B$,@\F,<-$G;O$^$k$9$Y$F$N(B URL $B$O(B CGI $B%W%m%0%i%`$H(B
+$B$_$J$5$l$k$H$$$&DI2C$N0UL#$r4^$s$G$$$k$3$H$G$9!#=>$C$F!"(B
+$B>e5-$NNc$G$O!"(B<code>/cgi-bin/</code>
+$B$G;O$^$k%j%=!<%9$X$N$"$i$f$k%j%/%(%9%H$KBP$7$F!"%G%#%l%/%H%j(B
+<code>/usr/local/apache/cgi-bin/</code>
+$B$+$iDs6!$7!"$=$l$i$r(B CGI $B%W%m%0%i%`$H$7$F07$&$h$&(B Apache $B$K<($7$^$9!#(B</p>
+
+<p>$BNc$($P!"(BURL <code>http://dev.rcbowen.com/cgi-bin/test.pl</code>
+$B$,MW5a$5$l$?>l9g!"(BApache $B$O(B $B%U%!%$%k(B
+<code>/usr/local/apache/cgi-bin/test.pl</code> $B$r<B9T$7!"$=$N=PNO$rJV$9$3$H$r(B
+$B;n$_$^$9!#(B
+$B$b$A$m$s!"%U%!%$%k$,B8:_$7!"<B9T2DG=$G$"$j!"7h$a$i$l$?J}K!$G=PNO$rJV$7$^$9!#(B
+$B$=$&$G$J$1$l$P!"(BApache $B$O%(%i!<%a%C%;!<%8$rJV$7$^$9!#(B
+
+<h3><a name="cgioutsideofscriptaliasdirectories">
+ScriptAlias $B%G%#%l%/%H%j30$N(B CGI</a></h3>
+
+<p>CGI $B%W%m%0%i%`$O!"%;%-%e%j%F%#>e$NM}M3$+$i(B
+<code>ScriptAlias</code> $B$5$l$?%G%#%l%/%H%j$K@)8B$5$l$k$3$H$,$7$P$7$P$"$j$^$9!#(B
+$B$3$NJ}K!$K$h$j!"(BCGI $B%W%m%0%i%`$r;HMQ$G$-$k%f!<%6$r4IM}<T$,87$7$/@)8f$9$k(B
+$B$3$H$,$G$-$^$9!#(B
+$B$7$+$7$J$,$i!"E,@Z$J%;%-%e%j%F%#;vA0BP:v$,$H$i$l$k$J$i$P!"(BCGI $B%W%m%0%i%`(B
+$B$rG$0U$N%G%#%l%/%H%j$G<B9T$G$-$J$$$h$&$K$9$kM}M3$O$"$j$^$;$s!#(B
+$BNc$($P!"%f!<%6$K(B <code>UserDir</code> $B%G%#%l%/%F%#%V$G(B
+$BH`$i$N%[!<%`%G%#%l%/%H%jG[2<$K%&%'%V%3%s%F%s%D$r;}$?$;$?$$$H$7$^$9!#(B
+$B$b$7!"H`$i$,(B CGI $B%W%m%0%i%`$r;}$D$3$H$rK>$s$G$$$F$b!"%a%$%s$N(B
+<code>cgi-bin</code> $B%G%#%l%/%H%j$X$N%"%/%;%9$,$G$-$J$$>l9g!"(BCGI $B%W%m%0%i%`$r(B
+$B<B9T$9$k$3$H$,$G$-$kB>$N>l=j$,I,MW$K$J$j$^$9!#(B</p>
+
+<h3><a name="explicitlyusingoptionstopermitcgiexecution">
+CGI $B$N<B9T$r2DG=$K$9$k$?$a$K(B Options $B$rL@<(E*$K;HMQ$9$k(B</a></h3>
+
+<p>$B%5!<%P$N%a%$%s$N@_Dj%U%!%$%kCf$G(B <code>Options</code> $B%G%#%l%/%F%#%V$r(B
+$BL@<(E*$K;HMQ$9$k$3$H$G!"FCDj$N%G%#%l%/%H%jG[2<$G(B CGI $B$N<B9T$r5v2D$9$k$h$&$K(B
+$B;XDj$9$k$3$H$,$G$-$^$9(B:<p>
+
+<pre>
+ &lt;Directory /usr/local/apache/htdocs/somedir&gt;
+ Options +ExecCGI
+ &lt;/Directory&gt;
+</pre>
+
+<p>$B>e5-%G%#%l%/%F%#%V$O!"(BCGI $B%U%!%$%k$N<B9T$r2DG=$K$9$k$h$&(B Apache
+$B$KEA$($^$9!#$^$?!"$I$N%U%!%$%k$,(B CGI $B%U%!%$%k$+$r(B
+$B%5!<%P$KEA$($kI,MW$,$"$j$^$9!#<!$N(B <code>AddHandler</code>
+$B%G%#%l%/%F%#%V$NNc$G$O!"(B<code>cgi</code> $B$^$?$O(B <code>pl</code> $B$r3HD%;R$K(B
+$B;}$D$9$Y$F$N%U%!%$%k$r(B CGI $B%W%m%0%i%`$H$7$F$_$J$9$3$H$r%5!<%P$KEA$($^$9(B:<p>
+
+<pre>
+ AddHandler cgi-script cgi pl
+</pre>
+
+<h3><a name="htaccessfiles">.htaccess $B%U%!%$%k(B</a></h3>
+
+<p><code>.htaccess</code> $B%U%!%$%k$O!"%G%#%l%/%H%jKh$K(B
+$B%G%#%l%/%F%#%V$r;XDj$9$kJ}K!$G$9!#(B
+Apache $B$O!"%j%=!<%9$rDs6!$9$k$H$-$K!"Ds6!$9$k%U%!%$%k$,CV$+$l$F$$$k(B
+$B%G%#%l%/%H%jCf$N(B <code>.htaccess</code> $B$H$$$&%U%!%$%k$r;2>H$7$^$9!#(B
+$B$=$N%U%!%$%k$rH/8+$7$?$i!"$=$NCf$GH/8+$5$l$?%G%#%l%/%F%#%V$,E,MQ$5$l$^$9!#(B
+<code>.htaccess</code> $B%U%!%$%k$O!"(B<code>AllowOverride</code>
+$B%G%#%l%/%F%#%V$N;XDj$K$h$j;H$($k$h$&$K$J$j$^$9!#(B
+<code>AllowOverride</code> $B%G%#%l%/%F%#%V$O!"(B<code>.htaccess</code> $B%U%!%$%k$G(B
+$B@_Dj$G$-$k%G%#%l%/%F%#%V$N%?%$%W$r;XDj$7$^$9!#(B
+<code>AllowOverride</code> $B%G%#%l%/%F%#%V$N;XDj$,$J$$>l9g!"$^$C$?$/;H$($^$;$s!#(B
+CGI $B$N<B9T$r5v2D$9$k$?$a$KI,MW$H$J$k%G%#%l%/%F%#%V$r;XDj2DG=$K$9$k$K$O!"(B
+$B0J2<$N@_Dj$,%5!<%P$N%a%$%s$N@_Dj$GI,MW$K$J$j$^$9(B:</p>
+
+<pre>
+ AllowOverride Options
+</pre>
+
+<p><code>.htaccess</code> $B%U%!%$%k$G$O!"<!$N%G%#%l%/%F%#%V$,I,MW$H(B
+$B$J$j$^$9(B:</p>
+
+<pre>
+ Options +ExecCGI
+</pre>
+
+<p>$B$3$N@_Dj$G$O!"$3$N%G%#%l%/%H%j$K$*$1$k(B CGI $B%W%m%0%i%`$N<B9T$r5v2D$9$k$h$&(B
+Apache $B$KEA$($^$9!#(B</p>
+
+<hr>
+<h2><a name="writingacgiprogram">CGI $B%W%m%0%i%`$r=q$/(B</a></h2>
+
+<p>$BDL>o$N%W%m%0%i%_%s%0$H(B CGI $B%W%m%0%i%_%s%0$N4V$K$O<g$KFs$D$N0c$$$,(B
+$B$"$j$^$9!#(B</p>
+
+<p>$B0l$D$O!"(BCGI $B%W%m%0%i%`$N$9$Y$F$N=PNO$K$O(B MIME-type
+$B%X%C%@$rIU$1$J$1$l$P$J$j$^$;$s!#$3$l$O$I$N$h$&$J<oN`$N%3%s%F%s%D$r<u$1<h$C$F(B
+$B$$$k$+$r%/%i%$%"%s%H$K<($9(B HTTP $B%X%C%@$G$9!#$[$H$s$I$N>l9g$G$O!"(B
+$B<!$N$h$&$K=PNO$7$^$9(B:</p>
+
+<pre>
+ Content-type: text/html
+</pre>
+
+<p>$B$b$&0l$D$O!"=PNO$r(B HTML $B$+!"%V%i%&%6$,I=<($9$k$3$H$,(B
+$B$G$-$k2?$+B>$N7A<0$K$9$kI,MW$,$"$j$^$9!#(B
+$BBgDq$N>l9g$O(B HTML $B$G$7$g$&$,!"(BGIF $B%$%a!<%8$dB>$NHs(B HTML
+$B%3%s%F%s%D$r=PNO$9$k(B CGI $B%W%m%0%i%`$r=q$/$3$H$b$"$k$G$7$g$&!#(B</p>
+
+<p>$B$3$l$iFsE@0J30$G$O!"(BCGI $B%W%m%0%i%`$r=q$/$3$H$O!"$"$J$?$,=q$$$F$$$k(B
+$BB>$N%W%m%0%i%`$HBg$$$K;w$F$$$k$G$7$g$&!#(B</p>
+
+<h3><a name="yourfirstcgiprogram">$B$"$J$?$N:G=i$N(B CGI $B%W%m%0%i%`(B</a></h3>
+
+<p>$B<!$K<($9$N$O!"%V%i%&%6$K(B 1 $B9T0u;z$9$k(B CGI $B%W%m%0%i%`$NNc$G$9!#(B
+$B0J2<$rF~NO$7!"(B<code>first.pl</code> $B$H$$$&%U%!%$%k$KJ]B8$7!"$=$l$r(B
+<code>cgi-bin</code> $B%G%#%l%/%H%j$KCV$$$F$/$@$5$$!#(B</p>
+
+<pre>
+ #!/usr/bin/perl
+ print "Content-type: text/html\r\n\r\n";
+ print "Hello, World.";
+</pre>
+
+<p>Perl $B$K@:DL$7$F$$$J$/$F$b!"2?$,5/$3$k$+$r(B
+$BM}2r$9$k$3$H$O$G$-$k$O$:$G$9!#(B
+1 $B9TL\$O!"(B<code>/usr/bin/perl</code> $B$G8+$D$1(B
+$B$i$l$k%$%s%?%W%j%?$K$3$N%U%!%$%k$r6!5k$9$k$3$H$G$3$N%W%m%0%i%`$,<B9T$5$l$k$3$H$r(B
+Apache $B$K(B ($B%7%'%k>e$G<B9T$7$h$&$H$7$F$$$k$J$i$P!"$=$N%7%'%k$K(B ) $B<($7$^$9!#(B
+2 $B9TL\$O!"A0=R$7$?$H$*$j(B content-type $B$NDj5A$r0u;z$7$^$9!#(B
+$B$3$l$K$OI|5"2~9T$NFs$D$NAH$r8e$KIU2C$7$^$9!#$3$l$K$h$j!"(B
+$B%X%C%@$N=*$j$K6u9T$,CV$+$l!"(BHTTP $B%X%C%@$N=*$j$H%\%G%#$N;O$^$j$r<($7$^$9!#(B
+3 $B9TL\$O!"(B``Hello, World.'' $B$H$$$&J8;zNs$r0u;z$7!"$3$l$G=*$j$H$J$j$^$9!#(B</p>
+
+<p>$B9%$_$N%V%i%&%6$r3+$-!"%"%I%l%9(B</p>
+
+<pre>
+ http://www.example.com/cgi-bin/first.pl
+</pre>
+
+<p>$B$"$k$$$O%U%!%$%k$rCV$$$?%m%1!<%7%g%s$r;XDj$9$k$H!"(B
+<code>Hello, World.</code> $B$H$$$&(B 1 $B9T$,%V%i%&%6%&%#%s%I$K8=$l$k$G$7$g$&!#(B
+$B$=$l$O$"$^$j%(%-%5%$%F%#%s%0$J$3$H$G$O$"$j$^$;$s!#(B
+$B$7$+$7!"$3$l$,$&$^$/F0$1$P!"(B
+$BB>$N$I$N$h$&$J$b$N$G$bF0$+$9$3$H$,$G$-$k$h$&$K$J$j$^$9!#(B</p>
+
+<hr>
+<h2><a name="butitsstillnotworking">$B$7$+$7!"$^$@F0$+$J$$(B !</a></h2>
+
+<p>$B%&%'%V$+$i(B CGI $B%W%m%0%i%`$X$N%"%/%;%9$r9T$J$C$?$H$-!"(B
+$B%V%i%&%6$G8+$k2DG=@-$,$"$k;M$D$N4pK\E*$J$3$H$,$"$j$^$9(B:</p>
+
+<dl>
+<dt>CGI $B%W%m%0%i%`$N=PNO(B</dt>
+<dd>$BAG@2$i$7$$(B ! $B$=$l$O$9$Y$F$,$&$^$/F0$$$?$3$H$r0UL#$7$^$9!#(B<br><br></dd>
+
+<dt>CGI $B%W%m%0%i%`$N%=!<%9%3!<%I!"$^$?$O(B "POST Method Not Allowed"
+$B$H$$$&%a%C%;!<%8(B</dt>
+<dd>$B$3$l$O!"(BCGI $B%W%m%0%i%`$r=hM}$G$-$k$h$&(B Apache $B$rE,@Z$K@_Dj$7$F(B
+$B$$$J$+$C$?$3$H$r0UL#$7$^$9!#(B
+<a href="#configuringapachetopermitcgi">$B!V(BCGI $B$r5v2D$9$k$h$&$K(B Apache $B$r@_Dj$9$k!W(B</a>$B$N>O$rFI$_D>$7!"$"$J$?$,2?$r4V0c$($?$+$r(B
+$BC5$7$F$_$F$/$@$5$$!#(B<br><br></dd>
+
+<dt>$B%a%C%;!<%8$,(B "Forbidden" $B$G;O$^$C$F$$$k(B</dt>
+<dd>$B$3$l$O%Q!<%_%C%7%g%s$NLdBj$H$$$&$3$H$r0UL#$7$^$9!#(B
+<a href="#errorlogs">Apache $B$N%(%i!<%m%0(B</a>$B$H!"8e=R$N(B
+<a href="#filepermissions">$B!V%U%!%$%k$N%Q!<%_%C%7%g%s!W(B</a>$B$N>O$r(B
+$B%A%'%C%/$7$F$/$@$5$$!#(B
+<br><br></dd>
+
+<dt>"Internal Server Error" $B$H$$$&%a%C%;!<%8(B</dt>
+<dd><a href="#errorlogs">Apache $B$N%(%i!<%m%0(B</a>$B$r%A%'%C%/$9$k$H!"(B
+"Premature end of script headers" $B$H$$$&%m%0$,5-O?$5$l$F$$$k$H;W$$$^$9!#(B
+$B$=$7$F!"$*$=$i$/(B CGI $B%W%m%0%i%`$K$h$C$F@8@.$5$l$?%(%i!<%a%C%;!<%8$b5-O?$5$l$F$$$k$G$7$g$&!#(B
+$B$3$N>l9g!"(BCGI $B%W%m%0%i%`$,E,@Z$J(B HTTP $B%X%C%@$r=PNO$G$-$J$$860x$r(B
+$BCN$k$?$a$K!"0J2<$N3F>O$G%A%'%C%/$7$F$_$F$/$@$5$$!#(B</dd>
+</dl>
+
+<h3><a name="filepermissions">$B%U%!%$%k$N%Q!<%_%C%7%g%s(B</a></h3>
+
+<p>$B%5!<%P$O$"$J$?$N8"8B$G<B9T$5$l$F$$$J$$$N$rK:$l$J$$$h$&$K!#(B
+$B$D$^$j!"5/F0$9$k$H$-!"%5!<%P$OFC8"$r$b$?$J$$%f!<%6(B - $BDL>o(B ``nobody'' $B$d(B ``www''
+$B$N8"8B$G<B9T$5$l$^$9!#$7$?$,$C$F!"$"$J$?$,=jM-$9$k%U%!%$%k$r(B
+$B<B9T$9$k$K$OJL$N%Q!<%_%C%7%g%s$,I,MW$H$J$j$^$9!#(B
+$BDL>o!"(B``nobody'' $B$,<B9T$9$k$N$K==J,$J%Q!<%_%C%7%g%s$rM?$($kJ}K!$O!"%U%!%$%k$K(B
+$BC/$G$b<B9T2DG=$H$9$k%Q!<%_%C%7%g%s$rM?$($k$3$H$G$9(B:</p>
+
+<pre>
+ chmod a+x first.pl
+</pre>
+
+<p>$B$^$?!"$b$7$"$J$?$N%W%m%0%i%`$,B>$N%U%!%$%k$rFI$_=q$-$9$k$J$i$P!"(B
+$B$=$l$i$N%U%!%$%k$O!"$3$l$,2DG=$H$J$k@5$7$$%Q!<%_%C%7%g%s$r;}$C$F$$$kI,MW$,(B
+$B$"$j$^$9!#(B</p>
+
+<p>$B$3$l$KBP$9$kNc30$O!"%5!<%P$,(B <a href="../suexec.html">suexec</a> $B$r(B
+$B;HMQ$9$k$h$&@_Dj$5$l$F$$$k>l9g$G$9!#(B
+suexec $B$O!"(BCGI $B%W%m%0%i%`$,CV$+$l$F$$$k%P!<%A%c%k%[%9%H$^$?$O%f!<%6$N%[!<%`(B
+$B%G%#%l%/%H%j$K$h$C$F!"0[$J$k%f!<%68"8B$G(B
+$B<B9T$5$l$k$h$&$K$7$^$9!#(B
+suexec $B$O$H$F$b87$7$$%Q!<%_%C%7%g%s$N%A%'%C%/$,$"$j!"(B
+$B$=$N%A%'%C%/$rDL2a$G$-$J$$$H(B "Internal Server Error" $B$H$J$j!"(B
+$B$=$N(B CGI $B%W%m%0%i%`$N<B9T$O<:GT$7$^$9!#(B
+$B$3$N>l9g!"$I$N%;%-%e%j%F%#%A%'%C%/$,<:GT$7$F$$$k$N$+$rCN$k(B
+$B$?$a$K(B suexec $B%m%0%U%!%$%k$r%A%'%C%/$9$kI,MW$,$"$j$^$9!#(B</p>
+
+<h3><a name="pathinformation">$B%Q%9>pJs(B</a></h3>
+
+<p>$B%3%^%s%I%i%$%s$+$i%W%m%0%i%`$r<B9T$9$k$H$-!"0U<1$7$J$/$F$b(B
+$B%7%'%k$KEO$5$l$k>pJs$,$"$j$^$9!#(B
+$BNc$($P!";2>H$9$k%U%!%$%k$N$?$a$K$I$3$r8!:w$7$?$i$h$$$+$r%7%'%k$KEA$($k%Q%9$,(B
+$B$"$j$^$9!#(B</p>
+
+<p>$B%W%m%0%i%`$,(B CGI $B%W%m%0%i%`$H$7$F%&%'%V%5!<%P$K$h$C$F<B9T$5$l$k$H$-!"(B
+$B$=$l$O%Q%9$r;}$A$^$;$s!#(B
+CGI $B%W%m%0%i%`Fb$G8F$S=P$9$"$i$f$k%W%m%0%i%`(B ($BNc$($P!"(B'sendmail' $B$N(B
+$B$h$&$J$b$N(B) $B$O!"%U%k%Q%9$G;XDj$9$kI,MW$,$"$k$G$7$g$&!#(B
+$B$=$l$K$h$j!"(BCGI $B%W%m%0%i%`$r<B9T$7$h$&$H$7$?$H$-!"%7%'%k$O$=$N$h$&$J%W%m%0%i%`$r(B
+$B8+$D$1$k$3$H$,$G$-$^$9!#(B</p>
+
+<p>$BF1MM$J$3$H$O!"%9%/%j%W%H$N%$%s%?%W%j%?(B ($B$7$P$7$P(B <code> perl </code>)
+$B$X$N%Q%9$G!"(BCGI $B%W%m%0%i%`$N(B 1 $B9TL\$K<!$N$h$&$K<($5$l$^$9(B:</p>
+
+<pre>
+ #!/usr/bin/perl
+</pre>
+
+<p>$B$3$l$,%$%s%?!<%W%j%?$X$N<B:]$N%Q%9$G$"$k$3$H$r3N<B$K$7$F$*$-$^$9!#(B</p>
+
+<h3><a name="syntaxerrors">$B9=J8%(%i!<(B</a></h3>
+
+<p>CGI $B%W%m%0%i%`$,<:GT$9$k$N$OBgDq!"%W%m%0%i%`<+?H$KLdBj$,$"$k>l9g$G$9!#(B
+$B0lEY(B CGI $B$N;H$$J}$rM}2r$7!"A0=R$NFs$D$N8m$j$rHH$7$F$$$J$$$J$i$P!"(B
+$B$^$:4V0c$$$J$/$=$&$G$7$g$&!#%V%i%&%6$rDL$7$F%F%9%H$r9T$&A0$KI,$:!"%3%^%s%I%i%$%s(B
+$B$+$i%W%m%0%i%`$N<B9T$r;n$7$J$5$$!#$3$l$K$h$j!"BgDq$NLdBj$,5/$3$i$J$/$J$j$^$9!#(B</p>
+
+<h3><a name="errorlogs">$B%(%i!<%m%0(B</a></h3>
+
+<p>$B%(%i!<%m%0$OM'C#$G$9!#A4$F$N$&$^$/$$$+$J$$$3$H$O!"%(%i!<%m%0$K(B
+$B%a%C%;!<%8$r@8@.$7$^$9!#I,$:$=$l$r:G=i$K8+$k$Y$-$G$9!#(B
+$B$b$7!"$"$J$?$,%&%'%V%5%$%H$r<g:E$7$F$$$k>l=j$,%(%i!<%m%0$N;2>H$r(B
+$B5v$7$F$$$J$$$J$i$P!"$-$C$HB>$N%5%$%H$G<g:E$9$k$Y$-$G$9!#(B
+$B%(%i!<%m%0$NFI$_J}$r3X$V$3$H$G!"$[$H$s$IA4$F$NLdBj$,?WB.$K(B
+$B3NG'$5$l!"?WB.$K2r7h$5$l$k$H$$$&$3$H$,J,$+$k$G$7$g$&!#(B</p>
+
+<hr>
+<h2><a name="whatsgoingonbehindthescenes">$BN"$G2?$,5/$3$C$F$$$k$N$+(B?</a></h2>
+
+<p>CGI $B%W%m%0%i%_%s%0$K=OC#$9$k$H!"N"$G5/$3$C$F$$$k(B
+$B$3$H$K$D$$$F99$KM}2r$9$k$3$H$OM-1W$K$J$k$G$7$g$&!#%V%i%&%6$H(B
+$B%5!<%P$,$I$N$h$&$KAj8_DL?.$9$k$+$K$D$$$F$OFC$K$=$&$G$9!#$J$<$J$i!"(B
+``Hello, World.'' $B$r0u;z$9$k%W%m%0%i%`$r=q$/$3$H$O$^$3$H$K7k9=$G$9$,!"(B
+$B$=$l$OFC$KM-1W$G$O$"$j$^$;$s!#(B</p>
+
+<h3><a name="environmentvariables">$B4D6-JQ?t(B</a></h3>
+
+<p>$B4D6-JQ?t$O!"$"$J$?$,%3%s%T%e!<%?$r;H$&$H$-$KJU$j$KB8:_$7$F$$$kCM$G$9!#(B
+$B$=$l$i$O!"%Q%9(B ($B%3%^%s%I$r%?%$%W$7$?$H$-$K<B9T$9$k<B:]$N%U%!%$%k$r(B
+$BC5$7=P$9$H$3$m(B)$B!"%f!<%6L>!"C<Kv7?$J$I$N$h$&$JJXMx$J$b$N$G$9!#(B
+$BDL>o$N!"KhF|$N4D6-JQ?t$N40A4$J%j%9%H$rD4$Y$k$K$O!"%3%^%s%I%W%m%s%W%H$G(B
+<code>env</code> $B$rF~NO$7$^$9!#(B</p>
+
+<p>CGI $B$N=hM}Cf!"%5!<%P$H%V%i%&%6$b4D6-JQ?t$r@_Dj$7!"$=$l$K$h$j(B
+$BAj8_$KDL?.$9$k$3$H$,$G$-$k$h$&$K$J$j$^$9!#(B
+$B$=$N4D6-JQ?t$O!"%V%i%&%6%?%$%W(B (Netscape, IE, Lynx)$B!"(B
+$B%5!<%P%?%$%W(B (Apache, IIS, WebSite)$B!"<B9T$5$l$F$$$k(B CGI $B%W%m%0%i%`$NL>A0(B
+$B$J$I$N$h$&$J$b$N$G$9!#(B</p>
+
+<p>$B$3$l$i$NJQ?t$O(B CGI $B%W%m%0%i%^$,;HMQ$9$k$3$H$,$G$-$^$9!#$=$7$F!"(B
+$B$=$l$O%/%i%$%"%s%H$H%5!<%P$NDL?.$NOC$NH>J,$G$9!#I,MW$JJQ?t$N40A4$J%j%9%H$O(B
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html">http://hoohoo.ncsa.uiuc.edu/cgi/env.html</a>
+$B$K$"$j$^$9!#(B</p>
+
+<p>$B0J2<$NC1=c$J(B Perl CGI $B%W%m%0%i%`$O!"EO$5$l$kA4$F$N4D6-JQ?t$r(B
+$BI=<($7$^$9!#F1MM$N%W%m%0%i%`$O!"(BApache $B%G%#%9%H%j%S%e!<%7%g%s$N(B
+<code>cgi-bin</code> $B%G%#%l%/%H%j$KFs$D4^$^$l$F$$$^$9!#(B
+$B$$$/$D$+$NJQ?t$,I,?\$G$"$j!"$$$/$D$+$OG$0U$G$"$k$3$H$KCm0U$7$F$/$@$5$$!#(B
+$B$=$7$F!"8x<0$N%j%9%H$K$O$J$$$$$/$D$+$NJQ?t$,I=<($5$l$F$$$k$+$b$7$l$^$;$s!#(B
+$B$5$i$K!"(BApache $B$O%G%U%)%k%H$GMQ0U$5$l$F$$$k4pK\E*$J$b$N$K(B
+<a href="../env.html">$B$"$J$?<+?H$N4D6-JQ?t$r2C$($k(B</a>
+$B$?$a$N!"B?$/$N0[$J$kJ}K!$rMQ0U$7$F$7$^$9!#(B</p>
+
+<pre>
+ #!/usr/bin/perl
+ print "Content-type: text/html\n\n";
+ foreach $key (keys %ENV) {
+ print "$key --&gt; $ENV{$key}&lt;br&gt;";
+ }
+</pre>
+
+<h3><a name="stdinandstdout">STDIN $B$H(B STDOUT</a></h3>
+
+<p>$B%5!<%P$H%/%i%$%"%s%H4V$N$b$&0l$D$NDL?.$O!"I8=`F~NO(B (<code>STDIN</code>)$B$H(B
+$BI8=`=PNO(B (<code>STDOUT</code>) $B$rDL$8$F9T$J$o$l$^$9!#DL>o$NJ8L.$K$*$$$F!"(B
+<code>STDIN</code> $B$O%-!<%\!<%I$d%W%m%0%i%`$,F0:n$9$k$?$a$KM?$($i$l$k(B
+$B%U%!%$%k$r0UL#$7!"(B<code>STDOUT</code> $B$ODL>o%3%s%=!<%k$^$?$O%9%/%j!<%s$r(B
+$B0UL#$7$^$9!#(B</p>
+
+<p>$B%&%'%V%U%)!<%`$+$i(B CGI $B%W%m%0%i%`$X(B<code>POST</code> $B$7$?$H$-!"(B
+$B%U%)!<%`$N%G!<%?$OFCJL$J%U%)!<%^%C%H$GB+$M$i$l!"(B<code>STDIN</code> $B$r(B
+$BDL$7$F!"(BCGI $B%W%m%0%i%`$K0z$-EO$5$l$^$9!#%W%m%0%i%`$O%G!<%?$,%-!<%\!<%I(B
+$B$b$7$/$O%U%!%$%k$+$iMh$F$$$?$+$N$h$&$K=hM}$9$k$3$H$,$G$-$^$9!#(B</p>
+
+<P>$B!VFCJL$J%U%)!<%^%C%H!W$O$H$F$bC1=c$G$9!#%U%#!<%k%IL>$HCM$O%$%3!<%k(B
+(=) $B$G7k$P$l$^$9!#$=$7$FCM$NAH$O%"%s%Q%5%s%I(B (&amp;) $B$G(B
+$B7k$P$l$^$9!#%9%Z!<%9!"%"%s%Q%5%s%I!"%$%3!<%k$N$h$&$JLLE]$JJ8;z(B
+$B$O!"$=$l$i$,F0:n$rBLL\$K$7$J$$$h$&$K$=$NJ8;z$KAjEv$9$k(B 16 $B?J$KJQ49$5$l$^$9!#(B
+$BA4%G!<%?J8;zNs$O!"0J2<$N$h$&$K$J$j$^$9(B:</p>
+
+<pre>
+ name=Rich%20Bowen&amp;city=Lexington&amp;state=KY&amp;sidekick=Squirrel%20Monkey
+</pre>
+
+<p>$B;~!9!"$3$N$h$&$JJ8;zNs$,(B URL $B$KIU2C$5$l$k$N$r8+$k$G$7$g$&!#(B
+$B$=$N>l9g!"%5!<%P$O(B <code>QUERY_STRING</code> $B$H$$$&4D6-JQ?t$K$=$N(B
+$BJ8;zNs$rF~$l$^$9!#$=$l$O(B <code>GET</code> $B%j%/%(%9%H$H8F$P$l$^$9!#(B
+HTML $B%U%)!<%`$G$O!"%G!<%?$rEO$9$?$a$K(B <code>GET</code> $B$H(B <code>POST</code>
+$B$N$I$A$i$r;HMQ$9$k$+$r!"(B
+<code>FORM</code>$B%?%0$N(B <code>METHOD</code> $BB0@-$N@_Dj$G;XDj$7$^$9!#(B</p>
+
+<p>CGI $B%W%m%0%i%`$O!"$=$NJ8;zNs$rLr$KN)$D>pJs$KJ,3d$9$k@UG$$,$"$j$^$9!#(B
+$B9,$$$K$b!"$=$N%G!<%?=hM}$r=u$1$k%i%$%V%i%j$d%b%8%e!<%k$,B8:_$7$^$9!#$3$l$i$O!"(B
+CGI $B%W%m%0%i%`$NB>$NLL$G$bF1MM$KLr$KN)$A$^$9!#(B</p>
+
+<hr>
+<h2><a name="cgimoduleslibraries">CGI $B%b%8%e!<%k(B/$B%i%$%V%i%j(B</a></h2>
+
+<p>CGI $B%W%m%0%i%`$r=q$/$H$-!"LLE]$J;E;v$NBgItJ,$r$7$F$/$l$k(B
+$B%3!<%I%i%$%V%i%j$^$?$O%b%8%e!<%k$r;H$&$3$H$r8!F$$9$Y$-$G$9!#(B
+$B$3$l$O%(%i!<$r8:$i$7!"Aa$$3+H/$K$D$J$,$j$^$9!#(B</p>
+
+<p>Perl $B$G(B CGI $B%W%m%0%i%`$r=q$$$F$$$k$J$i!"%b%8%e!<%k$O(B
+<a href="http://www.cpan.org/">CPAN</a> $B$GDs6!$5$l$F$$$^$9!#$3$NL\E*$N$?$a$N(B
+$B:G$bIa5Z$7$F$$$k%b%8%e!<%k$O(B CGI.pm $B$G$9!#(B
+CGI::Lite $B$b8!F$$7$^$7$g$&!#$3$l$O!"$[$H$s$I$N%W%m%0%i%`$K$*$$$FI,MW$H$9$k$9$Y$F$N5!G=$N:G>.%;%C%H$N<BAu$G$9!#(B</p>
+
+<p>C $B$G(B CGI $B%W%m%0%i%`$r=q$$$F$$$k$J$i!"$$$m$$$m$J(B
+$B%*%W%7%g%s$,$"$j$^$9!#$3$l$i$NFb$N0l$D$O(B
+<a href="http://www.boutell.com/cgic/">http://www.boutell.com/cgic/</a>
+$B$GDs6!$5$l$F$$$k(B CGIC $B%i%$%V%i%j$G$9!#(B</p>
+
+<hr>
+<h2><a name="formoreinformation">$B99$J$k>pJs(B</a></h2>
+
+<p>CGI $B$K4X$9$k>pJs$O%&%'%V$G?tB?$/Ds6!$5$l$F$$$^$9!#(BCGI $B$NLdBj$K$D$$$F$O(B
+Usenet $B$N(B comp.infosystems.www.authoring.cgi $B$G!"B>$N%f!<%6$H(B
+$BO@5D$9$k$3$H$,$G$-$^$9!#(BHTML Writers Guide $B$N(B -servers$B%a!<%j%s%0%j%9%H(B
+$B$O!"$"$J$?$N<ALd$K2sEz$7$F$/$l$k0NBg$J%j%=!<%9$G$9!#(B
+<a href="http://www.hwg.org/lists/hwg-servers/">http://www.hwg.org/lists/hwg-servers/</a>
+$B$G99$KB?$/$rC5$7=P$9$3$H$,(B
+$B$G$-$^$9!#(B</p>
+
+<p>$B$=$7$F$b$A$m$s!"$*$=$i$/(B CGI $B%W%m%0%i%`$NF0:n$K4X$9$k>\:Y$N(B
+$BA4$F$,5-=R$5$l$F$$$k(B CGI $B$N;EMM$rFI$`$Y$-$G$9!#%*%j%8%J%k%P!<%8%g%s$r(B
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html">NCSA</a> $B$G!"(B
+$B%"%C%W%G!<%H$5$l$?%I%i%U%H$r(B
+<a href="http://web.golux.com/coar/cgi/">Common Gateway Interface RFC
+$B%W%m%8%'%/%H(B</a>$B$G;2>H$9$k$3$H$,$G$-$^$9!#(B</p>
+
+<p>CGI $B$NLdBj$K$D$$$F!"2C$o$C$F$$$k%a!<%j%s%0%j%9%H$^$?$O%K%e!<%9(B
+$B%0%k!<%W$K<ALd$rAw$k$H$-!"5/$3$C$?$b$N!"5/$3$C$F$[$7$$$3$H!"(B
+$B<B:]$K5/$3$C$?$3$H$,$I$&0c$&$+!";HMQ$7$F$$$k%5!<%P!"(B
+CGI $B%W%m%0%i%`$r5-=R$7$F$$$k8@8l$K4X$9$k==J,$J>pJs$H!"(B
+$B2DG=$G$"$l$PLdBj$N%3!<%I$rDs6!$9$k$h$&$K$7$F$/$@$5$$!#(B
+$B$=$&$9$k$3$H$G!"LdBj$,$h$j4VC1$K8+$D$+$k$h$&$K$J$j$^$9!#(B</p>
+
+<p>Apache $B$N%=!<%9%3!<%I$K$*$$$FLdBj$rH/8+$7$?$3$H$r3N?.$7$F$$$J$$(B
+$B8B$j!"(BCGI $B$NLdBj$K4X$9$k<ALd$r(B Apache $B%P%0%G!<%?%Y!<%9$KAw$k$Y$-$G$J$$(B
+$B$3$H$KCmL\$7$F$/$@$5$$!#(B</p>
+
+<!--#include virtual="footer.html" -->
+
+</body>
+</html>
+
diff --git a/docs/manual/howto/ssi.html b/docs/manual/howto/ssi.html
new file mode 100644
index 0000000000..0bd2a1f8bc
--- /dev/null
+++ b/docs/manual/howto/ssi.html
@@ -0,0 +1,519 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Apache Tutorial: Introduction to Server Side Includes</title>
+<link rev="made" href="mailto:rbowen@rcbowen.com">
+</head>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#000080"
+alink="#FF0000">
+<!--#include virtual="header.html" -->
+<h1 align="CENTER">Apache Tutorial: Introduction to Server Side
+Includes</h1>
+
+<a name="__index__"></a> <!-- INDEX BEGIN -->
+
+
+<ul>
+<li><a href=
+"#apachetutorial:introductiontoserversideincludes">Apache
+Tutorial: Introduction to Server Side Includes</a></li>
+
+<li><a href="#whataressi">What are SSI?</a></li>
+
+<li><a href="#configuringyourservertopermitssi">Configuring your
+server to permit SSI</a></li>
+
+<li><a href="#basicssidirectives">Basic SSI directives</a>
+
+<ul>
+ <li><a href="#today'sdate">Today's date</a></li>
+
+ <li><a href="#modificationdateofthefile">Modification date of the
+file</a></li>
+
+ <li><a href="#includingtheresultsofacgiprogram">Including the
+results of a CGI program</a></li>
+</ul>
+</li>
+
+<li><a href="#additionalexamples">Additional examples</a>
+
+<ul>
+<li><a href="#whenwasthisdocumentmodified">When was this document
+modified?</a></li>
+
+<li><a href="#includingastandardfooter">Including a standard
+footer</a></li>
+
+<li><a href="#whatelsecaniconfig">What else can I config?</a></li>
+
+<li><a href="#executingcommands">Executing commands</a></li>
+</ul>
+</li>
+
+<li><a href="#advancedssitechniques">Advanced SSI techniques</a>
+
+<ul>
+<li><a href="#settingvariables">Setting variables</a></li>
+
+<li><a href="#conditionalexpressions">Conditional expressions</a></li>
+</ul>
+</li>
+
+<li><a href="#conclusion">Conclusion</a></li>
+</ul>
+
+<!-- INDEX END -->
+<hr>
+<h2><a name=
+"apachetutorial:introductiontoserversideincludes">Apache
+Tutorial: Introduction to Server Side Includes</a></h2>
+
+<table border="1">
+<tr>
+<td valign="top"><strong>Related Modules</strong><br>
+<br>
+ <a href="../mod/mod_include.html">mod_include</a><br>
+<a href="../mod/mod_cgi.html">mod_cgi</a><br>
+<a href="../mod/mod_expires.html">mod_expires</a><br>
+ </td>
+<td valign="top"><strong>Related Directives</strong><br>
+<br>
+ <a href="../mod/core.html#options">Options</a><br>
+<a href="../mod/mod_include.html#xbithack">XBitHack</a><br>
+<a href="../mod/mod_mime.html#addtype">AddType</a><br>
+<a href="../mod/mod_mime.html#addhandler">AddHandler</a><br>
+<a href=
+"../mod/mod_setenvif.html#BrowserMatchNoCase">BrowserMatchNoCase</a><br>
+
+ </td>
+</tr>
+</table>
+
+<p>This HOWTO first appeared in Apache Today
+(http://www.apachetoday.com/) as a series of three articles. They
+appear here by arrangement with ApacheToday and Internet.com.</p>
+
+<p>This article deals with Server Side Includes, usually called simply
+SSI. In this article, I'll talk about configuring your server to permit
+SSI, and introduce some basic SSI techniques for adding dynamic content
+to your existing HTML pages.</p>
+
+<p>In the latter part of the article, we'll talk about some of the
+somewhat more advanced things that can be done with SSI, such as
+conditional statements in your SSI directives.</p>
+
+<hr>
+<h2><a name="whataressi">What are SSI?</a></h2>
+
+<p>SSI (Server Side Includes) are directives that are placed in HTML
+pages, and evaluated on the server while the pages are being served.
+They let you add dynamically generated content to an existing HTML
+page, without having to serve the entire page via a CGI program, or
+other dynamic technology.</p>
+
+<p>The decision of when to use SSI, and when to have your page entirely
+generated by some program, is usually a matter of how much of the page
+is static, and how much needs to be recalculated every time the page is
+served. SSI is a great way to add small pieces of information, such as
+the current time. But if a majority of your page is being generated at
+the time that it is served, you need to look for some other
+solution.</p>
+
+<hr>
+<h2><a name="configuringyourservertopermitssi">Configuring your
+server to permit SSI</a></h2>
+
+<p>To permit SSI on your server, you must have the following directive
+either in your <code>httpd.conf</code> file, or in a
+<code>.htaccess</code> file:</p>
+
+<pre>
+ Options +Includes
+</pre>
+
+<p>This tells Apache that you want to permit files to be parsed for SSI
+directives.</p>
+
+<p>Not just any file is parsed for SSI directives. You have to tell
+Apache which files should be parsed. There are two ways to do this. You
+can tell Apache to parse any file with a particular file extension,
+such as <code>.shtml</code>, with the following directives:</p>
+
+<pre>
+ AddType text/html .shtml
+ AddHandler server-parsed .shtml
+</pre>
+
+<p>One disadvantage to this approach is that if you wanted to add SSI
+directives to an existing page, you would have to change the name of
+that page, and all links to that page, in order to give it a
+<code>.shtml</code> extension, so that those directives would be
+executed.</p>
+
+<p>The other method is to use the <code>XBitHack</code> directive:</p>
+
+<pre>
+ XBitHack on
+</pre>
+
+<p><code>XBitHack</code> tells Apache to parse files for SSI directives
+if they have the execute bit set. So, to add SSI directives to an
+existing page, rather than having to change the file name, you would
+just need to make the file executable using <code>chmod</code>.</p>
+
+<pre>
+ chmod +x pagename.html
+</pre>
+
+<p>A brief comment about what not to do. You'll occasionally see people
+recommending that you just tell Apache to parse all <code>.html</code>
+files for SSI, so that you don't have to mess with <code>.shtml</code>
+file names. These folks have perhaps not heard about
+<code>XBitHack</code>. The thing to keep in mind is that, by doing
+this, you're requiring that Apache read through every single file that
+it sends out to clients, even if they don't contain any SSI directives.
+This can slow things down quite a bit, and is not a good idea.</p>
+
+<p>Of course, on Windows, there is no such thing as an execute bit to
+set, so that limits your options a little.</p>
+
+<p>In its default configuration, Apache does not send the last modified
+date or content length HTTP headers on SSI pages, because these values are
+difficult to calculate for dynamic content. This can prevent your
+document from being cached, and result in slower perceived client
+performance. There are two ways to solve this:</p>
+
+<ol>
+
+<li>Use the <code>XBitHack Full</code> configuration. This tells
+Apache to determine the last modified date by looking only at the date
+of the originally requested file, ignoring the modification date of
+any included files. </li>
+
+<li>Use the directives provided by <a
+href="../mod/mod_expires.html">mod_expires</a> to set an explicit
+expiration time on your files, thereby letting browsers and proxies
+know that it is acceptable to cache them. </li>
+
+</ol>
+
+
+<hr>
+<h2><a name="basicssidirectives">Basic SSI directives</a></h2>
+
+<p>SSI directives have the following syntax:</p>
+
+<pre>
+ &lt;!--#element attribute=value attribute=value ... --&gt;
+</pre>
+
+<p>It is formatted like an HTML comment, so if you don't have SSI
+correctly enabled, the browser will ignore it, but it will still be
+visible in the HTML source. If you have SSI correctly configured, the
+directive will be replaced with its results.</p>
+
+<p>The element can be one of a number of things, and we'll talk some
+more about most of these in the next installment of this series. For
+now, here are some examples of what you can do with SSI</p>
+
+<h3><a name="today'sdate">Today's date</a></h3>
+
+<pre>
+ &lt;!--#echo var=DATE_LOCAL --&gt;
+</pre>
+
+<p>The <code>echo</code> element just spits out the value of a
+variable. There are a number of standard variables, which include the
+whole set of environment variables that are available to CGI programs.
+Also, you can define your own variables with the <code>set</code>
+element.</p>
+
+<p>If you don't like the format in which the date gets printed, you can
+use the <code>config</code> element, with a <code>timefmt</code>
+attribute, to modify that formatting.</p>
+
+<pre>
+ &lt;!--#config timefmt="%A %B %d, %Y" --&gt;
+ Today is &lt;!--#echo var=DATE_LOCAL --&gt;
+</pre>
+
+<h3><a name="modificationdateofthefile">Modification date of the
+file</a></h3>
+
+<pre>
+ This document last modified &lt;!--#flastmod file="index.html" --&gt;
+</pre>
+
+<p>This element is also subject to <code>timefmt</code> format
+configurations.</p>
+
+<h3><a name="includingtheresultsofacgiprogram">Including the
+results of a CGI program</a></h3>
+
+<p>This is one of the more common uses of SSI - to output the results
+of a CGI program, such as everybody's favorite, a ``hit counter.''</p>
+
+<pre>
+ &lt;!--#include virtual="/cgi-bin/counter.pl" --&gt;
+</pre>
+
+<hr>
+<h2><a name="additionalexamples">Additional examples</a></h2>
+
+<p>Following are some specific examples of things you can do in your
+HTML documents with SSI.</p>
+
+<hr>
+<h2><a name="whenwasthisdocumentmodified">When was this document
+modified?</a></h2>
+
+<p>Earlier, we mentioned that you could use SSI to inform the user when
+the document was most recently modified. However, the actual method for
+doing that was left somewhat in question. The following code, placed in
+your HTML document, will put such a time stamp on your page. Of course,
+you will have to have SSI correctly enabled, as discussed above.</p>
+
+<pre>
+ &lt;!--#config timefmt="%A %B %d, %Y" --&gt;
+ This file last modified &lt;!--#flastmod file="ssi.shtml" --&gt;
+</pre>
+
+<p>Of course, you will need to replace the <code>ssi.shtml</code> with
+the actual name of the file that you're referring to. This can be
+inconvenient if you're just looking for a generic piece of code that
+you can paste into any file, so you probably want to use the
+<code>LAST_MODIFIED</code> variable instead:</p>
+
+<pre>
+ &lt;!--#config timefmt="%D" --&gt;
+ This file last modified &lt;!--#echo var="LAST_MODIFIED" --&gt;
+</pre>
+
+<p>For more details on the <code>timefmt</code> format, go to your
+favorite search site and look for <code>ctime</code>. The syntax is the
+same.</p>
+
+<hr>
+<h2><a name="includingastandardfooter">Including a standard
+footer</a></h2>
+
+<p>If you are managing any site that is more than a few pages, you may
+find that making changes to all those pages can be a real pain,
+particularly if you are trying to maintain some kind of standard look
+across all those pages.</p>
+
+<p>Using an include file for a header and/or a footer can reduce the
+burden of these updates. You just have to make one footer file, and
+then include it into each page with the <code>include</code> SSI
+command. The <code>include</code> element can determine what file to
+include with either the <code>file</code> attribute, or the
+<code>virtual</code> attribute. The <code>file</code> attribute is a
+file path, <em>relative to the current directory</em>. That means that
+it cannot be an absolute file path (starting with /), nor can it
+contain ../ as part of that path. The <code>virtual</code> attribute is
+probably more useful, and should specify a URL relative to the document
+being served. It can start with a /, but must be on the same server as
+the file being served.</p>
+
+<pre>
+ &lt;!--#include virtual="/footer.html" --&gt;
+</pre>
+
+<p>I'll frequently combine the last two things, putting a
+<code>LAST_MODIFIED</code> directive inside a footer file to be
+included. SSI directives can be contained in the included file, and
+includes can be nested - that is, the included file can include another
+file, and so on.</p>
+
+<hr>
+<h2><a name="whatelsecaniconfig">What else can I config?</a></h2>
+
+<p>In addition to being able to <code>config</code> the time format,
+you can also <code>config</code> two other things.</p>
+
+<p>Usually, when something goes wrong with your SSI directive, you get
+the message</p>
+
+<pre>
+ [an error occurred while processing this directive]
+</pre>
+
+<p>If you want to change that message to something else, you can do so
+with the <code>errmsg</code> attribute to the <code>config</code>
+element:</p>
+
+<pre>
+ &lt;!--#config errmsg="[It appears that you don't know how to use SSI]" --&gt;
+</pre>
+
+<p>Hopefully, end users will never see this message, because you will
+have resolved all the problems with your SSI directives before your
+site goes live. (Right?)</p>
+
+<p>And you can <code>config</code> the format in which file sizes are
+returned with the <code>sizefmt</code> attribute. You can specify
+<code>bytes</code> for a full count in bytes, or <code>abbrev</code>
+for an abbreviated number in Kb or Mb, as appropriate.</p>
+
+<hr>
+<h2><a name="executingcommands">Executing commands</a></h2>
+
+<p>I expect that I'll have an article some time in the coming months
+about using SSI with small CGI programs. For now, here's something else
+that you can do with the <code>exec</code> element. You can actually
+have SSI execute a command using the shell (<code>/bin/sh</code>, to be
+precise - or the DOS shell, if you're on Win32). The following, for
+example, will give you a directory listing.</p>
+
+<pre>
+ &lt;pre&gt;
+ &lt;!--#exec cmd="ls" --&gt;
+ &lt;/pre&gt;
+</pre>
+
+<p>or, on Windows</p>
+
+<pre>
+ &lt;pre&gt;
+ &lt;!--#exec cmd="dir" --&gt;
+ &lt;/pre&gt;
+</pre>
+
+<p>You might notice some strange formatting with this directive on
+Windows, because the output from <code>dir</code> contains the string
+``&lt;<code>dir</code>&gt;'' in it, which confuses browsers.</p>
+
+<p>Note that this feature is exceedingly dangerous, as it will execute
+whatever code happens to be embedded in the <code>exec</code> tag. If
+you have any situation where users can edit content on your web pages,
+such as with a ``guestbook'', for example, make sure that you have this
+feature disabled. You can allow SSI, but not the <code>exec</code>
+feature, with the <code>IncludesNOEXEC</code> argument to the
+<code>Options</code> directive.</p>
+
+<hr>
+<h2><a name="advancedssitechniques">Advanced SSI techniques</a></h2>
+
+<p>In addition to spitting out content, Apache SSI gives you the option
+of setting variables, and using those variables in comparisons and
+conditionals.</p>
+
+<h3><a name="caveat">Caveat</a></h3>
+
+<p>Most of the features discussed in this article are only available to
+you if you are running Apache 1.2 or later. Of course, if you are not
+running Apache 1.2 or later, you need to upgrade immediately, if not
+sooner. Go on. Do it now. We'll wait.</p>
+
+<hr>
+<h2><a name="settingvariables">Setting variables</a></h2>
+
+<p>Using the <code>set</code> directive, you can set variables for
+later use. We'll need this later in the discussion, so we'll talk about
+it here. The syntax of this is as follows:</p>
+
+<pre>
+ &lt;!--#set var="name" value="Rich" --&gt;
+</pre>
+
+<p>In addition to merely setting values literally like that, you can
+use any other variable, including, for example, environment variables,
+or some of the variables we discussed in the last article (like
+<code>LAST_MODIFIED</code>, for example) to give values to your
+variables. You will specify that something is a variable, rather than a
+literal string, by using the dollar sign ($) before the name of the
+variable.</p>
+
+<pre>
+ &lt;!--#set var="modified" value="$LAST_MODIFIED" --&gt;
+</pre>
+
+<p>To put a literal dollar sign into the value of your variable, you
+need to escape the dollar sign with a backslash.</p>
+
+<pre>
+ &lt;!--#set var="cost" value="\$100" --&gt;
+</pre>
+
+<p>Finally, if you want to put a variable in the midst of a longer
+string, and there's a chance that the name of the variable will run up
+against some other characters, and thus be confused with those
+characters, you can place the name of the variable in braces, to remove
+this confusion. (It's hard to come up with a really good example of
+this, but hopefully you'll get the point.)</p>
+
+<pre>
+ &lt;!--#set var="date" value="${DATE_LOCAL}_${DATE_GMT}" --&gt;
+</pre>
+
+<hr>
+<h2><a name="conditionalexpressions">Conditional expressions</a></h2>
+
+<p>Now that we have variables, and are able to set and compare their
+values, we can use them to express conditionals. This lets SSI be a
+tiny programming language of sorts. <code>mod_include</code> provides
+an <code>if</code>, <code>elif</code>, <code>else</code>,
+<code>endif</code> structure for building conditional statements. This
+allows you to effectively generate multiple logical pages out of one
+actual page.</p>
+
+<p>The structure of this conditional construct is:</p>
+
+<pre>
+ &lt;!--#if expr="test_condition" --&gt;
+ &lt;!--#elif expr="test_condition" --&gt;
+ &lt;!--#else --&gt;
+ &lt;!--#endif --&gt;
+</pre>
+
+<p>A <em>test_condition</em> can be any sort of logical comparison -
+either comparing values to one another, or testing the ``truth'' of a
+particular value. (A given string is true if it is nonempty.) For a
+full list of the comparison operators available to you, see the
+<code>mod_include</code> documentation. Here are some examples of how
+one might use this construct.</p>
+
+<p>In your configuration file, you could put the following line:</p>
+
+<pre>
+ BrowserMatchNoCase macintosh Mac
+ BrowserMatchNoCase MSIE InternetExplorer
+</pre>
+
+<p>This will set environment variables ``Mac'' and ``InternetExplorer''
+to true, if the client is running Internet Explorer on a Macintosh.</p>
+
+<p>Then, in your SSI-enabled document, you might do the following:</p>
+
+<pre>
+ &lt;!--#if expr="${Mac} &amp;&amp; ${InternetExplorer}" --&gt;
+ Apologetic text goes here
+ &lt;!--#else --&gt;
+ Cool JavaScript code goes here
+ &lt;!--#endif --&gt;
+</pre>
+
+<p>Not that I have anything against IE on Macs - I just struggled for a
+few hours last week trying to get some JavaScript working on IE on a
+Mac, when it was working everywhere else. The above was the interim
+workaround.</p>
+
+<p>Any other variable (either ones that you define, or normal
+environment variables) can be used in conditional statements. With
+Apache's ability to set environment variables with the
+<code>SetEnvIf</code> directives, and other related directives, this
+functionality can let you do some pretty involved dynamic stuff without
+ever resorting to CGI.</p>
+
+<hr>
+<h2><a name="conclusion">Conclusion</a></h2>
+
+<p>SSI is certainly not a replacement for CGI, or other technologies
+used for generating dynamic web pages. But it is a great way to add
+small amounts of dynamic content to pages, without doing a lot of extra
+work.</p>
+</body>
+</html>
+
diff --git a/docs/manual/howto/ssi.html.en b/docs/manual/howto/ssi.html.en
new file mode 100644
index 0000000000..0bd2a1f8bc
--- /dev/null
+++ b/docs/manual/howto/ssi.html.en
@@ -0,0 +1,519 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Apache Tutorial: Introduction to Server Side Includes</title>
+<link rev="made" href="mailto:rbowen@rcbowen.com">
+</head>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#000080"
+alink="#FF0000">
+<!--#include virtual="header.html" -->
+<h1 align="CENTER">Apache Tutorial: Introduction to Server Side
+Includes</h1>
+
+<a name="__index__"></a> <!-- INDEX BEGIN -->
+
+
+<ul>
+<li><a href=
+"#apachetutorial:introductiontoserversideincludes">Apache
+Tutorial: Introduction to Server Side Includes</a></li>
+
+<li><a href="#whataressi">What are SSI?</a></li>
+
+<li><a href="#configuringyourservertopermitssi">Configuring your
+server to permit SSI</a></li>
+
+<li><a href="#basicssidirectives">Basic SSI directives</a>
+
+<ul>
+ <li><a href="#today'sdate">Today's date</a></li>
+
+ <li><a href="#modificationdateofthefile">Modification date of the
+file</a></li>
+
+ <li><a href="#includingtheresultsofacgiprogram">Including the
+results of a CGI program</a></li>
+</ul>
+</li>
+
+<li><a href="#additionalexamples">Additional examples</a>
+
+<ul>
+<li><a href="#whenwasthisdocumentmodified">When was this document
+modified?</a></li>
+
+<li><a href="#includingastandardfooter">Including a standard
+footer</a></li>
+
+<li><a href="#whatelsecaniconfig">What else can I config?</a></li>
+
+<li><a href="#executingcommands">Executing commands</a></li>
+</ul>
+</li>
+
+<li><a href="#advancedssitechniques">Advanced SSI techniques</a>
+
+<ul>
+<li><a href="#settingvariables">Setting variables</a></li>
+
+<li><a href="#conditionalexpressions">Conditional expressions</a></li>
+</ul>
+</li>
+
+<li><a href="#conclusion">Conclusion</a></li>
+</ul>
+
+<!-- INDEX END -->
+<hr>
+<h2><a name=
+"apachetutorial:introductiontoserversideincludes">Apache
+Tutorial: Introduction to Server Side Includes</a></h2>
+
+<table border="1">
+<tr>
+<td valign="top"><strong>Related Modules</strong><br>
+<br>
+ <a href="../mod/mod_include.html">mod_include</a><br>
+<a href="../mod/mod_cgi.html">mod_cgi</a><br>
+<a href="../mod/mod_expires.html">mod_expires</a><br>
+ </td>
+<td valign="top"><strong>Related Directives</strong><br>
+<br>
+ <a href="../mod/core.html#options">Options</a><br>
+<a href="../mod/mod_include.html#xbithack">XBitHack</a><br>
+<a href="../mod/mod_mime.html#addtype">AddType</a><br>
+<a href="../mod/mod_mime.html#addhandler">AddHandler</a><br>
+<a href=
+"../mod/mod_setenvif.html#BrowserMatchNoCase">BrowserMatchNoCase</a><br>
+
+ </td>
+</tr>
+</table>
+
+<p>This HOWTO first appeared in Apache Today
+(http://www.apachetoday.com/) as a series of three articles. They
+appear here by arrangement with ApacheToday and Internet.com.</p>
+
+<p>This article deals with Server Side Includes, usually called simply
+SSI. In this article, I'll talk about configuring your server to permit
+SSI, and introduce some basic SSI techniques for adding dynamic content
+to your existing HTML pages.</p>
+
+<p>In the latter part of the article, we'll talk about some of the
+somewhat more advanced things that can be done with SSI, such as
+conditional statements in your SSI directives.</p>
+
+<hr>
+<h2><a name="whataressi">What are SSI?</a></h2>
+
+<p>SSI (Server Side Includes) are directives that are placed in HTML
+pages, and evaluated on the server while the pages are being served.
+They let you add dynamically generated content to an existing HTML
+page, without having to serve the entire page via a CGI program, or
+other dynamic technology.</p>
+
+<p>The decision of when to use SSI, and when to have your page entirely
+generated by some program, is usually a matter of how much of the page
+is static, and how much needs to be recalculated every time the page is
+served. SSI is a great way to add small pieces of information, such as
+the current time. But if a majority of your page is being generated at
+the time that it is served, you need to look for some other
+solution.</p>
+
+<hr>
+<h2><a name="configuringyourservertopermitssi">Configuring your
+server to permit SSI</a></h2>
+
+<p>To permit SSI on your server, you must have the following directive
+either in your <code>httpd.conf</code> file, or in a
+<code>.htaccess</code> file:</p>
+
+<pre>
+ Options +Includes
+</pre>
+
+<p>This tells Apache that you want to permit files to be parsed for SSI
+directives.</p>
+
+<p>Not just any file is parsed for SSI directives. You have to tell
+Apache which files should be parsed. There are two ways to do this. You
+can tell Apache to parse any file with a particular file extension,
+such as <code>.shtml</code>, with the following directives:</p>
+
+<pre>
+ AddType text/html .shtml
+ AddHandler server-parsed .shtml
+</pre>
+
+<p>One disadvantage to this approach is that if you wanted to add SSI
+directives to an existing page, you would have to change the name of
+that page, and all links to that page, in order to give it a
+<code>.shtml</code> extension, so that those directives would be
+executed.</p>
+
+<p>The other method is to use the <code>XBitHack</code> directive:</p>
+
+<pre>
+ XBitHack on
+</pre>
+
+<p><code>XBitHack</code> tells Apache to parse files for SSI directives
+if they have the execute bit set. So, to add SSI directives to an
+existing page, rather than having to change the file name, you would
+just need to make the file executable using <code>chmod</code>.</p>
+
+<pre>
+ chmod +x pagename.html
+</pre>
+
+<p>A brief comment about what not to do. You'll occasionally see people
+recommending that you just tell Apache to parse all <code>.html</code>
+files for SSI, so that you don't have to mess with <code>.shtml</code>
+file names. These folks have perhaps not heard about
+<code>XBitHack</code>. The thing to keep in mind is that, by doing
+this, you're requiring that Apache read through every single file that
+it sends out to clients, even if they don't contain any SSI directives.
+This can slow things down quite a bit, and is not a good idea.</p>
+
+<p>Of course, on Windows, there is no such thing as an execute bit to
+set, so that limits your options a little.</p>
+
+<p>In its default configuration, Apache does not send the last modified
+date or content length HTTP headers on SSI pages, because these values are
+difficult to calculate for dynamic content. This can prevent your
+document from being cached, and result in slower perceived client
+performance. There are two ways to solve this:</p>
+
+<ol>
+
+<li>Use the <code>XBitHack Full</code> configuration. This tells
+Apache to determine the last modified date by looking only at the date
+of the originally requested file, ignoring the modification date of
+any included files. </li>
+
+<li>Use the directives provided by <a
+href="../mod/mod_expires.html">mod_expires</a> to set an explicit
+expiration time on your files, thereby letting browsers and proxies
+know that it is acceptable to cache them. </li>
+
+</ol>
+
+
+<hr>
+<h2><a name="basicssidirectives">Basic SSI directives</a></h2>
+
+<p>SSI directives have the following syntax:</p>
+
+<pre>
+ &lt;!--#element attribute=value attribute=value ... --&gt;
+</pre>
+
+<p>It is formatted like an HTML comment, so if you don't have SSI
+correctly enabled, the browser will ignore it, but it will still be
+visible in the HTML source. If you have SSI correctly configured, the
+directive will be replaced with its results.</p>
+
+<p>The element can be one of a number of things, and we'll talk some
+more about most of these in the next installment of this series. For
+now, here are some examples of what you can do with SSI</p>
+
+<h3><a name="today'sdate">Today's date</a></h3>
+
+<pre>
+ &lt;!--#echo var=DATE_LOCAL --&gt;
+</pre>
+
+<p>The <code>echo</code> element just spits out the value of a
+variable. There are a number of standard variables, which include the
+whole set of environment variables that are available to CGI programs.
+Also, you can define your own variables with the <code>set</code>
+element.</p>
+
+<p>If you don't like the format in which the date gets printed, you can
+use the <code>config</code> element, with a <code>timefmt</code>
+attribute, to modify that formatting.</p>
+
+<pre>
+ &lt;!--#config timefmt="%A %B %d, %Y" --&gt;
+ Today is &lt;!--#echo var=DATE_LOCAL --&gt;
+</pre>
+
+<h3><a name="modificationdateofthefile">Modification date of the
+file</a></h3>
+
+<pre>
+ This document last modified &lt;!--#flastmod file="index.html" --&gt;
+</pre>
+
+<p>This element is also subject to <code>timefmt</code> format
+configurations.</p>
+
+<h3><a name="includingtheresultsofacgiprogram">Including the
+results of a CGI program</a></h3>
+
+<p>This is one of the more common uses of SSI - to output the results
+of a CGI program, such as everybody's favorite, a ``hit counter.''</p>
+
+<pre>
+ &lt;!--#include virtual="/cgi-bin/counter.pl" --&gt;
+</pre>
+
+<hr>
+<h2><a name="additionalexamples">Additional examples</a></h2>
+
+<p>Following are some specific examples of things you can do in your
+HTML documents with SSI.</p>
+
+<hr>
+<h2><a name="whenwasthisdocumentmodified">When was this document
+modified?</a></h2>
+
+<p>Earlier, we mentioned that you could use SSI to inform the user when
+the document was most recently modified. However, the actual method for
+doing that was left somewhat in question. The following code, placed in
+your HTML document, will put such a time stamp on your page. Of course,
+you will have to have SSI correctly enabled, as discussed above.</p>
+
+<pre>
+ &lt;!--#config timefmt="%A %B %d, %Y" --&gt;
+ This file last modified &lt;!--#flastmod file="ssi.shtml" --&gt;
+</pre>
+
+<p>Of course, you will need to replace the <code>ssi.shtml</code> with
+the actual name of the file that you're referring to. This can be
+inconvenient if you're just looking for a generic piece of code that
+you can paste into any file, so you probably want to use the
+<code>LAST_MODIFIED</code> variable instead:</p>
+
+<pre>
+ &lt;!--#config timefmt="%D" --&gt;
+ This file last modified &lt;!--#echo var="LAST_MODIFIED" --&gt;
+</pre>
+
+<p>For more details on the <code>timefmt</code> format, go to your
+favorite search site and look for <code>ctime</code>. The syntax is the
+same.</p>
+
+<hr>
+<h2><a name="includingastandardfooter">Including a standard
+footer</a></h2>
+
+<p>If you are managing any site that is more than a few pages, you may
+find that making changes to all those pages can be a real pain,
+particularly if you are trying to maintain some kind of standard look
+across all those pages.</p>
+
+<p>Using an include file for a header and/or a footer can reduce the
+burden of these updates. You just have to make one footer file, and
+then include it into each page with the <code>include</code> SSI
+command. The <code>include</code> element can determine what file to
+include with either the <code>file</code> attribute, or the
+<code>virtual</code> attribute. The <code>file</code> attribute is a
+file path, <em>relative to the current directory</em>. That means that
+it cannot be an absolute file path (starting with /), nor can it
+contain ../ as part of that path. The <code>virtual</code> attribute is
+probably more useful, and should specify a URL relative to the document
+being served. It can start with a /, but must be on the same server as
+the file being served.</p>
+
+<pre>
+ &lt;!--#include virtual="/footer.html" --&gt;
+</pre>
+
+<p>I'll frequently combine the last two things, putting a
+<code>LAST_MODIFIED</code> directive inside a footer file to be
+included. SSI directives can be contained in the included file, and
+includes can be nested - that is, the included file can include another
+file, and so on.</p>
+
+<hr>
+<h2><a name="whatelsecaniconfig">What else can I config?</a></h2>
+
+<p>In addition to being able to <code>config</code> the time format,
+you can also <code>config</code> two other things.</p>
+
+<p>Usually, when something goes wrong with your SSI directive, you get
+the message</p>
+
+<pre>
+ [an error occurred while processing this directive]
+</pre>
+
+<p>If you want to change that message to something else, you can do so
+with the <code>errmsg</code> attribute to the <code>config</code>
+element:</p>
+
+<pre>
+ &lt;!--#config errmsg="[It appears that you don't know how to use SSI]" --&gt;
+</pre>
+
+<p>Hopefully, end users will never see this message, because you will
+have resolved all the problems with your SSI directives before your
+site goes live. (Right?)</p>
+
+<p>And you can <code>config</code> the format in which file sizes are
+returned with the <code>sizefmt</code> attribute. You can specify
+<code>bytes</code> for a full count in bytes, or <code>abbrev</code>
+for an abbreviated number in Kb or Mb, as appropriate.</p>
+
+<hr>
+<h2><a name="executingcommands">Executing commands</a></h2>
+
+<p>I expect that I'll have an article some time in the coming months
+about using SSI with small CGI programs. For now, here's something else
+that you can do with the <code>exec</code> element. You can actually
+have SSI execute a command using the shell (<code>/bin/sh</code>, to be
+precise - or the DOS shell, if you're on Win32). The following, for
+example, will give you a directory listing.</p>
+
+<pre>
+ &lt;pre&gt;
+ &lt;!--#exec cmd="ls" --&gt;
+ &lt;/pre&gt;
+</pre>
+
+<p>or, on Windows</p>
+
+<pre>
+ &lt;pre&gt;
+ &lt;!--#exec cmd="dir" --&gt;
+ &lt;/pre&gt;
+</pre>
+
+<p>You might notice some strange formatting with this directive on
+Windows, because the output from <code>dir</code> contains the string
+``&lt;<code>dir</code>&gt;'' in it, which confuses browsers.</p>
+
+<p>Note that this feature is exceedingly dangerous, as it will execute
+whatever code happens to be embedded in the <code>exec</code> tag. If
+you have any situation where users can edit content on your web pages,
+such as with a ``guestbook'', for example, make sure that you have this
+feature disabled. You can allow SSI, but not the <code>exec</code>
+feature, with the <code>IncludesNOEXEC</code> argument to the
+<code>Options</code> directive.</p>
+
+<hr>
+<h2><a name="advancedssitechniques">Advanced SSI techniques</a></h2>
+
+<p>In addition to spitting out content, Apache SSI gives you the option
+of setting variables, and using those variables in comparisons and
+conditionals.</p>
+
+<h3><a name="caveat">Caveat</a></h3>
+
+<p>Most of the features discussed in this article are only available to
+you if you are running Apache 1.2 or later. Of course, if you are not
+running Apache 1.2 or later, you need to upgrade immediately, if not
+sooner. Go on. Do it now. We'll wait.</p>
+
+<hr>
+<h2><a name="settingvariables">Setting variables</a></h2>
+
+<p>Using the <code>set</code> directive, you can set variables for
+later use. We'll need this later in the discussion, so we'll talk about
+it here. The syntax of this is as follows:</p>
+
+<pre>
+ &lt;!--#set var="name" value="Rich" --&gt;
+</pre>
+
+<p>In addition to merely setting values literally like that, you can
+use any other variable, including, for example, environment variables,
+or some of the variables we discussed in the last article (like
+<code>LAST_MODIFIED</code>, for example) to give values to your
+variables. You will specify that something is a variable, rather than a
+literal string, by using the dollar sign ($) before the name of the
+variable.</p>
+
+<pre>
+ &lt;!--#set var="modified" value="$LAST_MODIFIED" --&gt;
+</pre>
+
+<p>To put a literal dollar sign into the value of your variable, you
+need to escape the dollar sign with a backslash.</p>
+
+<pre>
+ &lt;!--#set var="cost" value="\$100" --&gt;
+</pre>
+
+<p>Finally, if you want to put a variable in the midst of a longer
+string, and there's a chance that the name of the variable will run up
+against some other characters, and thus be confused with those
+characters, you can place the name of the variable in braces, to remove
+this confusion. (It's hard to come up with a really good example of
+this, but hopefully you'll get the point.)</p>
+
+<pre>
+ &lt;!--#set var="date" value="${DATE_LOCAL}_${DATE_GMT}" --&gt;
+</pre>
+
+<hr>
+<h2><a name="conditionalexpressions">Conditional expressions</a></h2>
+
+<p>Now that we have variables, and are able to set and compare their
+values, we can use them to express conditionals. This lets SSI be a
+tiny programming language of sorts. <code>mod_include</code> provides
+an <code>if</code>, <code>elif</code>, <code>else</code>,
+<code>endif</code> structure for building conditional statements. This
+allows you to effectively generate multiple logical pages out of one
+actual page.</p>
+
+<p>The structure of this conditional construct is:</p>
+
+<pre>
+ &lt;!--#if expr="test_condition" --&gt;
+ &lt;!--#elif expr="test_condition" --&gt;
+ &lt;!--#else --&gt;
+ &lt;!--#endif --&gt;
+</pre>
+
+<p>A <em>test_condition</em> can be any sort of logical comparison -
+either comparing values to one another, or testing the ``truth'' of a
+particular value. (A given string is true if it is nonempty.) For a
+full list of the comparison operators available to you, see the
+<code>mod_include</code> documentation. Here are some examples of how
+one might use this construct.</p>
+
+<p>In your configuration file, you could put the following line:</p>
+
+<pre>
+ BrowserMatchNoCase macintosh Mac
+ BrowserMatchNoCase MSIE InternetExplorer
+</pre>
+
+<p>This will set environment variables ``Mac'' and ``InternetExplorer''
+to true, if the client is running Internet Explorer on a Macintosh.</p>
+
+<p>Then, in your SSI-enabled document, you might do the following:</p>
+
+<pre>
+ &lt;!--#if expr="${Mac} &amp;&amp; ${InternetExplorer}" --&gt;
+ Apologetic text goes here
+ &lt;!--#else --&gt;
+ Cool JavaScript code goes here
+ &lt;!--#endif --&gt;
+</pre>
+
+<p>Not that I have anything against IE on Macs - I just struggled for a
+few hours last week trying to get some JavaScript working on IE on a
+Mac, when it was working everywhere else. The above was the interim
+workaround.</p>
+
+<p>Any other variable (either ones that you define, or normal
+environment variables) can be used in conditional statements. With
+Apache's ability to set environment variables with the
+<code>SetEnvIf</code> directives, and other related directives, this
+functionality can let you do some pretty involved dynamic stuff without
+ever resorting to CGI.</p>
+
+<hr>
+<h2><a name="conclusion">Conclusion</a></h2>
+
+<p>SSI is certainly not a replacement for CGI, or other technologies
+used for generating dynamic web pages. But it is a great way to add
+small amounts of dynamic content to pages, without doing a lot of extra
+work.</p>
+</body>
+</html>
+
diff --git a/docs/manual/howto/ssi.html.ja.jis b/docs/manual/howto/ssi.html.ja.jis
new file mode 100644
index 0000000000..eafa3150b7
--- /dev/null
+++ b/docs/manual/howto/ssi.html.ja.jis
@@ -0,0 +1,501 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Apache $B%A%e!<%H%j%"%k(B: Server Side Includes $BF~Lg(B</title>
+<!-- link rev="made" href="mailto:rbowen@rcbowen.com" -->
+</head>
+<!-- English revision: 1.7 -->
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#000080"
+alink="#FF0000">
+<!--#include virtual="header.html" -->
+<h1 align="CENTER">Apache $B%A%e!<%H%j%"%k(B: Server Side Includes $BF~Lg(B</h1>
+
+<a name="__index__"></a> <!-- INDEX BEGIN -->
+
+
+<ul>
+<li><a href="#apachetutorial:introductiontoserversideincludes">Apache
+ $B%A%e!<%H%j%"%k(B: Server Side Includes $BF~Lg(B</a></li>
+
+<li><a href="#whataressi">SSI $B$H$O(B?</a></li>
+
+<li><a href="#configuringyourservertopermitssi">SSI $B$r5v2D$9$k$?$a$N(B
+$B%5!<%P$N@_Dj(B</a></li>
+
+<li><a href="#basicssidirectives">$B4pK\E*$J(B SSI $B%G%#%l%/%F%#%V(B</a>
+
+<ul>
+ <li><a href="#today'sdate">$B:#F|$NF|IU(B</a></li>
+
+ <li><a href="#modificationdateofthefile">$B%U%!%$%k$NJQ99F|(B</a></li>
+
+ <li><a href="#includingtheresultsofacgiprogram">CGI $B%W%m%0%i%`$N7k2L$r(B
+$B<h$j9~$`(B</a></li>
+</ul>
+</li>
+
+<li><a href="#additionalexamples">$BDI2C$NNc(B</a>
+
+<ul>
+<li><a href="#whenwasthisdocumentmodified">$B$$$D$3$N%I%-%e%a%s%H$O(B
+$B=$@5$5$l$?$N$+(B?</a></li>
+
+<li><a href="#includingastandardfooter">$BI8=`$N%U%C%?$rA^F~$9$k(B</a></li>
+
+<li><a href="#whatelsecaniconfig">$BB>$K2?$,@_Dj$G$-$k$N$+(B?</a></li>
+
+<li><a href="#executingcommands">$B%3%^%s%I$N<B9T(B</a></li>
+</ul>
+</li>
+
+<li><a href="#advancedssitechniques">$B9bEY$J(B SSI $B%F%/%K%C%/(B</a>
+
+<ul>
+<li><a href="#settingvariables">$BJQ?t$r@_Dj$9$k(B</a></li>
+
+<li><a href="#conditionalexpressions">$B>r7o<0(B</a></li>
+</ul>
+</li>
+
+<li><a href="#conclusion">$B=*$o$j$K(B</a></li>
+</ul>
+
+<!-- INDEX END -->
+<hr>
+<h2><a name=
+"apachetutorial:introductiontoserversideincludes">Apache
+$B%A%e!<%H%j%"%k(B: Server Side Includes $BF~Lg(B</a></h2>
+
+<table border="1">
+<tr>
+<td valign="top"><strong>$B4XO"%b%8%e!<%k(B</strong><br>
+<br>
+ <a href="../mod/mod_include.html">mod_include</a><br>
+<a href="../mod/mod_cgi.html">mod_cgi</a><br>
+<a href="../mod/mod_expires.html">mod_expires</a><br>
+ </td>
+<td valign="top"><strong>$B4XO"%G%#%l%/%F%#%V(B</strong><br>
+<br>
+ <a href="../mod/core.html#options">Options</a><br>
+<a href="../mod/mod_include.html#xbithack">XBitHack</a><br>
+<a href="../mod/mod_mime.html#addtype">AddType</a><br>
+<a href="../mod/mod_mime.html#addhandler">AddHandler</a><br>
+<a href=
+"../mod/mod_setenvif.html#BrowserMatchNoCase">BrowserMatchNoCase</a><br>
+
+ </td>
+</tr>
+</table>
+
+<p>$B$3$NJ8=q$O:G=i!"(BApache Today (http://www.apachetoday.com/) $B$K;02s$NO":\5-;v$H$7$F7G:\$5$l$^$7$?!#(B
+$B$3$3$G$O!"(BApcheToday $B$H(B Internet.com $B$H$N6(Dj$K$h$j:\$;$F$$$^$9!#(B</p>
+
+<p>$B$3$N5-;v$O!"DL>o$OC1$K(B SSI $B$H8F$P$l$k(B Server Side Includes $B$r(B
+$B07$$$^$9!#$3$N5-;v$K$*$$$F$O!"%5!<%P$G$N(B SSI $B$r5v2D$9$k$?$a$N@_Dj$H!"(B
+$B8=:_$N(B HTML $B%Z!<%8$KF0E*$J%3%s%F%s%D$r2C$($k$?$a$N$$$/$D$+$N4pK\E*$J(B
+SSI $B5;=Q$r>R2p$7$^$9!#(B</p>
+
+<p>$B5-;v$N8eH>$G$O!"(BSSI $B%G%#%l%/%F%#%V$G(B
+SSI $B$H6&$K<B9T$9$k$3$H$,$G$-$k>r7oJ8$N$h$&$J4vJ,9bEY$J;vJA$K(B
+$B$D$$$F=R$Y$F$$$^$9!#(B</p>
+
+<hr>
+<h2><a name="whataressi">SSI $B$H$O(B?</a></h2>
+
+<p>SSI (Server Side Includes) $B$O!"(BHTML $B%Z!<%8Cf$KG[CV$5$l$k%G%#%l%/%F%#%V$G$"$j!"(B
+$B%5!<%P$G%Z!<%8$rDs6!$9$k;~$KI>2A$5$l$^$9!#(B
+SSI $B$O!"(BCGI $B%W%m%0%i%`$d$=$NB>$NF0E*$J5;=Q$GA4$F$N%Z!<%8$rDs6!(B
+$B$;$:$K!"F0E*$K@8@.$5$l$?%3%s%F%s%D$r8=:_$N(B HTML $B%Z!<%8$K(B
+$B2C$($^$9!#(B</p>
+
+<p>$B$I$&$$$&>l9g$K(B SSI $B$r;H$$!"$I$&$$$&>l9g$K%W%m%0%i%`$G%Z!<%8$r40A4$K@8@.$9$k$+(B
+$B$O!"%Z!<%8$N$&$A$I$NDxEY$,@EE*$G$"$j!"%Z!<%8$,Ds6!$5$l$k$?$S$K(B
+$B:F7W;;$9$kI,MW$,$I$NDxEY$"$k$+$GDL>o$O7hDj$7$^$9!#(BSSI $B$O8=:_;~9o$N$h$&$J(B
+$B>.$5$$>pJs$r2C$($k$K$O$&$C$F$D$1$NJ}K!$G$9!#(B
+$B$7$+$7!"$=$N%Z!<%8$N$[$H$s$I$NItJ,$,Ds6!;~$K@8@.$5$l$k>l9g$O!"(B
+$BB>$NJ}K!$rC5$9I,MW$,$"$j$^$9!#(B</p>
+
+<hr>
+<h2><a name="configuringyourservertopermitssi">SSI $B$r5v2D$9$k$?$a$N(B
+$B%5!<%P$N@_Dj(B</a></h2>
+
+<p>$B%5!<%P$G(B SSI $B5v2D$9$k$K$O!"(B<code>httpd.conf</code> $B%U%!%$%k(B
+$B$^$?$O(B <code>.htaccess</code> $B%U%!%$%k$K<!$N%G%#%l%/%F%#%V$r;XDj$9$kI,MW$,$"$j$^$9(B:</p>
+
+<pre>
+ Options +Includes
+</pre>
+
+<p>$B$3$N;XDj$O!"%U%!%$%k$r(B SSI $B%G%#%l%/%F%#%V$G2r@O$5$;$k$3$H$r5v2D$9$k(B
+$B$H$$$&$3$H$r(B Apache $B$KEA$($^$9!#(B</p>
+
+<p>$BA4$F$N%U%!%$%k$,(B SSI $B%G%#%l%/%F%#%V$G2r@O$5$l$k$H$$$&$o$1$G$O$"$j$^$;$s!#(B
+$B$I$N%U%!%$%k$,2r@O$5$l$k$+$r(B Apache $B$KEA$($kI,MW$,$"$j$^$9!#$3$l$r(B
+$B9T$J$&$K$OFs$DJ}K!$,$"$j$^$9!#<!$N%G%#%l%/%F%#%V$r;H$&$3$H$G!"(B
+$BNc$($P(B <code>.shtml</code> $B$N$h$&$JFCJL$J%U%!%$%k3HD%;R$r;}$D%U%!%$%k$r(B
+$B2r@O$9$k$h$&(B Apache $B$KEA$($k$3$H$,$G$-$^$9(B:</p>
+
+<pre>
+ AddType text/html .shtml
+ AddHandler server-parsed .shtml
+</pre>
+
+<p>$B$3$NJ}K!$N7gE@$O!"$b$78=:_$N%Z!<%8$K(B SSI
+$B%G%#%l%/%F%#%V$r2C$($?$$>l9g!"$=$l$i$N%G%#%l%/%F%#%V$,<B9T$5$l$k(B
+$B$h$&$K(B <code>.shtml</code> $B3HD%;R$K$9$k$?$a!"$=$N%Z!<%8$NL>A0$H!"(B
+$B$=$N%Z!<%8$X$NA4$F$N%j%s%/$rJQ99$7$J$1$l$P$J$i$J$$$3$H$G$9!#(B</p>
+
+<p>$B$b$&0l$D$NJ}K!$O!"(B<code>XBitHack</code> $B%G%#%l%/%F%#%V$r;HMQ$9$k$3$H$G$9(B:</p>
+
+<pre>
+ XBitHack on
+</pre>
+
+<p><code>XBitHack</code> $B$O!"%U%!%$%k$N<B9T%S%C%H$,N)$C$F$$$k>l9g!"(B
+SSI $B%G%#%l%/%F%#%V$K$h$j2r@O$9$k$3$H$r(B Apache $B$KEA$($^$9!#=>$C$F!"(B
+SSI $B%G%#%l%/%F%#%V$r8=:_$N%Z!<%8$K2C$($k$?$a$K$O!"%U%!%$%kL>$rJQ99$7$J$/$F$b$h$/!"(B
+$BC1$K(B <code>chmod</code> $B$r;HMQ$7$F%U%!%$%k$r<B9T2DG=$K$9$k(B
+$B$@$1$G:Q$_$^$9!#(B</p>
+
+<pre>
+ chmod +x pagename.html
+</pre>
+
+<p>$B9T$J$&$Y$-$G$O$J$$$3$H$K4X$9$kC;$$%3%a%s%H!#;~!9C/$+$,!"(B
+$BA4$F$N(B <code>.html</code> $B%U%!%$%k$r(B SSI $B$G2r@O$9$k$h$&(B Apache $B$KEA$($l$P!"(B
+$B$o$6$o$6(B <code>.shtml</code> $B$H$$$&%U%!%$%kL>$K$9$kI,MW$,$J$$$H$$$C$F(B
+$BA&$a$k$N$r8+$k$3$H$G$7$g$&!#$3$&$$$&?M$?$A$O!"$*$=$i$/(B <code>XBitHack</code>
+$B$K$D$$$FJ9$$$?$3$H$,$J$$$N$G$7$g$&!#$3$NJ}K!$K$D$$$FCm0U$9$k$3$H$O!"(B
+$B$?$H$((B SSI $B%G%#%l%/%F%#%V$rA4$/4^$^$J$$>l9g$G$b!"(BApache $B$,%/%i%$%"%s%H$K(B
+$BAw$kA4$F$N%U%!%$%k$r:G8e$^$GFI$_9~$^$;$k$3$H$K$J$j$^$9!#(B
+$B$3$NJ}K!$O$+$J$j=hM}$rCY$/$9$k$b$N$G$"$j!"NI$/$J$$%"%$%G%"$G$9!#(B</p>
+
+<p>$B$b$A$m$s!"(BWindows $B$G$O$=$N$h$&$J<B9T%S%C%H$r%;%C%H$9$k$h$&$J$b$N$O(B
+$B$"$j$^$;$s$N$G%*%W%7%g%s$,>/$7@)8B$5$l$F$$$^$9!#(B</p>
+
+<p>$B%G%U%)%k%H$N@_Dj$G$O!"(BApache $B$O(B SSI $B%Z!<%8$K$D$$$F:G=*JQ99;~9o$d(B
+$B%3%s%F%s%D$ND9$5$r(B HTTP $B%X%C%@$KAw$j$^$;$s!#(B
+$BF0E*$J%3%s%F%s%D$G$"$k$?$a!"$=$l$i$NCM$r7W;;$9$k$N$,Fq$7$$$+$i$G$9!#(B
+$B$3$N$?$a%I%-%e%a%s%H$,(B
+$B%-%c%C%7%e$5$l$J$/$J$j!"7k2L$H$7$F%/%i%$%"%s%H$N@-G=$,(B
+$BCY$/$J$C$?$h$&$K46$8$5$;$k$3$H$K$J$j$^$9!#(B
+$B$3$l$r2r7h$9$kJ}K!$,Fs$D$"$j$^$9(B:</p>
+
+<ol>
+
+<li><code>XBitHack Full</code> $B@_Dj$r;HMQ$9$k!#$3$N@_Dj$K$h$j!"$b$H$b$HMW5a$5$l$?(B
+$B%U%!%$%k$N;~9o$r;2>H$7!"FI$_9~$^$l$k%U%!%$%k$NJQ99;~9o$r(B
+$BL5;k$7$F:G=*JQ99;~9o$r7hDj$9$k$h$&(B Apache $B$KEA$($^$9!#(B</li>
+
+<li><a href="../mod/mod_expires.html">mod_expires</a> $B$GDs6!$5$l$F$$$k%G%#%l%/%F%#%V$r;HMQ$7$F!"(B
+$B%U%!%$%k$,L58z$K$J$k;~9o$rL@<($7$^$9!#(B
+$B$3$l$K$h$j!"%V%i%&%6$H%W%m%-%7$K%-%c%C%7%e$,M-8z$G$"$k$3$H$rDLCN$7$^$9!#(B</li>
+
+</ol>
+
+<hr>
+<h2><a name="basicssidirectives">$B4pK\E*$J(B SSI $B%G%#%l%/%F%#%V(B</a></h2>
+
+<p>SSI $B%G%#%l%/%F%#%V$O0J2<$NJ8K!$G5-=R$7$^$9(B:</p>
+
+<pre>
+ &lt;!--#element attribute=value attribute=value ... --&gt;
+</pre>
+
+<p>HTML $B$N%3%a%s%H$N$h$&$J=q<0$r$7$F$$$k$N$G!"$b$7(B SSI $B$r(B
+$B@5$7$/F0:n2DG=$K$7$J$1$l$P!"%V%i%&%6$O$=$l$rL5;k$9$k$G$7$g$&!#$7$+$7!"(B
+HTML $B%=!<%9Cf$G$O8+$($^$9!#$b$7(B SSI $B$r@5$7$/@_Dj$7$?$J$i!"(B
+$B%G%#%l%/%F%#%V$O$=$N7k2L$HCV$-49$($i$l$^$9!#(B</p>
+
+<p>element $B$O$?$/$5$s$"$k$b$N$+$i0l$D;XDj$9$k$3$H$,$G$-$^$9!#(B
+$B;XDj$G$-$k$b$N$NBgB??t$K$D$$$F$O!"<!2s$b$&>/$7>\$7$/@bL@$7$^$9!#(B
+$B$3$3$G$O!"(BSSI $B$G9T$J$&$3$H$,$G$-$kNc$r$$$/$D$+<($7$^$9!#(B</p>
+
+<h3><a name="today'sdate">$B:#F|$NF|IU(B</a></h3>
+
+<pre>
+ &lt;!--#echo var="DATE_LOCAL" --&gt;
+</pre>
+
+<p><code>echo</code> $BMWAG$OC1$KJQ?t$NCM$r=PNO$7$^$9!#(BCGI $B%W%m%0%i%`$K(B
+$BMxMQ2DG=$J4D6-JQ?t$NA4$F$N%;%C%H$r4^$`B?$/$NI8=`JQ?t$,$"$j$^$9!#(B
+$B$^$?!"(B<code>set</code> $BMWAG$rMQ$$$k$3$H$G!"FH<+$NJQ?t$rDj5A$9$k$3$H$,(B
+$B$G$-$^$9!#(B</p>
+
+<p>$B=PNO$5$l$kF|IU$N=q<0$,9%$-$G$O$J$$>l9g!"$=$N=q<0$r(B
+$B=$@5$9$k$?$a$K!"(B<code>config</code> $BMWAG$K(B <code>timefmt</code> $BB0@-$r(B
+$B;HMQ$9$k$3$H$,$G$-$^$9!#(B</p>
+
+<pre>
+ &lt;!--#config timefmt="%A %B %d, %Y" --&gt;
+ Today is &lt;!--#echo var="DATE_LOCAL" --&gt;
+</pre>
+
+<h3><a name="modificationdateofthefile">$B%U%!%$%k$NJQ99F|(B</a></h3>
+
+<pre>
+ This document last modified &lt;!--#flastmod file="index.html" --&gt;
+</pre>
+
+<p>$B$3$NMWAG$b(B <code>timefmt</code> $B%U%)!<%^%C%H$N@_Dj$K=>$$$^$9!#(B</p>
+
+<h3><a name="includingtheresultsofacgiprogram">CGI $B%W%m%0%i%`$N7k2L$r<h$j9~$`(B
+</a></h3>
+
+<p>$B$3$l$O!"A4$F$N?M$N$*5$$KF~$j$G$"$k(B ``$B%R%C%H%+%&%s%?(B'' $B$N$h$&$J(B CGI $B%W%m%0%i%`$N(B
+$B7k2L$r=PNO$9$k(B SSI $B$N$h$j0lHLE*$J;HMQ$N$&$A$N0l$D$G$9!#(B</p>
+
+<pre>
+ &lt;!--#include virtual="/cgi-bin/counter.pl" --&gt;
+</pre>
+
+<hr>
+<h2><a name="additionalexamples">$BDI2C$NNc(B</a></h2>
+
+<p>$B0J2<$O!"(BSSI $B$r;HMQ$7$F(B HTML $B%I%-%e%a%s%H$K$*$$$F$G$-$k$3$H$N(B
+$B$$$/$D$+$NFCJL$JNc$G$9!#(B</p>
+
+<hr>
+<h2><a name="whenwasthisdocumentmodified">$B$$$D$3$N%I%-%e%a%s%H$O=$@5$5$l$?$N$+(B?
+</a></h2>
+
+<p>$B@h$K!"%I%-%e%a%s%H$,:G8e$KJQ99$5$l$?$N$O$$$D$+$r%f!<%6$KDLCN$9$k$?$a$K(B SSI $B$r;HMQ$9$k$3$H$,$G$-$k$3$H$r(B
+$B=R$Y$^$7$?!#$7$+$7$J$,$i!"<B:]$NJ}K!$O!"$$$/$V$sLdBj$N$^$^$K$7$F$*$-$^$7$?!#(B
+HTML $B%I%-%e%a%s%H$KG[CV$5$l$?<!$N%3!<%I$O!"%Z!<%8$K(B
+$B$=$N$h$&$J%?%$%`%9%?%s%W$rF~$l$k$G$7$g$&!#$b$A$m$s!">e=R$N(B
+$B$h$&$K!"(BSSI $B$r@5$7$/F0:n2DG=$K$7$F$*$/I,MW$,$"$j$^$9!#(B</p>
+
+<pre>
+ &lt;!--#config timefmt="%A %B %d, %Y" --&gt;
+ This file last modified &lt;!--#flastmod file="ssi.shtml" --&gt;
+</pre>
+
+<p>$B$b$A$m$s!"(B<code>ssi.shtml</code> $B$NItJ,$r<B:]$NEv3:%U%!%$%kL>$H(B
+$BCV$-49$($kI,MW$,$"$j$^$9!#$b$7!"$"$i$f$k%U%!%$%k$KD%$k$3$H$,(B
+$B$G$-$k0lHLE*$J%3!<%I$rC5$7$F$$$k$J$i!"$3$l$OITJX$G$"$k$+$b$7$l$^$;$s!#(B
+$B$*$=$i$/$=$N>l9g$O!"$=$&$9$kBe$o$j$KJQ?t(B <code>LAST_MODIFIED</code>
+$B$r;HMQ$7$?$$$H9M$($k$G$7$g$&(B:</p>
+
+<pre>
+ &lt;!--#config timefmt="%D" --&gt;
+ This file last modified &lt;!--#echo var="LAST_MODIFIED" --&gt;
+</pre>
+
+<p><code>timefmt</code> $B=q<0$K$D$$$F$N$h$j>\:Y$K$D$$$F$O!"(B
+$B$*9%$_$N8!:w%5%$%H$K9T$-!"(B<code>ctime</code> $B$G8!:w$7$F$_$F$/$@$5$$!#J8K!$OF1$8$G$9!#(B</p>
+
+<hr>
+<h2><a name="includingastandardfooter">$BI8=`$N%U%C%?$rA^F~$9$k(B</a></h2>
+
+<p>$B$b$7?t%Z!<%8$rD6$($k%Z!<%8$r;}$D%5%$%H$r4IM}$7$F$$$k$J$i$P!"(B
+$BA4%Z!<%8$KBP$7$FJQ9`$r9T$J$&$3$H$,K\Ev$K6lDK$H$J$jF@$k$3$H$,J,$+$k$G$7$g$&!#(B
+$BA4$F$N%Z!<%8$KEO$C$F$"$k<o$NI8=`E*$J304Q$r0];}$7$h$&$H(B
+$B$7$F$$$k$J$i$PFC$K$=$&$G$7$g$&!#(B</p>
+
+<p>$B%X%C%@$d%U%C%?MQ$NA^F~MQ%U%!%$%k$r;HMQ$9$k$3$H$G!"$3$N$h$&$J(B
+$B99?7$K$+$+$kIiC4$r8:$i$9$3$H$,$G$-$^$9!#0l$D$N%U%C%?%U%!%$%k$r(B
+$B:n@.$7!"$=$l$r(B <code>include</code> SSI $B%3%^%s%I$G3F%Z!<%8$K(B
+$BF~$l$k$@$1$G:Q$_$^$9!#(B<code>include</code> $BMWAG$O!"(B<code>file</code> $BB0@-(B
+$B$^$?$O(B <code>virtual</code> $BB0@-$N$$$:$l$+$r;HMQ$7$F$I$N%U%!%$%k$rA^F~$9$k$+$r(B
+$B7h$a$k$3$H$,$G$-$^$9!#(B<code>file</code> $BB0@-$O!"(B<em>$B%+%l%s%H%G%#%l%/%H%j$+$i$N(B
+$BAjBP%Q%9$G<($5$l$?(B</em>$B%U%!%$%k%Q%9$G$9!#$=$l$O(B
+/ $B$G;O$^$k@dBP%U%!%$%k%Q%9$K$O$G$-$:!"$^$?!"$=$N%Q%9$N0lIt$K(B ../ $B$r(B
+$B4^$`$3$H$,$G$-$J$$$3$H$r0UL#$7$^$9!#(B<code>virtual</code> $BB0@-$O!"$*$=$i$/(B
+$B$h$jJXMx$@$H;W$$$^$9$,!"Ds6!$9$k%I%-%e%a%s%H$+$i$NAjBP(B URL $B$G;XDj$9$Y$-$G$9!#(B
+$B$=$l$O(B / $B$G;O$a$k$3$H$,$G$-$^$9$,!"Ds6!$9$k%U%!%$%k$HF1$8%5!<%P>e$K(B
+$BB8:_$7$J$/$F$O$J$j$^$;$s!#(B</p>
+
+<pre>
+ &lt;!--#include virtual="/footer.html" --&gt;
+</pre>
+
+<p>$B;d$O:G8e$NFs$D$rAH$_9g$o$;$F!"(B<code>LAST_MODIFIED</code> $B%G%#%l%/%F%#%V$r(B
+$B%U%C%?%U%!%$%k$NCf$KCV$/$3$H$,$h$/$"$j$^$9!#(B
+SSI $B%G%#%l%/%F%#%V$O!"A^F~MQ$N%U%!%$%k$K4^$^$;$?$j!"(B
+$BA^F~%U%!%$%k$N%M%9%H$r$7$?$j$9$k$3$H$,$G$-$^$9!#$9$J$o$A!"(B
+$BA^F~MQ$N%U%!%$%k$OB>$N%U%!%$%k$r:F5"E*$KA^F~$9$k$3$H$,$G$-$^$9!#(B</p>
+
+<hr>
+<h2><a name="whatelsecaniconfig">$BB>$K2?$,@_Dj$G$-$k$N$+(B?</a></h2>
+
+<p>$B;~9o=q<0$r(B <code>config</code> $B$G@_Dj$G$-$k$3$H$K2C$($F!"(B
+$B99$KFs$D(B <code>config</code> $B$G@_Dj$9$k$3$H$,$G$-$^$9!#(B</p>
+
+<p>$BDL>o!"(BSSI $B%G%#%l%/%F%#%V$G2?$+$,$&$^$/$$$+$J$$$H$-$O!"<!$N%a%C%;!<%8$,(B
+$B=PNO$5$l$^$9!#(B</p>
+
+<pre>
+ [an error occurred while processing this directive]
+</pre>
+
+<p>$B$3$N%a%C%;!<%8$rB>$N$b$N$K$7$?$$>l9g!"(B
+<code>config</code> $BMWAG$N(B <code>errmsg</code> $BB0@-$GJQ99$9$k$3$H$,(B
+$B$G$-$^$9(B:<p>
+
+<pre>
+ &lt;!--#config errmsg="[It appears that you don't know how to use SSI]" --&gt;
+</pre>
+
+<p>$B$*$=$i$/!"%(%s%I%f!<%6$O$3$N%a%C%;!<%8$r7h$7$F8+$k$3$H$O$"$j$^$;$s!#(B
+$B$J$<$J$i!"$=$N%5%$%H$,@8$-$?>uBV$K$J$kA0$K(B SSI $B%G%#%l%/%F%#%V$K4X$9$k(B
+$BA4$F$NLdBj$r2r7h$7$F$$$k$O$:$@$+$i$G$9!#(B($B$=$&$G$9$h$M(B?)</p>
+
+<p>$B$=$7$F!"(B<code>config</code> $B$K$*$$$F(B <code>sizefmt</code> $BB0@-$r;HMQ$9$k$3$H$G!"(B
+$BJV$5$l$k%U%!%$%k%5%$%:$N=q<0$r@_Dj$9$k$3$H$,$G$-$^$9!#(B
+$B%P%$%H?t$K$O(B <code>bytes</code> $B$r!"E,Ev$K(B Kb $B$d(B Mb $B$K(B
+$BC;=L$5$;$k$K$O(B <code>abbrev</code> $B$r;XDj$9$k$3$H$,$G$-$^$9!#(B</p>
+
+<hr>
+<h2><a name="executingcommands">$B%3%^%s%I$N<B9T(B</a></h2>
+
+<p>$B:#8e?t%v7n$NFb$K!">.$5$J(B CGI $B%W%m%0%i%`$H(B SSI $B$r;HMQ$9$k(B
+$B5-;v$r=P$7$?$$$H9M$($F$$$^$9!#$3$3$G$O$=$l$H$OJL$K!"(B
+<code>exec</code> $BMWAG$K$h$C$F9T$J$&$3$H$,$G$-$k$3$H$r<($7$^$9!#(B
+SSI $B$K%7%'%k(B ($B@53N$K$O(B <code>/bin/sh</code>$B!#(BWin32 $B$J$i$P(B DOS $B%7%'%k(B)
+$B$r;HMQ$7$F%3%^%s%I$r<B9T$5$;$k$3$H$,$G$-$^$9!#2<5-$NNc$G$O!"%G%#%l%/%H%j(B
+$B%j%9%H=PNO$r9T$J$$$^$9!#(B</p>
+
+<pre>
+ &lt;pre&gt;
+ &lt;!--#exec cmd="ls" --&gt;
+ &lt;/pre&gt;
+</pre>
+
+<p>Windows $B>e$G$O!"(B</p>
+
+<pre>
+ &lt;pre&gt;
+ &lt;!--#exec cmd="dir" --&gt;
+ &lt;/pre&gt;
+</pre>
+
+<p>Windows $B>e$G$O!"$3$N%G%#%l%/%F%#%V$K$h$C$F$$$/$D$+$N4qL/$J(B
+$B=q<0$K5$$E$/$G$7$g$&!#$J$<$J$i(B <code>dir</code> $B$N=PNO$,(B
+$BJ8;zNs(B ``&lt;<code>dir</code>&gt;'' $B$r4^$_!"%V%i%&%6$r:.Mp$5$;$k$+$i$G$9!#(B</P>
+
+<p>$B$3$N5!G=$OHs>o$K4m81$G$"$j!"$I$s$J%3!<%I$G$b(B <code>exec</code> $B%?%0$K(B
+$BKd$a9~$^$l$F$7$^$($P<B9T$9$k$3$H$KCm0U$7$F$/$@$5$$!#Nc$($P(B
+`` $B%2%9%H%V%C%/(B '' $B$N$h$&$K!"$b$7!"%f!<%6$,%Z!<%8$NFbMF$r(B
+$BJT=8$G$-$k>u67$K$"$k$J$i$P!"$3$N5!G=$r3N<B$KM^@)$7$F$/$@$5$$!#(B
+<code>Options</code> $B%G%#%l%/%F%#%V$N(B <code>IncludesNOEXEC</code> $B0z?t$r;XDj$9$k$3$H$G!"(B
+SSI $B$O5v2D$9$k$1$l$I(B <code>exec</code> $B5!G=$O5v2D$7$J$$$h$&$K$9$k$3$H$,$G$-$^$9!#(B</p>
+
+<hr>
+<h2><a name="advancedssitechniques">$B9bEY$J(B SSI $B%F%/%K%C%/(B</a></h2>
+
+<p>$B%3%s%F%s%D$r=PNO$9$k$3$H$K2C$(!"(BApache SSI $B$OJQ?t$r@_Dj$7!"$=$7$FHf3S(B
+$B$H>r7oJ,4t$K$=$NJQ?t$r;HMQ$G$-$k5!G=$rDs6!$7$F$$$^$9!#(B</p>
+
+<h3><a name="caveat">$B7Y9p(B</a></h3>
+
+<p>$B$3$N5-;v$G=R$Y$?BgItJ,$N5!G=$O!"(BApache 1.2 $B0J9_$r(B
+$B;HMQ$7$F$$$k>l9g$N$_MxMQ2DG=$G$9!#$b$A$m$s!"$b$7(B Apache 1.2 $B0J9_$r(B
+$B;HMQ$7$F$J$$>l9g!"D>$A$K%"%C%W%0%l!<%I$9$kI,MW$,$"$j$^$9!#(B
+$B$5$!!":#$=$l$r9T$J$$$J$5$$!#$=$l$^$GBT$C$F$$$^$9!#(B</p>
+
+<hr>
+<h2><a name="settingvariables">$BJQ?t$r@_Dj$9$k(B</a></h2>
+
+<p><code>set</code> $B%G%#%l%/%F%#%V$r;HMQ$7$F!"8e$G;HMQ$9$k$?$a$KJQ?t$r(B
+$B@_Dj$9$k$3$H$,$G$-$^$9!#$3$l$O8e$N@bL@$GI,MW$K$J$k$N$G!"$3$3$G(B
+$B$=$l$K$D$$$F=R$Y$F$$$^$9!#J8K!$O0J2<$N$H$*$j$G$9(B:</p>
+
+<pre>
+ &lt;!--#set var="name" value="Rich" --&gt;
+</pre>
+
+<p>$B$3$N$h$&$KC1=c$KJ8;z$I$*$j$K@_Dj$9$k$3$H$K2C$(!"(B
+$BNc$($P4D6-JQ?t$dA0$N5-;v$G=R$Y$?JQ?t(B ($BNc$($P(B <code
+>LAST_MODIFIED</code> $B$N$h$&$J(B) $B$r4^$`B>$N$"$i$f$kJQ?t$r(B
+$BCM$r@_Dj$9$k$N$K;HMQ$9$k$3$H$,(B
+$B$G$-$^$9!#JQ?tL>$NA0$K%I%k5-9f(B ($) $B$r;HMQ$9$k$3$H$G!"(B
+$B$=$l$,%j%F%i%kJ8;zNs$G$O$J$/$FJQ?t$G$"$k$3$H$r<($7$^$9!#(B</p>
+
+<pre>
+ &lt;!--#set var="modified" value="$LAST_MODIFIED" --&gt;
+</pre>
+
+<p>$B%I%k5-9f(B ($) $B$rJ8;z$H$7$FJQ?t$NCM$KF~$l$k$K$O!"%P%C%/%9%i%C%7%e$K$h$C$F(B
+$B%I%k5-9f$r%(%9%1!<%W$9$kI,MW$,$"$j$^$9!#(B</p>
+
+<pre>
+ &lt;!--#set var="cost" value="\$100" --&gt;
+</pre>
+
+<p>$B:G8e$K$J$j$^$9$,!"D9$$J8;zNs$NCf$KJQ?t$rCV$-$?$$>l9g$G!"(B
+$BJQ?tL>$,B>$NJ8;z$H$V$D$+$k2DG=@-$,$"$j!"$=$l$i$NJ8;z$K$D$$$F(B
+$B:.Mp$7$F$7$^$&>l9g!"$3$N:.Mp$r<h$j=|$/$?$a!"JQ?tL>$rCf3g8L$G(B
+$B0O$`$3$H$,$G$-$^$9(B ($B$3$l$K$D$$$F$NNI$$Nc$r<($9$N$OFq$7$$$N$G$9$,!"(B
+$B$*$=$i$/J,$+$C$F$$$?$@$1$k$G$7$g$&(B)$B!#(B</P>
+
+<pre>
+ &lt;!--#set var="date" value="${DATE_LOCAL}_${DATE_GMT}" --&gt;
+</pre>
+
+<hr>
+<h2><a name="conditionalexpressions">$B>r7o<0(B</a></h2>
+
+<p>$B$5$F!"JQ?t$r;}$C$F$$$F!"$=$l$i$NCM$r@_Dj$7$FHf3S$9$k$3$H$,$G$-$k$N$G$9$+$i!"(B
+$B>r7o$rI=$9$?$a$K$=$l$i$r;HMQ$9$k$3$H$,$G$-$^$9!#$3$l$K$h$j(B SSI $B$O(B
+$B$"$k<o$N>.$5$J%W%m%0%i%_%s%08@8l$K$J$C$F$$$^$9!#(B<code>mod_include</code> $B$O(B
+$B>r7o$rI=8=$9$k$?$a$K(B <code>if</code>, <code>elif</code>, <code>else</code>,
+<code>endif</code> $B9=B$$rDs6!$7$F$$$^$9!#$3$l$K$h$C$F!"0l$D$N<B:]$N%Z!<%8$+$i(B
+$BJ#?t$NO@M}%Z!<%8$r8z2LE*$K@8@.$9$k$3$H$,$G$-$^$9!#(B</p>
+
+<p>$B>r7o9=B$$O0J2<$N$H$*$j$G$9(B:</p>
+
+<pre>
+ &lt;!--#if expr="test_condition" --&gt;
+ &lt;!--#elif expr="test_condition" --&gt;
+ &lt;!--#else --&gt;
+ &lt;!--#endif --&gt;
+</pre>
+
+<p><em>test_condition</em> $B$O$"$i$f$k<oN`$NO@M}E*Hf3S$r$9$k$3$H$,$G$-$^$9!#(B
+$BCM$rHf3S$7$?$j!"$=$NCM$,(B ``$B??(B'' $B$+$I$&$+$rI>2A$7$^$9(B ($B6u$G$J$$$J$i(B
+$BM?$($i$l$?J8;zNs$O??$G$9(B)$B!#MxMQ2DG=$JHf3S1i;;;R$NA4$F$N%j%9%H$K$D$$$F$O!"(B
+<code>mod_include</code> $B%I%-%e%a%s%F!<%7%g%s$r;2>H$7$F$/$@$5$$!#(B
+$B$3$3$G$O!"$3$N9=B$$r$I$&;HMQ$9$k$+$NNc$r$$$/$D$+<($7$^$9!#(B</p>
+
+<p>$B@_Dj%U%!%$%k$G<!$N9T$r5-=R$7$^$9(B:</P>
+
+<pre>
+ BrowserMatchNoCase macintosh Mac
+ BrowserMatchNoCase MSIE InternetExplorer
+</pre>
+
+<p>$B$3$l$O%/%i%$%"%s%H$,(B Macintosh $B>e$G%$%s%?!<%M%C%H%(%/%9%W%m!<%i$,(B
+$BF0$$$F$$$k>l9g!"4D6-JQ?t(B ``Mac'' $B$H(B ``InternetExplorer'' $B$r??$H@_Dj$7$^$9!#(B</P>
+
+<p>$B<!$K!"(BSSI $B$,2DG=$K$J$C$?%I%-%e%a%s%H$G0J2<$r9T$J$$$^$9(B:</p>
+
+<pre>
+ &lt;!--#if expr="${Mac} &amp;&amp; ${InternetExplorer}" --&gt;
+ Apologetic text goes here
+ &lt;!--#else --&gt;
+ Cool JavaScript code goes here
+ &lt;!--#endif --&gt;
+</pre>
+
+<p>Mac $B>e$N(B IE $B$KBP$7$F2?$+;W$&$H$3$m$,$"$k$o$1$G$"$j$^$;$s!#(B
+$BB>$G$O<B9T$G$-$F$$$k$$$/$D$+$N(B JavaScript $B$r(B Mac $B>e$N(B IE $B$G(B
+$B<B9T$5$;$k$N$K!"@h=5?t;~4V6lO+$7$?$H$$$&$@$1$N$3$H$G$9!#(B
+$B>e$NNc$O$=$N;CDjE*$JBP=hJ}K!$G$9!#(B</p>
+
+<p>$BB>$N$I$s$JJQ?t(B ($B$"$J$?$,Dj5A$9$k$b$N!"$^$?$OIaDL$N4D6-JQ?t$N$$$:$l$+(B) $B$b!"(B
+$B>r7oJ8$K;HMQ$9$k$3$H$,$G$-$^$9!#(BApache $B$O(B <code>SetEnvIf</code>
+$B%G%#%l%/%F%#%V$dB>$N4XO"%G%#%l%/%F%#%V;HMQ$7$F4D6-JQ?t$r@_Dj$9$k$3$H$,(B
+$B$G$-$^$9!#$3$N5!G=$K$h$j!"(BCGI $B$KMj$k$3$H$J$/$+$J$jJ#;($JF0E*$J$3$H$r$5$;$k(B
+$B$3$H$,$G$-$^$9!#(B</p>
+
+<hr>
+<h2><a name="conclusion">$B=*$o$j$K(B</a></h2>
+
+<p>SSI $B$O3N$+$K(B CGI $B$dF0E*$J%&%'%V%Z!<%8$r@8@.$9$kB>$N5;=Q$KBe$o$k$b$N(B
+$B$G$O$"$j$^$;$s!#$7$+$7!"(B
+$B$?$/$5$s$NM>J,$J:n6H$r$;$:$K!">/NL$NF0E*$J%3%s%F%s%D$r2C$($k$K$O(B
+$B$9$0$l$?J}K!$G$9!#(B</p>
+
+</body>
+</html>
diff --git a/docs/manual/images/apache_header.gif b/docs/manual/images/apache_header.gif
new file mode 100644
index 0000000000..260e421bf4
--- /dev/null
+++ b/docs/manual/images/apache_header.gif
Binary files differ
diff --git a/docs/manual/images/pixel.gif b/docs/manual/images/pixel.gif
new file mode 100644
index 0000000000..c0801475d2
--- /dev/null
+++ b/docs/manual/images/pixel.gif
Binary files differ
diff --git a/docs/manual/index.html.ja.jis b/docs/manual/index.html.ja.jis
new file mode 100644
index 0000000000..1d2ea9f808
--- /dev/null
+++ b/docs/manual/index.html.ja.jis
@@ -0,0 +1,179 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head><title>Apache HTTP $B%5!<%P(B $B%P!<%8%g%s(B 2.0 $B%I%-%e%a%s%H(B</title></head>
+<!-- English revision: 1.21 -->
+<body
+ bgcolor="#FFFFFF"
+ text="#000000"
+ link="#0000FF"
+ vlink="#000080"
+ alink="#FF0000"
+>
+<div align="center">
+<table cellspacing="0" cellpadding="0" border="0" width="600">
+<tr>
+<td align="center">
+ <img src="images/apache_header.gif" width=600 height=62 border=0 alt="[Apache $B%I%-%e%a%s%H(B]"></td>
+</tr>
+<tr>
+<td align="center" bgcolor="#4f4f4f">
+ <table cellspacing="1" cellpadding="4" border="0" width="100%">
+ <tr>
+ <td align="center" bgcolor="#bebebe">
+ <a href="misc/FAQ.html"><strong>FAQ</strong></a>
+ </td>
+ <td align="center" bgcolor="#bebebe">
+ <a href="mod/directives.html"><strong>$B%G%#%l%/%F%#%V(B</strong></a>
+ </td>
+ <td align="center" bgcolor="#bebebe">
+ <a href="mod/"><strong>$B%b%8%e!<%k(B</strong></a>
+ </td>
+ <td align="center" bgcolor="#bebebe">
+ <a href="http://www.apache.org/search.html"><strong>$B8!:w(B</strong></a>
+ </td>
+ </tr></table>
+</td>
+</tr>
+<tr>
+<td>&nbsp;</td>
+</tr><tr>
+<td align="center" height="30">
+ <h3>Apache HTTP Server Version 2.0</h3>
+</td>
+</tr>
+</table>
+</div>
+
+<div align="center">
+
+<table cellspacing="0" cellpadding="0" border="0" width="600">
+<tr><td align="center">
+ <form method="post" action="http://search.apache.org/">
+ <input type="hidden" name="what" value="httpd.apache.org">
+ <input type="hidden" name="results" value="20">
+ <input type="hidden" name="version" value="2">
+ <input type="text" name="keyword" size="20">
+ <input type="submit" value="Search">
+ </form>
+</td></tr></table>
+
+<!--
+<table cellspacing="0" cellpadding="0" border="0" width="600">
+<tr>
+<td align="center"><hr size="1" noshade width="100%">
+</td>
+</tr>
+</table>
+-->
+
+<table cellspacing="0" cellpadding="0" border="0" width="600">
+<tr>
+<td align="center" valign="top">
+<table border="0" cellpadding="4" cellspacing="0" bgcolor="#ffffff" width="280">
+<tr><td align="center" bgcolor="#e9e9e9">
+ <strong>$B%j%j!<%9%N!<%H(B</strong>
+</td></tr><tr><td>
+<a href="new_features_2_0.html">Apache 2.0 $B$N?75!G=(B</a>
+</td></tr><tr><td>
+<a href="upgrading.html">Apache 2.0 $B$X$N%"%C%W%0%l!<%I(B</a>
+</td></tr><tr><td>
+<a href="LICENSE">Apache $B$N%i%$%;%s%9(B</a>
+</td></tr>
+</table>
+
+<p>
+<table border="0" cellpadding="4" cellspacing="0" bgcolor="#ffffff" width="280">
+<tr><td align="center" bgcolor="#e9e9e9">
+<strong>$B%j%U%!%l%s%9%^%K%e%"%k(B</strong>
+</td></tr><tr><td>
+<a href="install.html">$B%3%s%Q%$%k$H%$%s%9%H!<%k(B</a>
+</td></tr><tr><td>
+<a href="invoking.html">$B5/F0(B</a>
+</td></tr><tr><td>
+<a href="stopping.html">$B=*N;$^$?$O:F5/F0(B</a>
+</td></tr><tr><td>
+<a href="mod/directives.html">$B<B9T;~$N@_DjJ}K!(B</a>
+</td></tr><tr><td>
+$B%b%8%e!<%k(B: <a href="mod/index-bytype.html">$B<oN`JL(B</a> $B$H(B
+ <a href="mod/">$B%"%k%U%!%Y%C%H=g(B</a>
+</td></tr><tr><td>
+<a href="mpm.html">Multi-Processing Modules (MPM)</a>
+</td></tr><tr><td>
+<a href="programs/">$B%5!<%P$H%5%]!<%H%W%m%0%i%`(B</a>
+</td></tr><tr><td>
+<a href="dso.html">Dynamic Shared Object (DSO) $B$N%5%]!<%H(B</a>
+</td></tr></table>
+
+<p>
+<table border="0" cellpadding="4" cellspacing="0" bgcolor="#ffffff" width="280">
+<tr><td align="center" bgcolor="#e9e9e9">
+<strong>$B%W%i%C%H%U%)!<%`8GM-$N>pJs(B</strong>
+</td></tr><tr><td>
+<a href="platform/windows.html">Microsoft Windows</a>
+</td></tr><tr><td>
+<a href="platform/">$BB>$N%W%i%C%H%U%)!<%`FCM-$N>pJs(B</a>
+</td></tr></table>
+
+</td>
+<td align="center" valign="top" bgcolor="#cccccc">
+<table border="0" cellpadding="0" cellspacing="0" bgcolor="#cccccc"><tr><td
+ align="center"><img src="images/pixel.gif" width="1" height="1"
+ border="0" alt="."></td></tr></table>
+</td>
+<td align="center" valign="top">
+<table border="0" cellpadding="4" cellspacing="0" bgcolor="#ffffff" width="280">
+<tr><td align="center" bgcolor="#e9e9e9">
+<strong>Apache HTTP $B%5!<%P$r;H$&(B</strong>
+</td></tr><tr><td>
+<a href="configuring.html">$B@_Dj%U%!%$%k(B</a>
+</td></tr><tr><td>
+<a href="server-wide.html">$B%5!<%PA4BN$N@_Dj(B</a>
+</td></tr><tr><td>
+<a href="urlmapping.html">URL $B$r%U%!%$%k%7%9%F%`$K%^%C%W$9$k(B<a>
+</td></tr><tr><td>
+<a href="vhosts/">$B%P!<%A%c%k%[%9%H(B</a>
+</td></tr><tr><td>
+<a href="handler.html">$B%O%s%I%i(B</a>
+</td></tr><tr><td>
+<a href="filter.html">$B%U%#%k%?(B</a>
+</td></tr><tr><td>
+<a href="content-negotiation.html">$B%3%s%F%s%H%M%4%7%(!<%7%g%s(B</a>
+</td></tr><tr><td>
+<a href="env.html">$B4D6-JQ?t(B</a>
+</td></tr><tr><td>
+<a href="suexec.html">CGI $B$G(B SetUserID $B<B9T$r;H$&(B</a>
+</td></tr><tr><td>
+<a href="misc/perf-tuning.html">$B0lHLE*$J@-G=$K4X$9$k%R%s%H(B</a>
+</td></tr><tr><td>
+<a href="misc/security_tips.html">$B%;%-%e%j%F%#>pJs(B</a>
+</td></tr><tr><td>
+<a href="misc/rewriteguide.html">URL Rewriting $B$N%,%$%I(B</a>
+</td></tr></table>
+
+<p>
+<table border="0" cellpadding="4" cellspacing="0" bgcolor="#ffffff" width="280">
+<tr><td align="center" bgcolor="#e9e9e9">
+<strong>$B$=$NB>(B</strong>
+</td></tr><tr><td>
+<a href="misc/FAQ.html">FAQ</a>
+</td></tr><tr><td>
+<a href="misc/tutorials.html">$B%A%e!<%H%j%"%k(B</a>
+</td></tr><tr><td>
+<a href="developer/">$B3+H/<T$N$?$a$N%I%-%e%a%s%H(B</a>
+</td></tr><tr><td>
+<a href="misc/">$B$=$NB>(B</a>
+</td></tr></table>
+</td>
+</tr>
+</table>
+
+</div>
+
+<p align="center"><a
+href="http://httpd.apache.org/docs-project/">Apache HTTP Server
+Documentation Project</a> $B$K$h$j%a%s%F%J%s%9$5$l$F$$$^$9!#(B</p>
+
+<!--#include virtual="footer.html" -->
+</body>
+</html>
+
diff --git a/docs/manual/mod/mod_charset_lite.html b/docs/manual/mod/mod_charset_lite.html
new file mode 100644
index 0000000000..8ae3e490b9
--- /dev/null
+++ b/docs/manual/mod/mod_charset_lite.html
@@ -0,0 +1,227 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD>
+ <TITLE>Apache module mod_charset_lite</TITLE>
+ </HEAD>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+ <BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+ >
+<!--#include virtual="header.html" -->
+ <H1 ALIGN="CENTER">Module mod_charset_lite</H1>
+
+ <P>
+ This module is contained in the <CODE>mod_charset_lite.c</CODE> file, with
+ Apache 2.0 and later. It provides the ability to specify character set
+ translation, or recoding, by directory or location or virtual server. It
+ is not compiled into the server by default. <CODE>mod_charset_lite</CODE>
+ requires that Apache is compiled with APACHE_XLATE defined.
+ </P>
+
+ <P>
+ This module provides a small subset of configuration mechanisms
+ implemented by Russian Apache and its associated <CODE>mod_charset</CODE>.
+ </P>
+
+ <H2>Summary</H2>
+ <P>
+ This is an <STRONG>experimental</STRONG> module and should be used with
+ care. Experiment with your <CODE>mod_charset_lite</CODE> configuration to
+ ensure that it performs the desired function.
+ </P>
+ <P>
+ <CODE>mod_charset_lite</CODE> allows the administrator to specify the
+ source character set of objects as well as the character set they should
+ be translated into before sending to the client.
+ <CODE>mod_charset_lite</CODE> does not translate the data itself but
+ instead tells Apache what translation to perform.
+ <CODE>mod_charset_lite</CODE> is applicable to EBCDIC and ASCII
+ host environments. In an EBCDIC environment, Apache normally translates
+ text content from the code page of the Apache process locale to
+ ISO-8859-1. <CODE>mod_charset_lite</CODE> can be used to specify that
+ a different translation is to be performed. In an ASCII environment,
+ Apache normally performs no translation, so <CODE>mod_charset_lite</CODE>
+ is needed in order for any translation to take place.
+ </P>
+
+ <H2>Directives</H2>
+ <UL>
+ <LI><A HREF="#charsetsourceenc">CharsetSourceEnc</A>
+ <LI><A HREF="#charsetdefault">CharsetDefault</A>
+ <LI><A HREF="#charsetdebug">CharsetDebug</A>
+ </LI>
+ </UL>
+
+ <HR>
+
+ <H2><A NAME="charsetsourceenc">CharsetSourceEnc</A></H2>
+ <P>
+ <A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+ ><STRONG>Syntax:</STRONG></A> CharsetSourceEnc <EM>charset</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+ ><STRONG>Default:</STRONG></A> <EM>None</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+ ><STRONG>Context:</STRONG></A> directory, virtual host
+ <BR>
+ <A
+ HREF="directive-dict.html#Override"
+ REL="Help"
+ ><STRONG>Override:</STRONG></A> <EM>FileInfo</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+ ><STRONG>Status:</STRONG></A> Experimental
+ <BR>
+ <A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+ ><STRONG>Module:</STRONG></A> mod_charset_lite
+ <BR>
+ <A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+ ><STRONG>Compatibility:</STRONG></A> Only available in Apache 2.0 or later
+
+ <P>
+ The <CODE>CharsetSourceEnc</CODE> directive specifies the source charset
+ of files in the associated container.
+ </P>
+
+ <P>
+ The value of the <EM>charset</EM> argument must be accepted as a valid
+ character set name by the character set support in APR. Generally, this
+ means that it must be supported by iconv.
+ </P>
+
+ Example:
+
+ <PRE>
+ &lt;Directory "/export/home/trawick/apacheinst/htdocs/convert"&gt;
+ CharsetSourceEnc UTF-16BE
+ CharsetDefault ISO8859-1
+ &lt;/Directory&gt;
+ </PRE>
+
+ The character set names in this example work with the iconv
+ translation support in Solaris 8.
+ <P>
+
+ <H2><A NAME="charsetdefault">CharsetDefault</A></H2>
+ <P>
+ <A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+ ><STRONG>Syntax:</STRONG></A> CharsetDefault <EM>charset</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+ ><STRONG>Default:</STRONG></A> <EM>None</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+ ><STRONG>Context:</STRONG></A> directory, virtual host
+ <BR>
+ <A
+ HREF="directive-dict.html#Override"
+ REL="Help"
+ ><STRONG>Override:</STRONG></A> <EM>FileInfo</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+ ><STRONG>Status:</STRONG></A> Experimental
+ <BR>
+ <A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+ ><STRONG>Module:</STRONG></A> mod_charset_lite
+ <BR>
+ <A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+ ><STRONG>Compatibility:</STRONG></A> Only available in Apache 2.0 or later
+
+ <P>
+ The <CODE>CharsetDefault</CODE> directive specifies the charset that
+ content in the associated container should be translated to.
+ </P>
+
+ <P>
+ The value of the <EM>charset</EM> argument must be accepted as a valid
+ character set name by the character set support in APR. Generally, this
+ means that it must be supported by iconv.
+ </P>
+
+ Example:
+
+ <PRE>
+ &lt;Directory "/export/home/trawick/apacheinst/htdocs/convert"&gt;
+ CharsetSourceEnc UTF-16BE
+ CharsetDefault ISO8859-1
+ &lt;/Directory&gt;
+ </PRE>
+
+ <P>
+
+ <H2><A NAME="charsetdebug">CharsetDebug</A></H2>
+ <P>
+ <A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+ ><STRONG>Syntax:</STRONG></A> CharsetDebug <EM>on/off</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+ ><STRONG>Default:</STRONG></A> <EM>off</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+ ><STRONG>Context:</STRONG></A> directory, virtual host
+ <BR>
+ <A
+ HREF="directive-dict.html#Override"
+ REL="Help"
+ ><STRONG>Override:</STRONG></A> <EM>FileInfo</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+ ><STRONG>Status:</STRONG></A> Experimental
+ <BR>
+ <A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+ ><STRONG>Module:</STRONG></A> mod_charset_lite
+ <BR>
+ <A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+ ><STRONG>Compatibility:</STRONG></A> Only available in Apache 2.0 or later
+
+ <P>
+ The <CODE>CharsetDebug</CODE> directive specifies whether or not
+ verbose logging should be performed by <CODE>mod_charset_lite</CODE>.
+ Such logging is written to the Apache error log with level
+ <EM>debug</EM>.
+ </P>
+
+<!--#include virtual="footer.html" -->
+ </BODY>
+</HTML>
diff --git a/docs/manual/mod/mod_dav.html b/docs/manual/mod/mod_dav.html
new file mode 100644
index 0000000000..c341095599
--- /dev/null
+++ b/docs/manual/mod/mod_dav.html
@@ -0,0 +1,272 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Apache module mod_dav</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<H1 ALIGN="CENTER">Module mod_dav</H1>
+
+This module provides class 1 and class 2
+<A HREF="http://www.webdav.org">WebDAV</A> ('Web-based
+Distributed Authoring and Versioning') functionality for Apache.
+This extension to the HTTP protocol allows creating, moving,
+copying, and deleting resources and collections on a remote web
+server.
+
+<H2>Directives</H2>
+<UL>
+<LI><A HREF="#DAV">Dav</A>
+<LI><A HREF="#DAVLockDB">DavLockDB</A>
+<LI><A HREF="#DAVMinTimeout">DavMinTimeout</A>
+<LI><A HREF="#DAVDepthInfinity">DavDepthInfinity</A>
+<LI><A HREF="#LimitXMLRequestBody">LimitXMLRequestBody</A>
+</UL>
+<HR>
+<P>
+To enable mod_dav, add the following to a container in your <CODE>httpd.conf</CODE> file:<P>
+
+<CODE>Dav On</CODE><P>
+
+Also, specify a valid filename for the DAV lock database by adding the following to the global section in your <CODE>httpd.conf</CODE> file:<P>
+
+<CODE>DavLockDB /tmp/DavLock&nbsp;&nbsp;&nbsp;&nbsp;</CODE><EM>(Any web-server writeable filename, without an extension)</EM><P>
+<HR>
+
+<H2><A NAME="DAV">Dav</A></H2>
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> Dav &lt;on | off&gt;<BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A>
+ <CODE>Dav off</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> directory<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> extension<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mod_dav<BR>
+<A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+><STRONG>Compatibility:</STRONG></A> Apache 1.3.4 and above<P>
+
+Use the <CODE>Dav</CODE> directive to enable the WebDAV HTTP methods
+for the given container.
+You may wish to add a
+<A
+ HREF="core.html#limit"
+>&lt;Limit&gt;</A>
+clause inside the
+<A
+ HREF="core.html#location"
+>location</A>
+directive to limit access to DAV-enabled locations.<P>
+
+<TABLE WIDTH="70%" BORDER=0 BGCOLOR="#E0E0F0" CELLSPACING=0 CELLPADDING=10>
+<TR><TD>
+<STRONG>Example</STRONG>:<BR><BR>
+<CODE>DavLockDB /tmp/DavLock<BR>
+<BR>
+&lt;Location /foo&gt;<BR>
+Dav On<BR>
+<BR>
+AuthType Basic<BR>
+AuthName DAV<BR>
+AuthUserFile user.passwd<BR>
+<BR>
+&nbsp;&nbsp;&lt;LimitExcept GET HEAD OPTIONS&gt;<BR>
+&nbsp;&nbsp;require user admin<BR>
+&nbsp;&nbsp;&lt;/LimitExcept&gt;<BR>
+&lt;/Location&gt;<BR>
+</CODE>
+</TD></TR>
+</TABLE>
+
+<BR>
+<HR>
+
+<H2><A NAME="DavLockDB">DavLockDB</A></H2>
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> DavLockDB &lt;Full path to lock database&gt;<BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A>
+ <EM>None</EM><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config, virtual host<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> extension<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mod_dav<BR>
+<A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+><STRONG>Compatibility:</STRONG></A> Apache 1.3.4 and above<P>
+
+Use the <CODE>DavLockDB</CODE> directive to specify the full path to the
+lock database, excluding an extension. The default (file system)
+implementation of mod_dav uses a SDBM database to track user locks.
+The utility <CODE>modules/dav/util/lockview</CODE> can be
+used from the server to display all locks in a lock database.<P>
+
+<TABLE WIDTH="70%" BORDER=0 BGCOLOR="#E0E0F0" CELLSPACING=0 CELLPADDING=10>
+<TR><TD>
+<STRONG>Example</STRONG>:<BR><BR>
+<CODE>DavLockDB /tmp/DavLock<BR>
+<BR>
+</CODE>
+</TD></TR>
+</TABLE>
+
+<BR>
+<HR>
+
+<H2><A NAME="DavMinTimeout">DavMinTimeout</A></H2>
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> DavMinTimeout &lt;seconds&gt;<BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A>
+ <CODE>DavMinTimeout 0</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> directory<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> extension<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mod_dav<BR>
+<A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+><STRONG>Compatibility:</STRONG></A> Apache 1.3.4 and above<P>
+
+When a client requests a DAV resource lock, it can also specify a time
+when the lock will be automatically removed by the server. This value
+is only a request, and the server can ignore it or inform the client
+of an arbitrary value.<P>
+
+Use the <CODE>DavMinTimeout</CODE> directive to specify, in seconds,
+the minimum lock timeout to return to a client. Microsoft Web Folders
+defaults to a timeout of 120 seconds; the <CODE>DavMinTimeout</CODE>
+can override this to a higher value (like 600 seconds) to reduce the chance
+of the client losing the lock due to network latency.<P>
+
+<TABLE WIDTH="70%" BORDER=0 BGCOLOR="#E0E0F0" CELLSPACING=0 CELLPADDING=10>
+<TR><TD>
+<STRONG>Example</STRONG>:<BR><BR>
+<CODE>&lt;Location /MSWord&gt;<BR>
+DavMinTimeout 600<BR>
+&lt;/Location&gt;<BR>
+<BR>
+</CODE>
+</TD></TR>
+</TABLE>
+
+<BR>
+<HR>
+
+<H2><A NAME="DavDepthInfinity">DavDepthInfinity</A></H2>
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> DavDepthInfinity &lt;on | off&gt;<BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A>
+ <CODE>DavDepthInfinity off</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> directory<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> extension<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mod_dav<BR>
+<A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+><STRONG>Compatibility:</STRONG></A> Apache 1.3.4 and above<P>
+
+Use the <CODE>DavDepthInfinity</CODE> directive to allow the processing
+of PROPFIND requests containing the header 'Depth: Infinity'.
+Because this type of request could constitute a denial-of-service attack,
+by default it is not allowed.
+<P>
+
+<HR>
+
+<H2><A NAME="LimitXMLRequestBody">LimitXMLRequestBody</A></H2>
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> LimitXMLRequestBody &lt;size in bytes&gt;<BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A>
+ <CODE>LimitXMLRequestBody 1000000</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> directory<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> extension<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mod_dav<BR>
+<A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+><STRONG>Compatibility:</STRONG></A> Apache 1.3.4 and above<P>
+
+Use the <CODE>LimitXMLRequestBody</CODE> directive to limit the
+size of an XML request which mod_dav will attempt to parse. Specify
+a size greater than zero.
+<P>
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/mod/mod_ext_filter.html b/docs/manual/mod/mod_ext_filter.html
new file mode 100644
index 0000000000..0886782352
--- /dev/null
+++ b/docs/manual/mod/mod_ext_filter.html
@@ -0,0 +1,286 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD>
+ <TITLE>Apache module mod_ext_filter</TITLE>
+ </HEAD>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+ <BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+ >
+<!--#include virtual="header.html" -->
+ <H1 ALIGN="CENTER">Module mod_ext_filter</H1>
+
+ <P>
+ This module is contained in the <CODE>mod_ext_filter.c</CODE> file, with
+ Apache 2.0 and later. It provides the ability to pass the response body
+ through an external program before delivering to the client.
+ <CODE>mod_ext_filter</CODE> is not compiled into the server by default.
+ </P>
+
+ <H2>Summary</H2>
+ <P>
+ This is an <STRONG>experimental</STRONG> module and should be used with
+ care. Test your <CODE>mod_ext_filter</CODE> configuration carefully to
+ ensure that it performs the desired function. You may wish to review
+ XXX for background on the Apache filtering model.
+ </P>
+
+ <P>
+ <CODE>mod_ext_filter</CODE> presents a simple and familiar programming
+ model for filters. With this module, a program which reads from stdin and
+ writes to stdout (i.e., a Unix-style filter command) can be a filter for
+ Apache. This filtering mechanism is much slower than using a filter which
+ is specially written for the Apache API and runs inside of the Apache
+ server process, but it does have the following benefits:
+ </P>
+
+ <UL>
+ <LI>the programming model is much simpler
+ <LI>any programming/scripting language can be used, provided that
+ it allows the program to read from standard input and write to standard
+ output
+ <LI>existing programs can be used unmodified as Apache filters
+ </UL>
+
+ <P>
+ Even when the performance characteristics are not suitable for production
+ use, <CODE>mod_ext_filter</CODE> can be used as a prototype environment
+ for filters.
+ </P>
+
+ <H2>Directives</H2>
+ <UL>
+ <LI><A HREF="#extfilterdefine">ExtFilterDefine</A>
+ <LI><A HREF="#extfilteroptions">ExtFilterOptions</A>
+ </LI>
+ </UL>
+
+ <HR>
+
+ <H2><A NAME="extfilterdefine">ExtFilterDefine</A></H2>
+ <P>
+ <A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+ ><STRONG>Syntax:</STRONG></A> ExtFilterDefine <EM>filtername</EM> <EM>parameters</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+ ><STRONG>Default:</STRONG></A> <EM>None</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+ ><STRONG>Context:</STRONG></A> server
+ <BR>
+ <A
+ HREF="directive-dict.html#Override"
+ REL="Help"
+ ><STRONG>Override:</STRONG></A> none
+ <BR>
+ <A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+ ><STRONG>Status:</STRONG></A> Experimental
+ <BR>
+ <A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+ ><STRONG>Module:</STRONG></A> mod_ext_filter
+ <BR>
+ <A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+ ><STRONG>Compatibility:</STRONG></A> Only available in Apache 2.0 or later
+
+ <P>
+ The <CODE>ExtFilterDefine</CODE> directive defines the characteristics of
+ an external filter, including the program to run and its arguments.
+ </P>
+
+ <P>
+ <EM>filtername</EM> specifies the name of the filter being defined. This name
+ can then be used in AddOutputFilter directives. It must be unique among all
+ registered filters. <EM>At the present time, no error is reported by the
+ register-filter API, so a problem with duplicate names isn't reported to the
+ user.</EM>
+ </P>
+
+ <P>
+ Subsequent parameters can appear in any order and define the external command
+ to run and certain other characteristics. The only required parameter is
+ <EM>cmd=</EM>. These parameters are:
+ </P>
+
+ <DL>
+ <DT>cmd=<EM>cmdline</EM>
+ <DD>
+ The <SAMP>cmd=</SAMP> keyword allows you to specify the external command
+ to run. If there are arguments after the program name, the command line
+ should be surrounded in quotation marks.
+ <DT>mode=<EM>mode</EM>
+ <DD>
+ <EM>mode</EM> should be <EM>output</EM> for now (the default). In the
+ future, <EM>mode=input</EM> will be used to specify a filter for request
+ bodies.
+ <DT>intype=<EM>imt</EM>
+ <DD>
+ This parameter specifies the internet media
+ type (i.e., MIME type) of documents which should be filtered. By default,
+ all documents are filtered. If <CODE>intype=</CODE> is specified,
+ the filter will be disabled for documents of other types.
+ <DT>outtype=<EM>imt</EM>
+ <DD>
+ This parameter specifies the internet media
+ type (i.e., MIME type) of filtered documents. It is useful when the filter
+ changes the internet media type as part of the filtering operation. By
+ default, the internet media type is unchanged.
+ <DT>PreservesContentLength
+ <DD>
+ The <SAMP>PreservesContentLength</SAMP> keyword specifies that the filter
+ preserves the content length. This is not the default, as most filters
+ change the content length. In the event that the filter doesn't modify the
+ length, this keyword should be specified.
+ </DL>
+
+ <H2><A NAME="extfilteroptions">ExtFilterOptions</A></H2>
+ <P>
+ <A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+ ><STRONG>Syntax:</STRONG></A> ExtFilterOptions <EM>option option ...</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+ ><STRONG>Default:</STRONG></A> <EM>DebugLevel=0</EM> <EM>NoLogStderr</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+ ><STRONG>Context:</STRONG></A> directory
+ <BR>
+ <A
+ HREF="directive-dict.html#Override"
+ REL="Help"
+ ><STRONG>Override:</STRONG></A> none
+ <BR>
+ <A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+ ><STRONG>Status:</STRONG></A> Experimental
+ <BR>
+ <A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+ ><STRONG>Module:</STRONG></A> mod_ext_filter
+ <BR>
+ <A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+ ><STRONG>Compatibility:</STRONG></A> Only available in Apache 2.0 or later
+
+ <P>
+ The <CODE>ExtFilterOptions</CODE> directive specifies special processing
+ options for <CODE>mod_ext_filter</CODE>. <EM>Option</EM> can be one of
+ <DL>
+ <DT>DebugLevel=<EM>n</EM>
+ <DD>
+ The <SAMP>DebugLevel</SAMP> keyword allows you to specify the level of
+ debug messages generated by <CODE>mod_ext_filter</CODE>. By default, no
+ debug messages are generated. This is equivalent to
+ <SAMP>DebugLevel=0</SAMP>. With higher numbers, more debug messages are
+ generated, and server performance will be degraded. The actual meanings
+ of the numeric values are described with the definitions of the DBGLVL_
+ constants near the beginning of <CODE>mod_ext_filter.c</CODE>.
+ <P>
+ Note: The core directive LogLevel should be used to cause debug messages
+ to be stored in the Apache error log.
+ <DT>LogStderr | NoLogStderr
+ <DD>
+ The <SAMP>LogStderr</SAMP> keyword specifies that messages written to
+ standard error by the external filter program will be saved in the Apache
+ error log. <SAMP>NoLogStderr</SAMP> disables this feature.
+ </DL>
+ </P>
+
+ Example:
+
+ <PRE>
+ ExtFilterOptions LogStderr DebugLevel=0
+ </PRE>
+
+ Messages written to the filter's standard error will be stored in the Apache
+ error log. No debug messages will be generated by
+ <CODE>mod_ext_filter</CODE>.
+
+ <P>
+
+ <H2>Examples</H2>
+
+ <H3>Generating HTML from some other type of response</H3>
+
+ <PRE>
+ # mod_ext_filter directive to define a filter to HTML-ize text/c files
+ # using the external program /usr/bin/enscript, with the type of the
+ # result set to text/html
+ ExtFilterDefine c-to-html mode=output intype=text/c outtype=text/html \
+ cmd="/usr/bin/enscript --color -W html -Ec -o - -"
+
+ &lt;Directory "/export/home/trawick/apacheinst/htdocs/c"&gt;
+
+ # core directive to cause the new filter to be run on output
+ AddOutputFilter c-to-heml
+
+ # mod_mime directive to set the type of .c files to text/c
+ AddType text/c .c
+
+ # mod_ext_filter directive to set the debug level just high
+ # enough to see a log message per request showing the configuration
+ # in force
+ ExtFilterOptions DebugLevel=1
+
+ &lt;/Directory&gt;
+ </PRE>
+
+ <H3>Implementing a content encoding filter</H3>
+
+ <PRE>
+ # mod_ext_filter directive to define the external filter
+ ExtFilterDefine gzip mode=output cmd=/bin/gzip
+
+ &lt;Location /gzipped&gt;
+
+ # core directive to cause the gzip filter to be run on output
+ AddOutputFilter gzip
+
+ # mod_header directive to add "Content-Encoding: gzip" header field
+ Header set Content-Encoding gzip
+
+ &lt;/Location&gt;
+ </PRE>
+
+ <H3>Slowing down the server</H3>
+ <PRE>
+ # mod_ext_filter directive to define a filter which runs everything
+ # through cat; cat doesn't modify anything; it just introduces extra
+ # pathlength and consumes more resources
+ ExtFilterDefine slowdown mode=output cmd=/bin/cat preservescontentlength
+
+ &lt;Location /&gt;
+
+ # core directive to cause the slowdown filter to be run several times on
+ # output
+ AddOutputFilter slowdown slowdown slowdown
+
+ &lt;/Location&gt;
+ </PRE>
+
+<!--#include virtual="footer.html" -->
+ </BODY>
+</HTML>
diff --git a/docs/manual/mod/mod_file_cache.html b/docs/manual/mod/mod_file_cache.html
new file mode 100644
index 0000000000..6abbdcb9e3
--- /dev/null
+++ b/docs/manual/mod/mod_file_cache.html
@@ -0,0 +1,267 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD>
+ <TITLE>Apache module mod_file_cache</TITLE>
+ </HEAD>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+ <BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+ >
+<!--#include virtual="header.html" -->
+ <H1 ALIGN="CENTER">Module mod_file_cache</H1>
+ <P>
+ <STRONG>This module should be used with care. You can easily create a
+ broken site using mod_file_cache, so read this document
+ carefully.</STRONG>
+ </P>
+
+ <P>
+ <EM>Caching</EM> frequently requested files that change very
+ infrequently is a technique for reducing server load. mod_file_cache
+ provides two techniques for caching frequently requested
+ <EM>static</EM> files.
+ Through configuration directives, you can direct mod_file_cache
+ to either open then mmap()a file, or to pre-open a file and save
+ the file's open <EM>file handle</EM>. Both techniques reduce server
+ load when processing requests for these files by doing part of the
+ work (specifically, the file I/O) for serving the file when the server
+ is started rather than during each request.
+ </P>
+
+ <P>
+ <CODE>mod_file_cache</CODE> is not compiled into the server by
+ default. To use <CODE>mod_file_cache</CODE> you have to enable the
+ following line in the server build <CODE>Configuration</CODE> file:
+ <PRE>
+ AddModule modules/experimental/mod_file_cache.o
+ </PRE>
+ </P>
+
+ <P>
+ Notice: You cannot use this for speeding up CGI programs or other files
+ which are served by special content handlers. It can only be used for
+ regular files which are usually served by the Apache core content handler.
+ </P>
+
+ <P>
+ This module is an extension of and borrows heavily from the
+ mod_mmap_static module in Apache 1.3.
+ </P>
+ <H2>Summary</H2>
+ <P>
+ <CODE>mod_file_cache</CODE> caches a list of statically configured
+ files via <CODE>MMapFile</CODE> or <CODE>CacheFile</CODE> directives
+ in the main server configuration.
+ </P>
+
+ <P>
+ Not all platforms support both directives. For
+ example, Apache on Windows does not currently support the MMapStatic
+ directive, while other platforms, like AIX, support both. You will
+ receive an error message in the server error log if you attempt to
+ use an unsupported directive. If given an unsupported directive, the
+ server will start but the file will not be cached. On platforms that
+ support both directives, you should experiment with both to see
+ which works best for you.
+ </P>
+
+ <H3><CODE>MmapFile</CODE> Directive </H3>
+ <P>
+ The <CODE>MmapFile</CODE> directive of <CODE>mod_file_cache</CODE>
+ maps a list of statically configured files into memory through the
+ system call <CODE>mmap()</CODE>. This system call is available on
+ most modern Unix derivates, but not on all. There are sometimes
+ system-specific limits on the size and number of files that can be
+ mmap()d, experimentation is probably the easiest way to find out.
+ </P>
+ <P>
+ This mmap()ing is done once at server start or restart, only. So whenever
+ one of the mapped files changes on the filesystem you <EM>have</EM> to
+ restart the server (see the <A HREF="../stopping.html">Stopping and
+ Restarting</A> documentation). To reiterate that point: if the
+ files are modified <EM>in place</EM> without restarting the server
+ you may end up serving requests that are completely bogus. You
+ should update files by unlinking the old copy and putting a new
+ copy in place. Most tools such as <CODE>rdist</CODE> and
+ <CODE>mv</CODE> do this. The reason why this modules doesn't take
+ care of changes to the files is that this check would need an extra
+ <CODE>stat()</CODE> every time which is a waste and against the
+ intent of I/O reduction.
+ </P>
+
+ <H3><CODE>CacheFile</CODE> Directive </H3>
+ <P>
+ The <CODE>CacheFile</CODE> directive of <CODE>mod_file_cache</CODE>
+ opens an active <EM>handle</EM> or <EM>file descriptor</EM> to the
+ file (or files) listed in the configuration directive and places
+ these open file handles in the cache. When the file is requested,
+ the server retrieves the handle from the cache and passes it to the
+ sendfile() (or TransmitFile() on Windows), socket API.
+ </P>
+ <P>
+ Insert more details about sendfile API...
+ </P>
+ <P>
+ This file handle caching is done once at server start or restart,
+ only. So whenever one of the cached files changes on the filesystem
+ you <EM>have</EM> to restart the server (see the <A
+ HREF="../stopping.html">Stopping and Restarting</A> documentation).
+ To reiterate that point: if the files are modified <EM>in
+ place</EM> without restarting the server you may end up serving
+ requests that are completely bogus. You should update files by
+ unlinking the old copy and putting a new copy in place. Most tools
+ such as <CODE>rdist</CODE> and <CODE>mv</CODE> do this.
+ </P>
+
+ <H2>Directives</H2>
+ <UL>
+ <LI><A HREF="#mmapfile">MMapFile</A>
+ </LI>
+ <LI><A HREF="#cachefile">CacheFile</A>
+ </LI>
+ </UL>
+
+ <HR>
+
+ <H2><A NAME="mmapfile">MMapFile</A></H2>
+ <P>
+ <A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+ ><STRONG>Syntax:</STRONG></A> MMapFile <EM>filename ...</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+ ><STRONG>Default:</STRONG></A> <EM>None</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+ ><STRONG>Context:</STRONG></A> server-config
+ <BR>
+ <A
+ HREF="directive-dict.html#Override"
+ REL="Help"
+ ><STRONG>Override:</STRONG></A> <EM>Not applicable</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+ ><STRONG>Status:</STRONG></A> Experimental
+ <BR>
+ <A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+ ><STRONG>Module:</STRONG></A> mod_file_cache
+ <BR>
+ <A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+ ><STRONG>Compatibility:</STRONG></A> Only in Apache 1.3 (via
+ mod_mmap_statis) or later.
+
+ <P>
+ The <CODE>MMapFile</CODE> directive maps one or more files (given as
+ whitespace separated arguments) into memory at server startup time. They
+ are automatically unmapped on a server shutdown. When the files have changed
+ on the filesystem at least a HUP or USR1 signal should be send to the server
+ to re-mmap them.
+ </P>
+
+ <P>
+ Be careful with the <EM>filename</EM> arguments: They have to literally
+ match the filesystem path Apache's URL-to-filename translation handlers
+ create. We cannot compare inodes or other stuff to match paths through
+ symbolic links <EM>etc.</EM> because that again would cost extra <CODE>stat()</CODE>
+ system calls which is not acceptable. This module may or may not work
+ with filenames rewritten by <CODE>mod_alias</CODE> or
+ <CODE>mod_rewrite</CODE>.
+ </P>
+
+ Example:
+
+ <PRE>
+ MMapFile /usr/local/apache/htdocs/index.html
+ </PRE>
+
+
+ <H2><A NAME="cachefile">CacheFile</A></H2>
+ <P>
+ <A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+ ><STRONG>Syntax:</STRONG></A> CacheFile <EM>filename ...</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+ ><STRONG>Default:</STRONG></A> <EM>None</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+ ><STRONG>Context:</STRONG></A> server-config
+ <BR>
+ <A
+ HREF="directive-dict.html#Override"
+ REL="Help"
+ ><STRONG>Override:</STRONG></A> <EM>Not applicable</EM>
+ <BR>
+ <A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+ ><STRONG>Status:</STRONG></A> Experimental
+ <BR>
+ <A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+ ><STRONG>Module:</STRONG></A> mod_file_cache
+ <BR>
+ <A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+ ><STRONG>Compatibility:</STRONG></A> Only available in Apache 2.0 or later.
+
+ <P>
+ The <CODE>CacheFile</CODE> directive opens handles to one or more
+ files (given as whitespace separated arguments) and places these
+ handles into the cache at server startup time. Handles to cached
+ files are automatically closed on a server shutdown. When the files
+ have changed on the filesystem, the server should be restarted to
+ to re-cache them.
+ </P>
+
+ <P>
+ Be careful with the <EM>filename</EM> arguments: They have to literally
+ match the filesystem path Apache's URL-to-filename translation handlers
+ create. We cannot compare inodes or other stuff to match paths through
+ symbolic links <EM>etc.</EM> because that again would cost extra <CODE>stat()</CODE>
+ system calls which is not acceptable. This module may or may not work
+ with filenames rewritten by <CODE>mod_alias</CODE> or
+ <CODE>mod_rewrite</CODE>.
+ </P>
+
+ Example:
+
+ <PRE>
+ CacheFile /usr/local/apache/htdocs/index.html
+ </PRE>
+
+ <P>
+ <STRONG>Note</STRONG>: don't bother asking for a for a directive which
+ recursively caches all the files in a directory. Try this
+ instead...
+ See the <A HREF="core.html#include">Include</A> directive, and consider this command:
+ <PRE>
+ find /www/htdocs -type f -print \
+ | sed -e 's/.*/mmapfile &amp;/' &gt; /www/conf/mmap.conf
+ </PRE>
+
+<!--#include virtual="footer.html" -->
+ </BODY>
+</HTML>
diff --git a/docs/manual/mod/module-dict.html b/docs/manual/mod/module-dict.html
new file mode 100644
index 0000000000..3e90bbb680
--- /dev/null
+++ b/docs/manual/mod/module-dict.html
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD>
+ <TITLE>Definitions of terms used to describe Apache modules
+ </TITLE>
+ </HEAD>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+ <BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+ >
+<!--#include virtual="header.html" -->
+ <H1 ALIGN="CENTER">Terms Used to Describe Apache Modules</H1>
+
+ <P>
+ Each Apache module is described using a common format that looks
+ like this:
+ </P>
+ <DL>
+ <DD><A
+ HREF="#Status"
+ REL="Help"
+ ><STRONG>Status:</STRONG></A> <EM>status</EM>
+ <BR>
+ <A
+ HREF="#SourceFile"
+ REL="Help"
+ ><STRONG>Source File:</STRONG></A> <EM>source-file</EM>
+ <BR>
+ <A
+ HREF="#ModuleIdentifier"
+ REL="Help"
+ ><STRONG>Module Identifier:</STRONG></A> <EM>module-identifier</EM>
+ <BR>
+ <A
+ HREF="#Compatibility"
+ REL="Help"
+ ><STRONG>Compatibility:</STRONG></A> <EM>compatibility notes</EM>
+ </DD>
+ </DL>
+ <P>
+ Each of the attributes, complete with values where possible, are
+ described in this document.
+ </P>
+
+ <H2>Module Terms</H2>
+ <UL>
+ <LI><A HREF="#Status">Status</A>
+ </LI>
+ <LI><A HREF="#SourceFile">Source File</A>
+ </LI>
+ <LI><A HREF="#ModuleIdentifier">Module Identifier</A>
+ </LI>
+ <LI><A HREF="#Compatibility">Compatibility</A>
+ </LI>
+ </UL>
+
+ <HR>
+ <H2><A NAME="Status">Status</A></H2>
+ <P>
+ This indicates how tightly bound into the Apache Web server the
+ module is; in other words, you may need to recompile the server in
+ order to gain access to the module and its functionality. Possible
+ values for this attribute are:
+ </P>
+ <DL>
+ <DT><STRONG>Base</STRONG>
+ </DT>
+ <DD>A module labeled as having &quot;Base&quot; status is compiled
+ and loaded into the server by default, and is therefore normally
+ available unless you have taken steps to remove the module from your
+ configuration.
+ <P>
+ </P>
+ </DD>
+ <DT><STRONG>Extension</STRONG>
+ </DT>
+ <DD>A module with &quot;Extension&quot; status is not normally
+ compiled and loaded into the server. To enable the module and its
+ functionality, you may need to change the server build
+ configuration files and re-compile Apache.
+ <P>
+ </P>
+ </DD>
+ <DT><STRONG>Experimental</STRONG>
+ </DT>
+ <DD>&quot;Experimental&quot; status indicates that the module is
+ available as part of the Apache kit, but you are on your own if you
+ try to use it. The module is being documented for completeness,
+ and is not necessarily supported.
+ <P>
+ </P>
+ </DD>
+ <DT><STRONG>External</STRONG>
+ </DT>
+ <DD>Modules which are not included with the base Apache
+ distribution (&quot;third-party modules&quot;) may use the
+ &quot;External&quot; status. We are not responsible for, nor do we
+ support such modules.
+ <P>
+ </P>
+ </DD>
+ </DL>
+
+ <HR>
+ <H2><A NAME="SourceFile">Source File</A></H2>
+ <P>
+ This quite simply lists the name of the source file which contains
+ the code for the module. This is also the name used by the <A
+ HREF="core.html#ifmodule"><CODE>&lt;IfModule&gt;</CODE></A>
+ directive.
+ </P>
+
+ <HR>
+ <H2><A NAME="ModuleIdentifier">Module Identifier</A></H2>
+ <P>
+ This is a string which identifies the module for use in the <A
+ HREF="mod_so.html#loadmodule">LoadModule</A> directive when
+ dynamically loading modules. In particular, it is the name
+ of the external variable of type module in the source file.
+ </P>
+
+ <HR>
+ <H2><A NAME="Compatibility">Compatibility</A></H2>
+ <P>
+ If the module was not part of the original Apache version 2
+ distribution, the version in which it was introduced should be listed
+ here.
+ </P>
+<!--#include virtual="footer.html" -->
+ </BODY>
+</HTML>
diff --git a/docs/manual/mod/module-dict.html.en b/docs/manual/mod/module-dict.html.en
new file mode 100644
index 0000000000..3e90bbb680
--- /dev/null
+++ b/docs/manual/mod/module-dict.html.en
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD>
+ <TITLE>Definitions of terms used to describe Apache modules
+ </TITLE>
+ </HEAD>
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+ <BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+ >
+<!--#include virtual="header.html" -->
+ <H1 ALIGN="CENTER">Terms Used to Describe Apache Modules</H1>
+
+ <P>
+ Each Apache module is described using a common format that looks
+ like this:
+ </P>
+ <DL>
+ <DD><A
+ HREF="#Status"
+ REL="Help"
+ ><STRONG>Status:</STRONG></A> <EM>status</EM>
+ <BR>
+ <A
+ HREF="#SourceFile"
+ REL="Help"
+ ><STRONG>Source File:</STRONG></A> <EM>source-file</EM>
+ <BR>
+ <A
+ HREF="#ModuleIdentifier"
+ REL="Help"
+ ><STRONG>Module Identifier:</STRONG></A> <EM>module-identifier</EM>
+ <BR>
+ <A
+ HREF="#Compatibility"
+ REL="Help"
+ ><STRONG>Compatibility:</STRONG></A> <EM>compatibility notes</EM>
+ </DD>
+ </DL>
+ <P>
+ Each of the attributes, complete with values where possible, are
+ described in this document.
+ </P>
+
+ <H2>Module Terms</H2>
+ <UL>
+ <LI><A HREF="#Status">Status</A>
+ </LI>
+ <LI><A HREF="#SourceFile">Source File</A>
+ </LI>
+ <LI><A HREF="#ModuleIdentifier">Module Identifier</A>
+ </LI>
+ <LI><A HREF="#Compatibility">Compatibility</A>
+ </LI>
+ </UL>
+
+ <HR>
+ <H2><A NAME="Status">Status</A></H2>
+ <P>
+ This indicates how tightly bound into the Apache Web server the
+ module is; in other words, you may need to recompile the server in
+ order to gain access to the module and its functionality. Possible
+ values for this attribute are:
+ </P>
+ <DL>
+ <DT><STRONG>Base</STRONG>
+ </DT>
+ <DD>A module labeled as having &quot;Base&quot; status is compiled
+ and loaded into the server by default, and is therefore normally
+ available unless you have taken steps to remove the module from your
+ configuration.
+ <P>
+ </P>
+ </DD>
+ <DT><STRONG>Extension</STRONG>
+ </DT>
+ <DD>A module with &quot;Extension&quot; status is not normally
+ compiled and loaded into the server. To enable the module and its
+ functionality, you may need to change the server build
+ configuration files and re-compile Apache.
+ <P>
+ </P>
+ </DD>
+ <DT><STRONG>Experimental</STRONG>
+ </DT>
+ <DD>&quot;Experimental&quot; status indicates that the module is
+ available as part of the Apache kit, but you are on your own if you
+ try to use it. The module is being documented for completeness,
+ and is not necessarily supported.
+ <P>
+ </P>
+ </DD>
+ <DT><STRONG>External</STRONG>
+ </DT>
+ <DD>Modules which are not included with the base Apache
+ distribution (&quot;third-party modules&quot;) may use the
+ &quot;External&quot; status. We are not responsible for, nor do we
+ support such modules.
+ <P>
+ </P>
+ </DD>
+ </DL>
+
+ <HR>
+ <H2><A NAME="SourceFile">Source File</A></H2>
+ <P>
+ This quite simply lists the name of the source file which contains
+ the code for the module. This is also the name used by the <A
+ HREF="core.html#ifmodule"><CODE>&lt;IfModule&gt;</CODE></A>
+ directive.
+ </P>
+
+ <HR>
+ <H2><A NAME="ModuleIdentifier">Module Identifier</A></H2>
+ <P>
+ This is a string which identifies the module for use in the <A
+ HREF="mod_so.html#loadmodule">LoadModule</A> directive when
+ dynamically loading modules. In particular, it is the name
+ of the external variable of type module in the source file.
+ </P>
+
+ <HR>
+ <H2><A NAME="Compatibility">Compatibility</A></H2>
+ <P>
+ If the module was not part of the original Apache version 2
+ distribution, the version in which it was introduced should be listed
+ here.
+ </P>
+<!--#include virtual="footer.html" -->
+ </BODY>
+</HTML>
diff --git a/docs/manual/mod/mpm_common.html b/docs/manual/mod/mpm_common.html
new file mode 100644
index 0000000000..75dd0b8082
--- /dev/null
+++ b/docs/manual/mod/mpm_common.html
@@ -0,0 +1,409 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Apache MPM Common Directives</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+
+<H1 ALIGN="CENTER">Multi-Processing Module Common Directives</H1>
+
+<P>This file documents directives that are implemented by more
+than one multi-processing module (MPM).
+</P>
+
+<H2>Directives</H2>
+<UL>
+<li><a href="#coredumpdirectory">CoreDumpDirectory</a></li>
+<li><a href="#pidfile">PidFile</a></li>
+<li><a href="#lockfile">LockFile</a></li>
+<li><a href="#maxclients">MaxClients</a></li>
+<li><a href="#maxrequestsperchild">MaxRequestsPerChild</a></li>
+<li><a href="#maxsparethreads">MaxSpareThreads</a></li>
+<li><a href="#minsparethreads">MinSpareThreads</a></li>
+<li><a href="#scoreboardfile">ScoreBoardFile</a></li>
+<li><a href="#startservers">StartServers</a></li>
+<li><a href="#threadsperchild">ThreadsPerChild</a></li>
+</UL>
+<HR>
+
+<H2><A NAME="coredumpdirectory">CoreDumpDirectory directive</A></H2>
+<!--%plaintext &lt;?INDEX {\tt CoreDumpDirectory} directive&gt; -->
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> CoreDumpDirectory <EM>directory</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> the same location as ServerRoot<BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> MPM<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mpmt_pthread, prefork</p>
+
+<p>This controls the directory to which Apache attempts to switch
+before dumping core. The default is in the <A
+HREF="core.html#serverroot">ServerRoot</A> directory, however since
+this should not be writable by the user the server runs as, core dumps
+won't normally get written. If you want a core dump for debugging,
+you can use this directive to place it in a different location.<P><HR>
+
+<H2><A NAME="pidfile">PidFile directive</A></H2>
+<!--%plaintext &lt;?INDEX {\tt PidFile} directive&gt; -->
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> PidFile <EM>filename</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> <CODE>PidFile logs/httpd.pid</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> MPM<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mpmt_pthread, prefork</p>
+
+<p>The PidFile directive sets the file to which the server records the
+process id of the daemon. If the filename does not begin with a slash
+(/) then it is assumed to be relative to the <A
+HREF="core.html#serverroot">ServerRoot</A>.</p>
+
+<p>It is often useful to be able to send the server a signal, so that
+it closes and then reopens its <A
+HREF="core.html#errorlog">ErrorLog</A> and TransferLog, and re-reads
+its configuration files. This is done by sending a SIGHUP (kill -1)
+signal to the process id listed in the PidFile.</p>
+
+<p>The PidFile is subject to the same warnings about log file placement and
+<A HREF="../misc/security_tips.html#serverroot">security</A>.</p>
+
+
+<P><HR>
+
+
+<H2><A NAME="lockfile">LockFile directive</A></H2>
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> LockFile <EM>filename</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> <CODE>LockFile logs/accept.lock</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> MPM<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mpmt_pthread, prefork</p>
+
+<p>The LockFile directive sets the path to the lockfile used when
+Apache is compiled with either USE_FCNTL_SERIALIZED_ACCEPT or
+USE_FLOCK_SERIALIZED_ACCEPT. This directive should normally be
+left at its default value. The main reason for changing it is if
+the <CODE>logs</CODE> directory is NFS mounted, since <STRONG>the lockfile
+must be stored on a local disk</STRONG>. The PID of the main
+server process is automatically appended to the filename. <P>
+
+<p><STRONG>SECURITY:</STRONG> It is best to avoid putting this file in a
+world writable directory such as <CODE>/var/tmp</CODE> because someone
+could create a denial of service attack and prevent the server from
+starting by creating a lockfile with the same name as the one the
+server will try to create.</p>
+
+<hr>
+
+<H2><A NAME="maxclients">MaxClients directive</A></H2>
+<!--%plaintext &lt;?INDEX {\tt MaxClients} directive&gt; -->
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> MaxClients <EM>number</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> <CODE>MaxClients 8</code> (with threads)
+<code>MaxClients 256</code> (no threads)<BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> MPM<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mpmt_pthread, prefork</p>
+
+<P>The MaxClients directive sets the limit on the number of child
+processes that will be created to serve requests. When the server is
+built without threading, no more than this number of clients can be
+served simultaneously. To configure more than 256 clients, you must
+edit the <code>HARD_SERVER_LIMIT</code> entry in
+<code>mpm_default.h</code> and recompile.
+
+<P>Any connection attempts over the MaxClients limit will normally
+be queued, up to a number based on the <A HREF="#listenbacklog">
+ListenBacklog</A> directive. Once a child process is freed at the
+end of a different request, the connection will then be serviced.</p>
+
+<p>When the server is compiled with threading, then the maximum number
+of simultaneous requests that can be served is obtained from the value
+of this directive multiplied by <a
+href="#threadsperchild">ThreadsPerChild</a>.</p>
+
+<HR>
+
+<H2><A NAME="maxrequestsperchild">MaxRequestsPerChild directive</A></H2>
+<!--%plaintext &lt;?INDEX {\tt MaxRequestsPerChild} directive&gt; -->
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> MaxRequestsPerChild <EM>number</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> <CODE>MaxRequestsPerChild 10000</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> MPM<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mpmt_pthread, prefork</p>
+
+<p>The MaxRequestsPerChild directive sets the limit on the number of requests
+that an individual child server process will handle. After MaxRequestsPerChild
+requests, the child process will die. If MaxRequestsPerChild is 0, then
+the process will never expire.<P>
+
+Setting MaxRequestsPerChild to a non-zero limit has two beneficial effects:
+<UL>
+<LI>it limits the amount of memory that process can consume by (accidental)
+memory leakage;
+<LI> by giving processes a finite lifetime, it helps reduce the
+number of processes when the server load reduces.
+</UL>
+
+<P><STRONG>NOTE:</STRONG> For <EM>KeepAlive</EM> requests, only the first
+request is counted towards this limit. In effect, it changes the
+behavior to limit the number of <EM>connections</EM> per child.
+
+<P><HR>
+
+
+<H2><A NAME="maxsparethreads">MaxSpareThreads directive</A></H2>
+<!--%plaintext &lt;?INDEX {\tt MaxSpareServers} directive&gt; -->
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> MaxSpareThreads <EM>number</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> <CODE>MaxSpareThreads ??</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> core<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mpmt_pthread</p>
+
+<P>Content needed here!</p>
+
+<p>See also <A HREF="#minsparethreads">MinSpareThreads</A> and
+<A HREF="#startservers">StartServers</A>.
+
+<P><HR>
+
+<H2><A NAME="minsparethreads">MinSpareThreads directive</A></H2>
+<!--%plaintext &lt;?INDEX {\tt MinSpareServers} directive&gt; -->
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> MinSpareServers <EM>number</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> <CODE>MinSpareServers ???</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> core<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mpmt_pthread</p>
+
+<p><strong>fix me</strong></p>
+
+<p>The MinSpareServers directive sets the desired minimum number of <EM>idle</EM>
+child server processes. An idle process is one which is not handling
+a request. If there are fewer than MinSpareServers idle, then the parent
+process creates new children at a maximum rate of 1 per second.<P>
+
+Tuning of this parameter should only be necessary on very busy sites.
+Setting this parameter to a large number is almost always a bad idea.<P>
+
+This directive has no effect on Microsoft Windows.
+
+<P>
+
+See also <A HREF="#maxsparethreads">MaxSpareThreads</A> and
+<A HREF="#startservers">StartServers</A>.<P><HR>
+
+
+<H2><A NAME="scoreboardfile">ScoreBoardFile directive</A></H2>
+<!--%plaintext &lt;?INDEX {\tt ScoreBoardFile} directive&gt; -->
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> ScoreBoardFile <EM>filename</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> <CODE>ScoreBoardFile logs/apache_status</CODE>
+<BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> MPM<BR>
+<A
+ HREF="directive-dict.html#Compatibility"
+ REL="Help"
+>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mpmt_pthread, prefork</p>
+
+<p>The ScoreBoardFile directive is required on some architectures to place
+a file that the server will use to communicate between its children and
+the parent. The easiest way to find out if your architecture requires
+a scoreboard file is to run Apache and see if it creates the file named
+by the directive. If your architecture requires it then you must ensure
+that this file is not used at the same time by more than one invocation
+of Apache.</p>
+
+<p>If you have to use a ScoreBoardFile then you may see improved speed by
+placing it on a RAM disk. But be careful that you heed the same warnings
+about log file placement and
+<A HREF="../misc/security_tips.html">security</A>.</p>
+
+<p><STRONG>See Also</STRONG>:
+<A HREF="../stopping.html">Stopping and Restarting Apache</A></P>
+
+
+<P><HR>
+
+<H2><A NAME="startservers">StartServers directive</A></H2>
+<!--%plaintext &lt;?INDEX {\tt StartServers} directive&gt; -->
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> StartServers <EM>number</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> <CODE>StartServers 5</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> MPM<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mpmt_pthread, prefork</p>
+
+<p>The StartServers directive sets the number of child server processes created
+on startup. As the number of processes is dynamically controlled depending
+on the load, there is usually little reason to adjust this parameter.</P>
+
+<P>See also <A HREF="#minsparethreads">MinSpareThreads</A> and
+<A HREF="#maxsparethreads">MaxSpareThreads</A>.<P><HR>
+
+<H2><A NAME="threadsperchild">ThreadsPerChild</A></H2>
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> ThreadsPerChild <EM>number</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> <CODE>ThreadsPerChild 50</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> Base<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> mpmt_pthread</p>
+
+<P>This directive sets the number of threads created by each child
+process.</p>
+
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/mod/mpm_winnt.html b/docs/manual/mod/mpm_winnt.html
new file mode 100644
index 0000000000..f4a90f03cf
--- /dev/null
+++ b/docs/manual/mod/mpm_winnt.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Apache MPM pthread</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+
+<H1 ALIGN="CENTER">Multi-Processing Module mpm_winnt</H1>
+<P>
+This Multi-Processing Module is optimized for Windows NT.
+</P>
+
+<P><A
+HREF="module-dict.html#Status"
+REL="Help"
+><STRONG>Status:</STRONG></A> MPM
+<BR>
+<A
+HREF="module-dict.html#SourceFile"
+REL="Help"
+><STRONG>Source File:</STRONG></A> mpm_winnt.c
+<BR>
+<A
+HREF="module-dict.html#ModuleIdentifier"
+REL="Help"
+><STRONG>Module Identifier:</STRONG></A> mpm_winnt_module
+</P>
+
+<H2>Summary</H2>
+
+<p>This Multi-Processing Module (MPM) is the default for
+the Windows NT operating systems. It uses a single control
+process which launches a single child process which in turn
+creates threads to handle requests</p>
+
+
+<H2>Directives</H2>
+<UL>
+<li><a href="mpm_common.html#coredumpdirectory">CoreDumpDirectory</a></li>
+<li><a href="mpm_common.html#pidfile">PidFile</a></li>
+<li><a href="mpm_common.html#listen">Listen</a></li>
+<li><a href="mpm_common.html#listenbacklog">ListenBacklog</a></li>
+<li><a href="mpm_common.html#maxrequestsperchild">MaxRequestsPerChild</a></li>
+<li><a href="mpm_common.html#sendbuffersize">SendBufferSize</a></li>
+<li><a href="mpm_common.html#threadsperchild">ThreadsPerChild</a></li>
+</UL>
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/mod/perchild.html b/docs/manual/mod/perchild.html
new file mode 100644
index 0000000000..cabafdab90
--- /dev/null
+++ b/docs/manual/mod/perchild.html
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Apache MPM mpmt_pthread</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+
+<H1 ALIGN="CENTER">Multi-Processing Module perchild</H1>
+<P>
+This Multi-Processing Module allows for daemon processes serving requests
+to be assigned a variety of different userids.
+</P>
+
+<P><A
+HREF="module-dict.html#Status"
+REL="Help"
+><STRONG>Status:</STRONG></A> MPM
+<BR>
+<A
+HREF="module-dict.html#SourceFile"
+REL="Help"
+><STRONG>Source File:</STRONG></A> perchild.c
+<BR>
+<A
+HREF="module-dict.html#ModuleIdentifier"
+REL="Help"
+><STRONG>Module Identifier:</STRONG></A> mpm_perchild_module
+</P>
+
+<H2>Summary</H2>
+
+<p>Some description on how this whole thing works with a couple examples.</p>
+
+
+<H2>Directives</H2>
+<UL>
+<li><a href="#assignuserid">AssignUserID</a></li>
+<li><a href="#childperuserid">ChildPerUserID</a></li>
+<li><a href="mpm_common.html#connectionstatus">ConnectionStatus</a></li>
+<li><a href="mpm_common.html#coredumpdirectory">CoreDumpDirectory</a></li>
+<li><a href="mpm_common.html#group">Group</a></li>
+<li><a href="mpm_common.html#pidfile">PidFile</a></li>
+<li><a href="mpm_common.html#listen">Listen</a></li>
+<li><a href="mpm_common.html#listenbacklog">ListenBacklog</a></li>
+<li><a href="mpm_common.html#lockfile">LockFile</a></li>
+<li><a href="mpm_common.html#maxrequestsperchild">MaxRequestsPerChild</a></li>
+<li><a href="mpm_common.html#maxsparethreads">MaxSpareThreads</a></li>
+<li><a href="mpm_common.html#minsparethreads">MinSpareThreads</a></li>
+<li><a href="mpm_common.html#numservers">NumServers</a></li>
+<li><a href="mpm_common.html#scoreboardfile">ScoreBoardFile</a></li>
+<li><a href="mpm_common.html#sendbuffersize">SendBufferSize</a></li>
+<li><a href="mpm_common.html#startthreads">StartThreads</a></li>
+<li><a href="mpm_common.html#user">User</a></li>
+</UL>
+
+<p><hr></p>
+
+<H2><A NAME="assignuserid">AssignUserID directive</A></H2>
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> MPM<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> perchild</p>
+
+<p>Tie a virtual host to a specific child process.</p>
+
+<p><hr></p>
+
+<H2><A NAME="childperuserid">ChilePerUserID directive</A></H2>
+<p><A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> MPM<BR>
+<A
+ HREF="directive-dict.html#Module"
+ REL="Help"
+><STRONG>Module:</STRONG></A> perchild</p>
+
+<p>Specify a User and Group for a specific child process.</p>
+
+
+
+
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/mod/prefork.html b/docs/manual/mod/prefork.html
new file mode 100644
index 0000000000..ba305704f1
--- /dev/null
+++ b/docs/manual/mod/prefork.html
@@ -0,0 +1,130 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Apache MPM mpmt_pthread</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+
+<H1 ALIGN="CENTER">Multi-Processing Module prefork</H1>
+<P>
+This Multi-Processing Module implements a pre-forking web server.
+</P>
+
+<P><A
+HREF="module-dict.html#Status"
+REL="Help"
+><STRONG>Status:</STRONG></A> MPM
+<BR>
+<A
+HREF="module-dict.html#SourceFile"
+REL="Help"
+><STRONG>Source File:</STRONG></A> prefork.c
+<BR>
+<A
+HREF="module-dict.html#ModuleIdentifier"
+REL="Help"
+><STRONG>Module Identifier:</STRONG></A> mpm_prefork_module
+</P>
+
+<H2>Summary</H2>
+
+<p>This Multi-Processing Module (MPM) implements a pre-forking
+non-threaded web server which handles request in a manner
+very similar to the default behavior of Apache 1.3 on Unix.</p>
+
+
+<H2>Directives</H2>
+<UL>
+<li><a href="mpm_common.html#coredumpdirectory">CoreDumpDirectory</a></li>
+<li><a href="mpm_common.html#pidfile">PidFile</a></li>
+<li><a href="mpm_common.html#lockfile">LockFile</a></li>
+<li><a href="mpm_common.html#maxclients">MaxClients</a></li>
+<li><a href="mpm_common.html#maxrequestsperchild">MaxRequestsPerChild</a></li>
+<li><a href="#maxsparethreads">MaxSpareServers</a></li>
+<li><a href="#minsparethreads">MinSpareServers</a></li>
+<li><a href="mpm_common.html#scoreboardfile">ScoreBoardFile</a></li>
+<li><a href="mpm_common.html#startservers">StartServers</a></li>
+</UL>
+
+<hr>
+
+
+<H2><A NAME="maxspareservers">MaxSpareServers directive</A></H2>
+<!--%plaintext &lt;?INDEX {\tt MaxSpareServers} directive&gt; -->
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> MaxSpareServers <EM>number</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> <CODE>MaxSpareServers 10</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> core<P>
+
+The MaxSpareServers directive sets the desired maximum number of <EM>idle</EM>
+child server processes. An idle process is one which is not handling
+a request. If there are more than MaxSpareServers idle, then the parent
+process will kill off the excess processes.<P>
+
+Tuning of this parameter should only be necessary on very busy sites.
+Setting this parameter to a large number is almost always a bad idea.<P>
+
+<P>
+
+See also <A HREF="#minspareservers">MinSpareServers</A> and
+<A HREF="mpm_common.html#startservers">StartServers</A>.<P><HR>
+
+<H2><A NAME="minspareservers">MinSpareServers directive</A></H2>
+<!--%plaintext &lt;?INDEX {\tt MinSpareServers} directive&gt; -->
+<A
+ HREF="directive-dict.html#Syntax"
+ REL="Help"
+><STRONG>Syntax:</STRONG></A> MinSpareServers <EM>number</EM><BR>
+<A
+ HREF="directive-dict.html#Default"
+ REL="Help"
+><STRONG>Default:</STRONG></A> <CODE>MinSpareServers 5</CODE><BR>
+<A
+ HREF="directive-dict.html#Context"
+ REL="Help"
+><STRONG>Context:</STRONG></A> server config<BR>
+<A
+ HREF="directive-dict.html#Status"
+ REL="Help"
+><STRONG>Status:</STRONG></A> core<P>
+
+The MinSpareServers directive sets the desired minimum number of <EM>idle</EM>
+child server processes. An idle process is one which is not handling
+a request. If there are fewer than MinSpareServers idle, then the parent
+process creates new children at a maximum rate of 1 per second.<P>
+
+Tuning of this parameter should only be necessary on very busy sites.
+Setting this parameter to a large number is almost always a bad idea.<P>
+
+This directive has no effect on Microsoft Windows.
+
+<P>
+
+See also <A HREF="#maxspareservers">MaxSpareServers</A> and
+<A HREF="mpm_common.html#startservers">StartServers</A>.<P><HR>
+
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/new_features_2_0.html.fr b/docs/manual/new_features_2_0.html.fr
new file mode 100644
index 0000000000..0b5188f0c0
--- /dev/null
+++ b/docs/manual/new_features_2_0.html.fr
@@ -0,0 +1,88 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD>
+<TITLE>Nouvelles fonctionnalit&eacute;s d'Apache 2.0</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited),
+red (active) -->
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF"
+ VLINK="#000080" ALINK="#FF0000">
+<!--#include virtual="header.html" -->
+<H1 ALIGN="CENTER">Aper&ccedil;u des nouvelles fonctionnalit&eacute;s
+d'Apache 2.0</H1>
+
+<P>Am&eacute;liorations&nbsp;: <A HREF="#core">Noyau</A>
+| <a href="#module">Module</a>
+
+<P><HR>
+
+<H2><A NAME="core">Am&eacute;liorations du noyau &nbsp;:</A></H2>
+
+<DL>
+<DT><STRONG>Threads sur Unix</STRONG>
+<DD>Sur les syst&egrave;mes Unix, Apache peut s'ex&eacute;cuter selon
+un mod&egrave;le hybride multi-processus et multi-threads,
+en employant les threads selon la norme POSIX. Ceci devrait am&eacute;liorer
+les performances.
+
+<DT><STRONG>Nouveau système de construction</STRONG>
+<DD>Le système de construction a &eacute;t&eacute; enti&egrave;rement
+r&eacute;&eacute;crit et repose sur autoconf et libtool. Cela rend le
+syst&egrave;me de configuration plus semblables aux autres paquetages.
+
+<DT><STRONG>Support multiprotocole</STRONG>
+<DD>Apache possède maintenant une infrastructure afin de servir de multiples
+protocoles. mod_echo a &eacute;t&eacute; &eacute;crit comme exemple de ces
+nouvelles fonctions.
+
+<DT><STRONG>Meilleur support des plates-formes autres qu'Unix</STRONG>
+<DD>Apache 2.0 est plus rapide et plus stable sur les plates-formes non Unix
+telles que BeOS, OS/2, et Windows. Avec l'introduction des
+<a href="mpm.html">modules multi traitements</a> (MPMs) sp&eacute;cifiques aux
+plates-formes et l'ex&eacute;cuteur portable Apache (APR), le code pour ces
+plates-formes est r&eacute;alis&eacute; en employant leurs API natives,
+permettant ainsi d'&eacute;viter les couches d'&eacute;mulations POSIX
+souvent bogu&eacute;es et peu performantes.
+
+<DT><STRONG>Nouvelle API Apache</STRONG>
+<DD>L'API pour les modules de la version 2.0 a chang&eacute; de mani&egrave;re
+importante. Beaucoup de probl&egrave;mes d'ordonnancement des modules existants
+dans la version 1.3 devraient dispara&icirc;tre. La version 2.0 g&egrave;re ceci de
+manière automatique, et l'ordonnancement des modules s'effectue selon
+une fonction d'accrochage afin de permettre une plus grande flexibilit&eacute;.
+
+</DL>
+
+<P><HR>
+<H2><A NAME="module">Am&eacute;liorations concernant les modules&nbsp;:</A></H2>
+
+<dl>
+
+<dt><strong>mod_auth_db</strong>
+<dd>Il accepte maintenant les bases Berkeley DB 3.0.
+
+<dt><strong>mod_auth_digest</strong>
+<dd>Il inclut une nouvelle gestion des sessions en utilisant un cache commun
+aux processus grace &agrave; une m&eacute;moire partag&eacute;e.
+
+<dt><strong>mod_charset_lite</strong> <dd>Nouveau module dans Apache 2.0.
+Ce module exp&eacute;rimental permet la traduction des pages de caract&egrave;res
+ou leur recodage.
+
+<dt><strong>mod_dav</strong>
+<dd>Nouveau module dans Apache 2.0. Ce module met en oeuvre la sp&eacute;cification
+"HTTP Distributed Authoring and Versioning (DAV)" permettant de distribuer et
+maintenir le contenu d'un site web.
+
+<dt><strong>mod_file_cache</strong>
+<dd>Nouveau module dans Apache 2.0. Ce module inclut les fonctionnalit&eacute;s
+du module mod_mmap_static existant dans la version d'Apache 1.3, en ajoutant
+davantage de possibilit&eacute;s de cache.
+
+</dl>
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
+
+
diff --git a/docs/manual/programs/ab.html b/docs/manual/programs/ab.html
new file mode 100644
index 0000000000..5cc22202d0
--- /dev/null
+++ b/docs/manual/programs/ab.html
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD><TITLE>Manual Page: ab - Apache HTTP Server</TITLE></HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF"
+VLINK="#000080" ALINK="#FF0000">
+<!--#include virtual="header.html" -->
+<!-- This document was autogenerated from the man page -->
+<pre>
+<strong>NAME</strong>
+ ab - Apache HTTP server benchmarking tool
+
+<strong>SYNOPSIS</strong>
+ <strong>ab </strong>[ -<strong>k </strong>] [ -<strong>n </strong><em>requests </em>] [ -<strong>t </strong><em>timelimit </em>] [ -<strong>c </strong><em>concurrency</em>
+ ] [ -<strong>p </strong><em>POST file </em>] [ -<strong>A </strong><em>Authenticate username</em>:<em>password </em>] [
+ -<strong>P </strong><em>Proxy Authenticate username</em>:<em>password </em>] [ -<strong>H </strong><em>Custom header</em>
+ ] [ -<strong>C </strong><em>Cookie name</em>=<em>value </em>] [ -<strong>T </strong><em>content</em>-<em>type </em>] [ -<strong>v </strong><em>verbos-</em>
+ <em>ity </em>] ] [ -<strong>w </strong><em>output HTML </em>] ] [ -<strong>x </strong>&lt;<em>table</em>&gt; <em>attributes </em>] ] [
+ -<strong>y </strong>&lt;<em>tr</em>&gt; <em>attributes </em>] ] [ -<strong>z </strong>&lt;<em>td</em>&gt; <em>attributes </em>]
+ [<em>http</em>://]<em>hostname</em>[:<em>port</em>]/<em>path</em>
+
+ <strong>ab </strong>[ -<strong>V </strong>] [ -<strong>h </strong>]
+
+<strong>DESCRIPTION</strong>
+ <strong>ab </strong>is a tool for benchmarking your Apache HyperText Transfer
+ Protocol (HTTP) server. It is designed to give you an
+ impression on how performant is your current Apache instal-
+ lation. This especially shows you how much requests per
+ time your Apache installation is capable to serve.
+
+<strong>OPTIONS</strong>
+ -<strong>k </strong>Enable the HTTP KeepAlive feature, i.e. perform
+ multiple requests within one HTTP session
+ instead. Default is no KeepAlive.
+
+ -<strong>n </strong><em>requests </em>Number of requests to perform for the benchmark-
+ ing session. The default is to just perform one
+ single request which usually leads to not very
+ representative benchmarking results.
+
+ -<strong>t </strong><em>timelimit</em>
+ Seconds to max. spend for benchmarking. This
+ implies a -<strong>n 50000 </strong>internally. Use this to
+ benchmark the server within a fixed total amount
+ of time. Per default there is no timelimit.
+
+ -<strong>c </strong><em>concurrency</em>
+ Number of multiple requests per time to perform.
+ Default is one request per time.
+
+ -<strong>p </strong><em>POST file</em>
+ File containing data to POST.
+
+ -<strong>A </strong><em>Authorization username</em>:<em>password</em>
+ Supply BASIC Authentification credentials to the
+ server. The username and password are separated
+ by a single ':' and send on the wire uuencoded.
+ The string is send regardless of wether the
+ server needs it; (i.e. has send an 401.
+ Authentifcation needed).
+
+ -<strong>p </strong><em>Proxy</em>-<em>Authorization username</em>:<em>password</em>
+ Supply BASIC Authentification credentials to a
+ proxy en-route. The username and password are
+ separated by a single ':' and send on the wire
+ uuencoded. The string is send regardless of
+ wether the proxy needs it; (i.e. has send an 407
+ Proxy authentifcation needed).
+
+ -<strong>C </strong><em>Cookie name</em>=<em>value</em>
+ Add a 'Cookie:' line to the request. The argu-
+ ment is typically in the form of a 'name=value'
+ pair. This field is repeatable.
+
+ -<strong>p </strong><em>Header string</em>
+ Postfix extra headers to the request. The argu-
+ ment is typically in the form of a valid header
+ line; containing a colon separated field value
+ pair. (i.e.
+
+ -<strong>T </strong><em>content</em>-<em>type</em>
+ Content-type header to use for POST data.
+
+ -<strong>v </strong>Set verbosity level - 4 and above prints infor-
+ mation on headers, 3 and above prints response
+ codes (404, 200, etc.), 2 and above prints warn-
+ ings and info.
+
+ -<strong>w </strong>Print out results in HTML tables. Default table
+ is two columns wide, with a white background.
+
+ -<strong>x </strong><em>attributes</em>
+ String to use as attributes for &lt;table&gt;. Attri-
+ butes are inserted &lt;table <strong>here </strong>&gt;
+
+ -<strong>y </strong><em>attributes</em>
+ String to use as attributes for &lt;tr&gt;.
+
+ -<strong>z </strong><em>attributes</em>
+ String to use as attributes for &lt;td&gt;.
+
+ -<strong>V </strong>Display version number and exit.
+
+ -<strong>h </strong>Display usage information.
+
+<strong>BUGS</strong>
+ There are various statically declared buffers of fixed
+ length. Combined with the lazy parsing of the command line
+ arguments, the response headers from the server and other
+ external inputs this might bite you.
+
+ It does not implement HTTP/1.x fully; only accepts some
+ 'expected' forms of responses. The rather heavy use of
+ <strong>strstr(3) </strong>shows up top in profile, which might indicate a
+ performance problem; i.e. you would measure the <strong>ab </strong>perfor-
+ mance rather than the server's.
+
+<strong>SEE ALSO</strong>
+ <strong>httpd(8)</strong>
+
+</pre>
+<!--#include virtual="footer.html" -->
+</BODY></HTML>
diff --git a/docs/manual/programs/apachectl.html b/docs/manual/programs/apachectl.html
new file mode 100644
index 0000000000..b16a717961
--- /dev/null
+++ b/docs/manual/programs/apachectl.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD><TITLE>Manual Page: apachectl - Apache HTTP Server</TITLE></HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF"
+VLINK="#000080" ALINK="#FF0000">
+<!--#include virtual="header.html" -->
+<!-- This document was autogenerated from the man page -->
+<pre>
+<strong>NAME</strong>
+ apachectl - Apache HTTP server control interface
+
+<strong>SYNOPSIS</strong>
+ <strong>apachectl </strong><em>command </em>[...]
+
+<strong>DESCRIPTION</strong>
+ <strong>apachectl </strong>is a front end to the Apache HyperText Transfer
+ Protocol (HTTP) server. It is designed to help the adminis-
+ trator control the functioning of the Apache <strong>httpd </strong>daemon.
+
+ <strong>NOTE: </strong>If your Apache installation uses non-standard paths,
+ you will need to edit the <strong>apachectl </strong>script to set the
+ appropriate paths to your PID file and your <strong>httpd </strong>binary.
+ See the comments in the script for details.
+
+ The <strong>apachectl </strong>script returns a 0 exit value on success, and
+ &gt;0 if an error occurs. For more details, view the comments
+ in the script.
+
+ Full documentation for Apache is available at
+ <strong>http://www.apache.org/</strong>
+
+<strong>OPTIONS</strong>
+ The <em>command </em>can be any one or more of the following options:
+
+ <strong>start </strong>Start the Apache daemon. Gives an error if it
+ is already running.
+
+ <strong>stop </strong>Stops the Apache daemon.
+
+ <strong>restart </strong>Restarts the Apache daemon by sending it a
+ SIGHUP. If the daemon is not running, it is
+ started. This command automatically checks the
+ configuration files via <strong>configtest </strong>before ini-
+ tiating the restart to make sure Apache doesn't
+ die.
+
+ <strong>fullstatus </strong>Displays a full status report from <strong>mod_status.</strong>
+ For this to work, you need to have mod_status
+ enabled on your server and a text-based browser
+ such as <em>lynx </em>available on your system. The URL
+ used to access the status report can be set by
+ editing the <strong>STATUSURL </strong>variable in the script.
+
+ <strong>status </strong>Displays a brief status report. Similar to the
+ fullstatus option, except that the list of
+ requests currently being served is omitted.
+
+ <strong>graceful </strong>Gracefully restarts the Apache daemon by sending
+ it a SIGUSR1. If the daemon is not running, it
+ is started. This differs from a normal restart
+ in that currently open connections are not
+ aborted. A side effect is that old log files
+ will not be closed immediately. This means that
+ if used in a log rotation script, a substantial
+ delay may be necessary to ensure that the old
+ log files are closed before processing them.
+ This command automatically checks the configura-
+ tion files via <strong>configtest </strong>before initiating the
+ restart to make sure Apache doesn't die.
+
+ <strong>configtest </strong>Run a configuration file syntax test. It parses
+ the configuration files and either reports <strong>Syn-</strong>
+ <strong>tax Ok </strong>or detailed information about the partic-
+ ular syntax error.
+
+ <strong>help </strong>Displays a short help message.
+
+<strong>SEE ALSO</strong>
+ <strong>httpd(8)</strong>
+
+</pre>
+<!--#include virtual="footer.html" -->
+</BODY></HTML>
diff --git a/docs/manual/programs/apxs.html b/docs/manual/programs/apxs.html
new file mode 100644
index 0000000000..14ff1965c9
--- /dev/null
+++ b/docs/manual/programs/apxs.html
@@ -0,0 +1,269 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD><TITLE>Manual Page: apxs - Apache HTTP Server</TITLE></HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF"
+VLINK="#000080" ALINK="#FF0000">
+<!--#include virtual="header.html" -->
+<!-- This document was autogenerated from the man page -->
+<pre>
+<strong>NAME</strong>
+ apxs - APache eXtenSion tool
+
+<strong>SYNOPSIS</strong>
+ <strong>apxs </strong>-<strong>g </strong>[ -<strong>S </strong><em>name</em>=<em>value </em>] -<strong>n </strong><em>modname</em>
+
+ <strong>apxs </strong>-<strong>q </strong>[ -<strong>S </strong><em>name</em>=<em>value </em>] <em>query </em>...
+
+ <strong>apxs </strong>-<strong>c </strong>[ -<strong>S </strong><em>name</em>=<em>value </em>] [ -<strong>o </strong><em>dsofile </em>] [ -<strong>I </strong><em>incdir </em>] [ -<strong>D</strong>
+ <em>name</em>=<em>value </em>] [ -<strong>L </strong><em>libdir </em>] [ -<strong>l </strong><em>libname </em>] [ -<strong>Wc,</strong><em>compiler</em>-
+ <em>flags </em>] [ -<strong>Wl,</strong><em>linker</em>-<em>flags </em>] <em>files </em>...
+
+ <strong>apxs </strong>-<strong>i </strong>[ -<strong>S </strong><em>name</em>=<em>value </em>] [ -<strong>n </strong><em>modname </em>] [ -<strong>a </strong>] [ -<strong>A </strong>] <em>dso-</em>
+ <em>file </em>...
+
+ <strong>apxs </strong>-<strong>e </strong>[ -<strong>S </strong><em>name</em>=<em>value </em>] [ -<strong>n </strong><em>modname </em>] [ -<strong>a </strong>] [ -<strong>A </strong>] <em>dso-</em>
+ <em>file </em>...
+
+<strong>DESCRIPTION</strong>
+ <strong>apxs </strong>is a tool for building and installing extension modules
+ for the Apache HyperText Transfer Protocol (HTTP) server.
+ This is achieved by building a dynamic shared object (DSO)
+ from one or more source or object <em>files </em>which then can be
+ loaded into the Apache server under runtime via the <strong>LoadMo-</strong>
+ <strong>dule </strong>directive from <strong>mod_so.</strong>
+
+ So to use this extension mechanism your platform has to sup-
+ port the DSO feature and your Apache <strong>httpd </strong>binary has to be
+ built with the <strong>mod_so </strong>module. The <strong>apxs </strong>tool automatically
+ complains if this is not the case. You can check this your-
+ self by manually running the command
+
+ $ httpd -l
+
+ The module <strong>mod_so </strong>should be part of the displayed list. If
+ these requirements are fulfilled you can easily extend your
+ Apache server's functionality by installing your own modules
+ with the DSO mechanism by the help of this <strong>apxs </strong>tool:
+
+ $ apxs -i -a -c mod_foo.c
+ gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c
+ ld -Bshareable -o mod_foo.so mod_foo.o
+ cp mod_foo.so /path/to/apache/libexec/mod_foo.so
+ chmod 755 /path/to/apache/libexec/mod_foo.so
+ [activating module `foo' in /path/to/apache/etc/httpd.conf]
+ $ apachectl restart
+ /path/to/apache/sbin/apachectl restart: httpd not running, trying to start
+ [Tue Mar 31 11:27:55 1998] [debug] mod_so.c(303): loaded module foo_module
+ /path/to/apache/sbin/apachectl restart: httpd started
+ $ _
+
+ The arguments <em>files </em>can be any C source file (.c), a object
+ file (.o) or even a library archive (.a). The <strong>apxs </strong>tool
+ automatically recognizes these extensions and automtaically
+ used the C source files for compilation while just using the
+ object and archive files for the linking phase. But when
+ using such pre-compiled objects make sure they are compiled
+ for position independend code (PIC) to be able to use them
+ for a dynamically loaded shared object. For instance with
+ GCC you always just have to use <strong>-fpic</strong>. For other C com-
+ pilers consult its manual page or at watch for the flags
+ <strong>apxs </strong>uses to compile the object files.
+
+ For more details about DSO support in Apache read the docu-
+ mentation of <strong>mod_so </strong>or perhaps even read the
+ <strong>src/modules/standard/mod_so.c </strong>source file.
+
+<strong>OPTIONS</strong>
+ Common options:
+
+ -<strong>n </strong><em>modname </em>This explicitly sets the module name for the -<strong>i</strong>
+ (install) and -<strong>g </strong>(template generation) option.
+ Use this to explicitly specify the module name.
+ For option -<strong>g </strong>this is required, for option -<strong>i</strong>
+ the <strong>apxs </strong>tool tries to determine the name from
+ the source or (as a fallback) at least by guess-
+ ing it from the filename.
+
+ Query options:
+
+ -<strong>q </strong>Performs a query for <strong>apxs</strong>'s knowledge about cer-
+ tain settings. The <em>query </em>parameters can be one
+ or more of the following strings:
+ CC TARGET
+ CFLAGS SBINDIR
+ CFLAGS_SHLIB INCLUDEDIR
+ LD_SHLIB LIBEXECDIR
+ LDFLAGS_SHLIB SYSCONFDIR
+ LIBS_SHLIB
+ Use this for manually determining settings. For
+ instance use
+ INC=-I`apxs -q INCLUDEDIR`
+ inside your own Makefiles if you need manual
+ access to Apache's C header files.
+
+ Configuration options:
+
+ -<strong>S </strong><em>name</em>=<em>value</em>
+ This option changes the apxs settings described
+ above.
+
+ Template Generation options:
+
+ -<strong>g </strong>This generates a subdirectory <em>name </em>(see option
+ -<strong>n</strong>) and there two files: A sample module source
+ file named <strong>mod_</strong><em>name</em>.<em>c </em>which can be used as a
+ template for creating your own modules or as a
+ quick start for playing with the APXS mechanism.
+ And a corresponding <strong>Makefile </strong>for even easier
+ build and installing of this module.
+
+ DSO compilation options:
+
+ -<strong>c </strong>This indicates the compilation operation. It
+ first compiles the C source files (.c) of <em>files</em>
+ into corresponding object files (.o) and then
+ builds a dynamically shared object in <em>dsofile </em>by
+ linking these object files plus the remaining
+ object files (.o and .a) of <em>files </em>If no -<strong>o</strong>
+ option is specified the output file is guessed
+ from the first filename in <em>files </em>and thus usu-
+ ally defaults to <strong>mod_</strong><em>name</em>.<em>so</em>
+
+ -<strong>o </strong><em>dsofile </em>Explicitly specifies the filename of the created
+ dynamically shared object. If not specified and
+ the name cannot be guessed from the <em>files </em>list,
+ the fallback name <strong>mod_unknown.so </strong>is used.
+
+ -<strong>D </strong><em>name</em>=<em>value</em>
+ This option is directly passed through to the
+ compilation command(s). Use this to add your
+ own defines to the build process.
+
+ -<strong>I </strong><em>incdir </em>This option is directly passed through to the
+ compilation command(s). Use this to add your
+ own include directories to search to the build
+ process.
+
+ -<strong>L </strong><em>libdir </em>This option is directly passed through to the
+ linker command. Use this to add your own
+ library directories to search to the build pro-
+ cess.
+
+ -<strong>l </strong><em>libname </em>This option is directly passed through to the
+ linker command. Use this to add your own
+ libraries to search to the build process.
+
+ -<strong>Wc,</strong><em>compiler</em>-<em>flags</em>
+ This option passes <em>compiler</em>-<em>flags </em>as additional
+ flags to the compiler command. Use this to add
+ local compiler-specific options.
+
+ -<strong>Wl,</strong><em>linker</em>-<em>flags</em>
+ This option passes <em>linker</em>-<em>flags </em>as additional
+ flags to the linker command. Use this to add
+ local linker-specific options.
+
+ DSO installation and configuration options:
+
+ -<strong>i </strong>This indicates the installation operation and
+ installs one or more dynamically shared objects
+ into the server's <em>libexec </em>directory.
+
+ -<strong>a </strong>This activates the module by automatically
+ adding a corresponding <strong>LoadModule </strong>line to
+ Apache's <strong>httpd.conf </strong>configuration file, or by
+ enabling it if it already exists.
+
+ -<strong>A </strong>Same as option -<strong>a </strong>but the created <strong>LoadModule</strong>
+ directive is prefixed with a hash sign (#), i.e.
+ the module is just prepared for later activation
+ but initially disabled.
+
+ -<strong>e </strong>This indicates the editing operation, which can
+ be used with the -<strong>a </strong>and -<strong>A </strong>options similarly to
+ the -<strong>i </strong>operation to edit Apache's <strong>httpd.conf</strong>
+ configuration file without attempting to install
+ the module.
+
+<strong>EXAMPLES</strong>
+ Assume you have an Apache module named mod_foo.c available
+ which should extend Apache's server functionality. To accom-
+ plish this you first have to compile the C source into a
+ shared object suitable for loading into the Apache server
+ under runtime via the following command:
+
+ $ apxs -c mod_foo.c
+ gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c
+ ld -Bshareable -o mod_foo.so mod_foo.o
+ $ _
+
+ Then you have to update the Apache configuration by making
+ sure a <strong>LoadModule </strong>directive is present to load this shared
+ object. To simplify this step <strong>apxs </strong>provides an automatic way
+ to install the shared object in its "libexec" directory and
+ updating the <strong>httpd.conf </strong>file accordingly. This can be
+ achieved by running:
+
+ $ apxs -i -a mod_foo.c
+ cp mod_foo.so /path/to/apache/libexec/mod_foo.so
+ chmod 755 /path/to/apache/libexec/mod_foo.so
+ [activating module `foo' in /path/to/apache/etc/httpd.conf]
+ $ _
+
+ This way a line named
+
+ LoadModule foo_module libexec/mod_foo.so
+
+ is added to the configuration file if still not present. If
+ you want to have this this disabled per default use the -<strong>A</strong>
+ option, i.e.
+
+ $ apxs -i -A mod_foo.c
+
+ For a quick test of the APXS mechanism you can create a sam-
+ ple Apache module template plus a corresponding Makefile
+ via:
+
+ $ apxs -g -n foo
+ Creating [DIR] foo
+ Creating [FILE] foo/Makefile
+ Creating [FILE] foo/mod_foo.c
+ $ _
+
+ Then you can immediately compile this sample module into a
+ shared object and load it into the Apache server:
+
+ $ cd foo
+ $ make all reload
+ apxs -c mod_foo.c
+ gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c
+ ld -Bshareable -o mod_foo.so mod_foo.o
+ apxs -i -a -n "foo" mod_foo.so
+ cp mod_foo.so /path/to/apache/libexec/mod_foo.so
+ chmod 755 /path/to/apache/libexec/mod_foo.so
+ [activating module `foo' in /path/to/apache/etc/httpd.conf]
+ apachectl restart
+ /path/to/apache/sbin/apachectl restart: httpd not running, trying to start
+ [Tue Mar 31 11:27:55 1998] [debug] mod_so.c(303): loaded module foo_module
+ /path/to/apache/sbin/apachectl restart: httpd started
+ $ _
+
+ You can even use <strong>apxs </strong>to compile complex modules outside the
+ Apache source tree, like PHP3:
+
+ $ cd php3
+ $ ./configure --with-shared-apache=../apache-1.3
+ $ apxs -c -o libphp3.so mod_php3.c libmodphp3-so.a
+ gcc -fpic -DSHARED_MODULE -I/tmp/apache/include -c mod_php3.c
+ ld -Bshareable -o libphp3.so mod_php3.o libmodphp3-so.a
+ $ _
+
+ because <strong>apxs </strong>automatically recognized C source files and
+ object files. Only C source files are compiled while
+ remaining object files are used for the linking phase.
+
+<strong>SEE ALSO</strong>
+ <strong>apachectl(1), httpd(8).</strong>
+
+</pre>
+<!--#include virtual="footer.html" -->
+</BODY></HTML>
diff --git a/docs/manual/programs/dbmmanage.html b/docs/manual/programs/dbmmanage.html
new file mode 100644
index 0000000000..da860698f5
--- /dev/null
+++ b/docs/manual/programs/dbmmanage.html
@@ -0,0 +1,103 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD><TITLE>Manual Page: dbmmanage - Apache HTTP Server</TITLE></HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF"
+VLINK="#000080" ALINK="#FF0000">
+<!--#include virtual="header.html" -->
+<!-- This document was autogenerated from the man page -->
+<pre>
+<strong>NAME</strong>
+ dbmmanage - Create and update user authentication files in
+ DBM format
+
+<strong>SYNOPSIS</strong>
+ <strong>dbmmanage </strong><em>filename </em>[ <em>command </em>] [ <em>username </em>[ <em>encpasswd </em>] ]
+
+<strong>DESCRIPTION</strong>
+ <strong>dbmmanage </strong>is used to create and update the DBM format files
+ used to store usernames and password for basic authentica-
+ tion of HTTP users. Resources available from the <strong>httpd</strong>
+ Apache web server can be restricted to just the users listed
+ in the files created by <strong>dbmmanage. </strong>This program can only be
+ used when the usernames are stored in a DBM file. To use a
+ flat-file database see <strong>htpasswd</strong>.
+
+ This manual page only lists the command line arguments. For
+ details of the directives necessary to configure user
+ authentication in <strong>httpd </strong>see the Apache manual, which is part
+ of the Apache distribution or can be found at
+ http://www.apache.org/.
+
+<strong>OPTIONS</strong>
+ <em>filename</em>
+ The filename of the DBM format file. Usually without
+ the extension .db, .pag, or .dir.
+
+ <em>command</em>
+ This selects the operation to perform:
+
+ <strong>add </strong>Adds an entry for <em>username </em>to <em>filename </em>using the
+ encrypted password <em>encpassword</em>.
+
+ <strong>adduser </strong>Asks for a password and then adds an entry for
+ <em>username </em>to <em>filename </em>.
+
+ <strong>check </strong>Asks for a password and then checks if <em>username</em>
+ is in <em>filename </em>and if it's password matches the
+ specified one.
+
+ <strong>delete </strong>Deletes the <em>username </em>entry from <em>filename</em>.
+
+ <strong>import </strong>Reads username:password entries (one per line)
+ from STDIN and adds them to <em>filename</em>. The pass-
+ words already has to be crypted.
+
+ <strong>update </strong>Same as the "adduser" command, except that it
+ makes sure <em>username </em>already exists in <em>filename</em>.
+
+ <strong>view </strong>Just displays the complete contents of the DBM
+ file.
+
+ <em>username </em>The user for which the update operation is per-
+ formed.
+
+<strong>BUGS</strong>
+ One should be aware that there are a number of different DBM
+ file formats in existance, and with all likelihood,
+ libraries for more than one format may exist on your system.
+ The three primary examples are NDBM, the GNU project's GDBM,
+ and Berkeley DB 2. Unfortunately, all these libraries use
+ different file formats, and you must make sure that the file
+ format used by <em>filename </em>is the same format that <strong>dbmmanage</strong>
+ expects to see. <strong>dbmmanage </strong>currently has no way of determin-
+ ing what type of DBM file it is looking at. If used against
+ the wrong format, will simply return nothing, or may create
+ a different DBM file with a different name, or at worst, it
+ may corrupt the DBM file if you were attempting to write to
+ it.
+
+ <strong>dbmmanage </strong>has a list of DBM format preferences, defined by
+ the <strong>@AnyDBM::ISA </strong>array near the beginning of the program.
+ Since we prefer the Berkeley DB 2 file format, the order in
+ which <strong>dbmmanage </strong>will look for system libraries is Berkeley
+ DB 2, then NDBM, and then GDBM. The first library found
+ will be the library <strong>dbmmanage </strong>will attempt to use for all
+ DBM file transactions. This ordering is slightly different
+ than the standard <strong>@AnyDBM::ISA </strong>ordering in perl, as well as
+ the ordering used by the simple dbmopen() call in Perl, so
+ if you use any other utilities to manage your DBM files,
+ they must also follow this preference ordering. Similar
+ care must be taken if using programs in other languages,
+ like C, to access these files.
+
+ Apache's <strong>mod_auth_db.c </strong>module corresponds to Berkeley DB 2
+ library, while <strong>mod_auth_dbm.c </strong>corresponds to the NDBM
+ library. Also, one can usually use the <strong>file </strong>program sup-
+ plied with most Unix systems to see what format a DBM file
+ is in.
+
+<strong>SEE ALSO</strong>
+ <strong>httpd(8)</strong>
+
+</pre>
+<!--#include virtual="footer.html" -->
+</BODY></HTML>
diff --git a/docs/manual/programs/htdigest.html b/docs/manual/programs/htdigest.html
new file mode 100644
index 0000000000..1891e0d066
--- /dev/null
+++ b/docs/manual/programs/htdigest.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD><TITLE>Manual Page: htdigest - Apache HTTP Server</TITLE></HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF"
+VLINK="#000080" ALINK="#FF0000">
+<!--#include virtual="header.html" -->
+<!-- This document was autogenerated from the man page -->
+<pre>
+<strong>NAME</strong>
+ htdigest - Create and update user authentication files
+
+<strong>SYNOPSIS</strong>
+ <strong>htdigest </strong>[ -<strong>c </strong>] <em>passwdfile realm username</em>
+
+<strong>DESCRIPTION</strong>
+ <strong>htdigest </strong>is used to create and update the flat-files used to
+ store usernames, realm and password for digest authentica-
+ tion of HTTP users. Resources available from the <strong>httpd</strong>
+ Apache web server can be restricted to just the users listed
+ in the files created by <strong>htdigest.</strong>
+
+ This manual page only lists the command line arguments. For
+ details of the directives necessary to configure digest
+ authentication in <strong>httpd </strong>see the Apache manual, which is part
+ of the Apache distribution or can be found at
+ http://www.apache.org/.
+
+<strong>OPTIONS</strong>
+ -c Create the <em>passwdfile</em>. If <em>passwdfile </em>already exists, it
+ is deleted first.
+
+ <em>passwdfile</em>
+ Name of the file to contain the username, realm and
+ password. If -c is given, this file is created if it
+ does not already exist, or deleted and recreated if it
+ does exist.
+
+ <em>realm</em>
+ The realm name to which the user name belongs.
+
+ <em>username</em>
+ The user name to create or update in <strong>passwdfile</strong>. If
+ <em>username </em>does not exist is this file, an entry is
+ added. If it does exist, the password is changed.
+
+<strong>SEE ALSO</strong>
+ <strong>httpd(8)</strong>
+
+</pre>
+<!--#include virtual="footer.html" -->
+</BODY></HTML>
diff --git a/docs/manual/programs/htpasswd.html b/docs/manual/programs/htpasswd.html
new file mode 100644
index 0000000000..6c95b6d77e
--- /dev/null
+++ b/docs/manual/programs/htpasswd.html
@@ -0,0 +1,158 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD><TITLE>Manual Page: htpasswd - Apache HTTP Server</TITLE></HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF"
+VLINK="#000080" ALINK="#FF0000">
+<!--#include virtual="header.html" -->
+<!-- This document was autogenerated from the man page -->
+<pre>
+<strong>NAME</strong>
+ htpasswd - Create and update user authentication files
+
+<strong>SYNOPSIS</strong>
+ <strong>htpasswd </strong>[ -<strong>c </strong>] [ -<strong>m </strong>] <em>passwdfile username</em>
+ <strong>htpasswd </strong>-<strong>b </strong>[ -<strong>c </strong>] [ -<strong>m </strong>| -<strong>d </strong>| -<strong>p </strong>| -<strong>s </strong>] <em>passwdfile username</em>
+ <em>password</em>
+ <strong>htpasswd </strong>-<strong>n </strong>[ -<strong>m </strong>| -<strong>d </strong>| -<strong>s </strong>| -<strong>p </strong>] <em>username</em>
+ <strong>htpasswd </strong>-<strong>nb </strong>[ -<strong>m </strong>| -<strong>d </strong>| -<strong>s </strong>| -<strong>p </strong>] <em>username password</em>
+
+<strong>DESCRIPTION</strong>
+ <strong>htpasswd </strong>is used to create and update the flat-files used to
+ store usernames and password for basic authentication of
+ HTTP users. If <strong>htpasswd </strong>cannot access a file, such as not
+ being able to write to the output file or not being able to
+ read the file in order to update it, it returns an error
+ status and makes no changes.
+
+ Resources available from the <strong>httpd </strong>Apache web server can be
+ restricted to just the users listed in the files created by
+ <strong>htpasswd. </strong>This program can only manage usernames and pass-
+ words stored in a flat-file. It can encrypt and display
+ password information for use in other types of data stores,
+ though. To use a DBM database see <strong>dbmmanage</strong>.
+
+ <strong>htpasswd </strong>encrypts passwords using either a version of MD5
+ modified for Apache, or the system's <em>crypt</em>() routine. Files
+ managed by <strong>htpasswd </strong>may contain both types of passwords;
+ some user records may have MD5-encrypted passwords while
+ others in the same file may have passwords encrypted with
+ <em>crypt</em>().
+
+ This manual page only lists the command line arguments. For
+ details of the directives necessary to configure user
+ authentication in <strong>httpd </strong>see the Apache manual, which is part
+ of the Apache distribution or can be found at
+ &lt;URL:http://www.apache.org/&gt;.
+
+<strong>OPTIONS</strong>
+ -b Use batch mode; <em>i</em>.<em>e</em>., get the password from the command
+ line rather than prompting for it. <strong>This option should</strong>
+ <strong>be used with extreme care, since the password is</strong>
+ <strong>clearly visible on the command line.</strong>
+
+ -c Create the <em>passwdfile</em>. If <em>passwdfile </em>already exists, it
+ is rewritten and truncated. This option cannot be com-
+ bined with the <strong>-n </strong>option.
+
+ -n Display the results on standard output rather than
+ updating a file. This is useful for generating pass-
+ word records acceptable to Apache for inclusion in
+ non-text data stores. This option changes the syntax
+ of the command line, since the <em>passwdfile </em>argument
+ (usually the first one) is omitted. It cannot be com-
+ bined with the <strong>-c </strong>option.
+
+ -m Use MD5 encryption for passwords. On Windows and TPF,
+ this is the default.
+
+ -d Use crypt() encryption for passwords. The default on
+ all platforms but Windows and TPF. Though possibly sup-
+ ported by <strong>htpasswd </strong>on all platforms, it is not sup-
+ ported by the <strong>httpd </strong>server on Windows and TPF.
+
+ -s Use SHA encryption for passwords. Faciliates migration
+ from/to Netscape servers using the LDAP Directory
+ Interchange Format (ldif).
+
+ -p Use plaintext passwords. Though <strong>htpasswd </strong>will support
+ creation on all platofrms, the <strong>httpd </strong>deamon will only
+ accept plain text passwords on Windows and TPF.
+
+ <em>passwdfile</em>
+ Name of the file to contain the user name and password.
+ If -c is given, this file is created if it does not
+ already exist, or rewritten and truncated if it does
+ exist.
+
+ <em>username</em>
+ The username to create or update in <strong>passwdfile</strong>. If
+ <em>username </em>does not exist in this file, an entry is
+ added. If it does exist, the password is changed.
+
+ <em>password</em>
+ The plaintext password to be encrypted and stored in
+ the file. Only used with the -<em>b </em>flag.
+
+<strong>EXIT STATUS</strong>
+ <strong>htpasswd </strong>returns a zero status ("true") if the username and
+ password have been successfully added or updated in the
+ <em>passwdfile</em>. <strong>htpasswd </strong>returns 1 if it encounters some prob-
+ lem accessing files, 2 if there was a syntax problem with
+ the command line, 3 if the password was entered interac-
+ tively and the verification entry didn't match, 4 if its
+ operation was interrupted, 5 if a value is too long (user-
+ name, filename, password, or final computed record), and 6
+ if the username contains illegal characters (see the <strong>RES-</strong>
+ <strong>TRICTIONS </strong>section).
+
+<strong>EXAMPLES</strong>
+ <strong>htpasswd /usr/local/etc/apache/.htpasswd-users jsmith</strong>
+
+ Adds or modifies the password for user <em>jsmith</em>. The user
+ is prompted for the password. If executed on a Windows
+ system, the password will be encrypted using the modi-
+ fied Apache MD5 algorithm; otherwise, the system's
+ <em>crypt</em>() routine will be used. If the file does not
+ exist, <strong>htpasswd </strong>will do nothing except return an error.
+
+ <strong>htpasswd -c /home/doe/public_html/.htpasswd jane</strong>
+
+ Creates a new file and stores a record in it for user
+ <em>jane</em>. The user is prompted for the password. If the
+ file exists and cannot be read, or cannot be written,
+ it is not altered and <strong>htpasswd </strong>will display a message
+ and return an error status.
+
+ <strong>htpasswd -mb /usr/web/.htpasswd-all jones Pwd4Steve</strong>
+
+ Encrypts the password from the command line (<em>Pwd4Steve</em>)
+ using the MD5 algorithm, and stores it in the specified
+ file.
+
+<strong>SECURITY CONSIDERATIONS</strong>
+ Web password files such as those managed by <strong>htpasswd </strong>should
+ <strong>not </strong>be within the Web server's URI space -- that is, they
+ should not be fetchable with a browser.
+
+ The use of the -<em>b </em>option is discouraged, since when it is
+ used the unencrypted password appears on the command line.
+
+<strong>RESTRICTIONS</strong>
+ On the Windows and MPE platforms, passwords encrypted with
+ <strong>htpasswd </strong>are limited to no more than 255 characters in
+ length. Longer passwords will be truncated to 255 charac-
+ ters.
+
+ The MD5 algorithm used by <strong>htpasswd </strong>is specific to the Apache
+ software; passwords encrypted using it will not be usable
+ with other Web servers.
+
+ Usernames are limited to 255 bytes and may not include the
+ character ':'.
+
+<strong>SEE ALSO</strong>
+ <strong>httpd(8) </strong>and the scripts in support/SHA1 which come with the
+ distribution.
+
+</pre>
+<!--#include virtual="footer.html" -->
+</BODY></HTML>
diff --git a/docs/manual/programs/httpd.html b/docs/manual/programs/httpd.html
new file mode 100644
index 0000000000..ff5a16f462
--- /dev/null
+++ b/docs/manual/programs/httpd.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD><TITLE>Manual Page: httpd - Apache HTTP Server</TITLE></HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF"
+VLINK="#000080" ALINK="#FF0000">
+<!--#include virtual="header.html" -->
+<!-- This document was autogenerated from the man page -->
+<pre>
+<strong>NAME</strong>
+ httpd - Apache hypertext transfer protocol server
+
+<strong>SYNOPSIS</strong>
+ <strong>httpd </strong>[ -<strong>X </strong>] [ -<strong>R </strong><em>libexecdir </em>] [ -<strong>d </strong><em>serverroot </em>] [ -<strong>f </strong><em>config</em>
+ ] [ -<strong>C </strong><em>directive </em>] [ -<strong>c </strong><em>directive </em>] [ -<strong>D </strong><em>parameter </em>]
+
+ <strong>httpd </strong>[ -<strong>h </strong>] [ -<strong>l </strong>] [ -<strong>L </strong>] [ -<strong>v </strong>] [ -<strong>V </strong>] [ -<strong>S </strong>] [ -<strong>t </strong>] [ -<strong>T</strong>
+ ]
+
+<strong>DESCRIPTION</strong>
+ <strong>httpd </strong>is the Apache HyperText Transfer Protocol (HTTP)
+ server program. It is designed to be run as a standalone
+ daemon process. When used like this it will create a pool of
+ child processes to handle requests. To stop it, send a TERM
+ signal to the initial (parent) process. The PID of this pro-
+ cess is written to a file as given in the configuration
+ file. Alternatively <strong>httpd </strong>may be invoked by the Internet
+ daemon inetd(8) each time a connection to the HTTP service
+ is made.
+
+ This manual page only lists the command line arguments. For
+ details of the directives necessary to configure <strong>httpd </strong>see
+ the Apache manual, which is part of the Apache distribution
+ or can be found at http://www.apache.org/. Paths in this
+ manual may not reflect those compiled into <strong>httpd.</strong>
+
+<strong>OPTIONS</strong>
+ -<strong>R </strong><em>libexecdir</em>
+ This option is only available if Apache was
+ built with the <em>SHARED</em>_<em>CORE </em>rule enabled which
+ forces the Apache core code to be placed into a
+ dynamic shared object (DSO) file. This file is
+ searched in a hardcoded path under ServerRoot
+ per default. Use this option if you want to
+ override it.
+
+ -<strong>d </strong><em>serverroot</em>
+ Set the initial value for the ServerRoot direc-
+ tive to <em>serverroot</em>. This can be overridden by
+ the ServerRoot command in the configuration
+ file. The default is <strong>/usr/local/apache</strong>.
+
+ -<strong>f </strong><em>config </em>Execute the commands in the file <em>config </em>on
+ startup. If <em>config </em>does not begin with a /, then
+ it is taken to be a path relative to the Server-
+ Root. The default is <strong>conf/httpd.conf</strong>.
+
+ -<strong>C </strong><em>directive</em>
+ Process the configuration <em>directive </em>before read-
+ ing config files.
+
+ -<strong>c </strong><em>directive</em>
+ Process the configuration <em>directive </em>after read-
+ ing config files.
+
+ -<strong>D </strong><em>parameter</em>
+ Sets a configuration <em>parameter </em>which can be used
+ with &lt;IfDefine&gt;...&lt;/IfDefine&gt; sections in the
+ configuration files to conditionally skip or
+ process commands.
+
+ -<strong>h </strong>Output a short summary of available command line
+ options.
+
+ -<strong>l </strong>Output a list of modules compiled into the
+ server.
+
+ -<strong>L </strong>Output a list of directives together with
+ expected arguments and places where the direc-
+ tive is valid.
+
+ -<strong>S </strong>Show the settings as parsed from the config file
+ (currently only shows the virtualhost settings).
+
+ -<strong>t </strong>Run syntax tests for configuration files only.
+ The program immediately exits after these syntax
+ parsing with either a return code of 0 (Syntax
+ OK) or return code not equal to 0 (Syntax
+ Error).
+
+ -<strong>T </strong>Same as option -<strong>t </strong>but does not check the config-
+ ured document roots.
+
+ -<strong>X </strong>Run in single-process mode, for internal debug-
+ ging purposes only; the daemon does not detach
+ from the terminal or fork any children. Do NOT
+ use this mode to provide ordinary web service.
+
+ -<strong>v </strong>Print the version of <strong>httpd </strong>, and then exit.
+
+ -<strong>V </strong>Print the version and build parameters of <strong>httpd</strong>
+ , and then exit.
+
+<strong>FILES</strong>
+ <strong>/usr/local/apache/conf/httpd.conf</strong>
+ <strong>/usr/local/apache/conf/srm.conf</strong>
+ <strong>/usr/local/apache/conf/access.conf</strong>
+ <strong>/usr/local/apache/conf/mime.types</strong>
+ <strong>/usr/local/apache/conf/magic</strong>
+ <strong>/usr/local/apache/logs/error_log</strong>
+ <strong>/usr/local/apache/logs/access_log</strong>
+ <strong>/usr/local/apache/logs/httpd.pid</strong>
+
+<strong>SEE ALSO</strong>
+ <strong>inetd</strong>(8).
+
+</pre>
+<!--#include virtual="footer.html" -->
+</BODY></HTML>
diff --git a/docs/manual/programs/index.html b/docs/manual/programs/index.html
new file mode 100755
index 0000000000..3e246aae4b
--- /dev/null
+++ b/docs/manual/programs/index.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD>
+ <TITLE>Apache HTTP Server and Supporting Programs</TITLE>
+ </HEAD>
+
+ <!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+ <BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+ >
+ <!--#include virtual="header.html" -->
+ <H1 ALIGN="CENTER">Server and Supporting Programs</H1>
+
+<p>This page documents all the executable programs included with the
+Apache HTTP Server.</p>
+
+<dl>
+
+<dt><a href="httpd.html">httpd</a></dt>
+<dd>Apache hypertext transfer protocol server</dd>
+
+<dt><a href="apachectl.html">apachectl</a></dt>
+<dd>Apache HTTP server control interface</dd>
+
+<dt><a href="ab.html">ab</a></dt>
+<dd>Apache HTTP server benchmarking tool</dd>
+
+<dt><a href="apxs.html">apxs</a></dt>
+<dd>APache eXtenSion tool</dd>
+
+<dt><a href="dbmmanage.html">dbmmanage</a></dt>
+<dd>Create and update user authentication files in DBM format for basic
+authentication</dd>
+
+<dt><a href="htdigest.html">htdigest</a></dt>
+<dd>Create and update user authentication files for digest authentication</dd>
+
+<dt><a href="htpasswd.html">htpasswd</a></dt>
+<dd>Create and update user authentication files for basic authentication</dd>
+
+<dt><a href="logresolve.html">logresolve</a></dt>
+<dd>Resolve hostnames for IP-addresses in Apache logfiles</dd>
+
+<dt><a href="rotatelogs.html">rotatelogs</a></dt>
+<dd>Rotate Apache logs without having to kill the server</dd>
+
+<dt><a href="suexec.html">suexec</a></dt>
+<dd>Switch User For Exec</dd>
+
+<dt><a href="other.html">Other Programs</dt>
+
+</dl>
+
+ <!--#include virtual="footer.html" -->
+ </BODY>
+</HTML>
diff --git a/docs/manual/programs/logresolve.html b/docs/manual/programs/logresolve.html
new file mode 100644
index 0000000000..32efc42934
--- /dev/null
+++ b/docs/manual/programs/logresolve.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD><TITLE>Manual Page: logresolve - Apache HTTP Server</TITLE></HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF"
+VLINK="#000080" ALINK="#FF0000">
+<!--#include virtual="header.html" -->
+<!-- This document was autogenerated from the man page -->
+<pre>
+
+<strong>NAME</strong>
+ logresolve - resolve hostnames for IP-adresses in Apache
+ logfiles
+
+<strong>SYNOPSIS</strong>
+ <strong>logresolve </strong>[ -<strong>s </strong><em>filename </em>] [ -<strong>c </strong>] &lt; <em>access</em>_<em>log </em>&gt;
+ <em>access</em>_<em>log</em>.<em>new</em>
+
+<strong>DESCRIPTION</strong>
+ <strong>logresolve </strong>is a post-processing program to resolve IP-
+ adresses in Apache's access logfiles. To minimize impact on
+ your nameserver, logresolve has its very own internal hash-
+ table cache. This means that each IP number will only be
+ looked up the first time it is found in the log file.
+
+<strong>OPTIONS</strong>
+ -<strong>s </strong><em>filename </em>Specifies a filename to record statistics.
+
+ -<strong>c </strong>This causes <strong>logresolve </strong>to apply some DNS checks:
+ after finding the hostname from the IP address,
+ it looks up the IP addresses for the hostname
+ and checks that one of these matches the origi-
+ nal address.
+
+<strong>SEE ALSO</strong>
+ <strong>httpd(8)</strong>
+
+</pre>
+<!--#include virtual="footer.html" -->
+</BODY></HTML>
diff --git a/docs/manual/programs/other.html b/docs/manual/programs/other.html
new file mode 100755
index 0000000000..e80e8a2d9b
--- /dev/null
+++ b/docs/manual/programs/other.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD>
+ <TITLE>Other Programs - Apache HTTP Server</TITLE>
+ </HEAD>
+
+ <!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+ <BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+ >
+ <!--#include virtual="header.html" -->
+ <H1 ALIGN="CENTER">Other Programs</H1>
+
+<p>The following programs are simple support programs included with
+the Apache HTTP Server which do not have their own manual pages.</p>
+
+
+<h2><a name="log_server_status">log_server_status</a></h2>
+
+<p>This Perl script is designed to be run at a frequent interval by something
+like cron. It connects to the server and downloads the status
+information. It reformats the information to a single line and logs
+it to a file. Adjust the variables at the top of the script
+to specify the location of the resulting logfile.</p>
+
+<h2><a name="split-logfile">split-logfile</a></h2>
+
+<p>This Perl script will take a combined Web server access
+log file and break its contents into separate files.
+It assumes that the first field of each line is the
+virtual host identity (put there by "%v"), and that
+the logfiles should be named that+".log" in the current
+directory.</p>
+
+<p>The combined log file is read from stdin. Records read
+will be appended to any existing log files.</p>
+
+
+
+</dl>
+
+ <!--#include virtual="footer.html" -->
+ </BODY>
+</HTML>
diff --git a/docs/manual/programs/rotatelogs.html b/docs/manual/programs/rotatelogs.html
new file mode 100644
index 0000000000..70d2295110
--- /dev/null
+++ b/docs/manual/programs/rotatelogs.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD><TITLE>Manual Page: rotatelogs - Apache HTTP Server</TITLE></HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF"
+VLINK="#000080" ALINK="#FF0000">
+<!--#include virtual="header.html" -->
+<!-- This document was autogenerated from the man page -->
+<pre>
+
+<strong>NAME</strong>
+ rotatelogs - rotate Apache logs without having to kill the
+ server
+
+<strong>SYNOPSIS</strong>
+ <strong>rotatelogs </strong><em>logfile rotationtime</em>
+
+<strong>DESCRIPTION</strong>
+ <strong>rotatelogs </strong>is a simple program for use in conjunction with
+ Apache's piped logfile feature which can be used like this:
+
+ TransferLog "|rotatelogs /path/to/logs/access_log 86400"
+
+ This creates the files /path/to/logs/access_log.nnnn where
+ nnnn is the system time at which the log nominally starts
+ (this time will always be a multiple of the rotation time,
+ so you can synchronize cron scripts with it). At the end of
+ each rotation time (here after 24 hours) a new log is
+ started.
+
+<strong>OPTIONS</strong>
+ <em>logfile</em>
+ The path plus basename of the logfile. The suffix .nnnn
+ is automatically added.
+
+ <em>rotationtime</em>
+ The rotation time in seconds.
+
+<strong>SEE ALSO</strong>
+ <strong>httpd(8)</strong>
+
+</pre>
+<!--#include virtual="footer.html" -->
+</BODY></HTML>
diff --git a/docs/manual/programs/suexec.html b/docs/manual/programs/suexec.html
new file mode 100644
index 0000000000..f9f9b76a63
--- /dev/null
+++ b/docs/manual/programs/suexec.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD><TITLE>Manual Page: suexec - Apache HTTP Server</TITLE></HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF"
+VLINK="#000080" ALINK="#FF0000">
+<!--#include virtual="header.html" -->
+<!-- This document was autogenerated from the man page -->
+<pre>
+
+<strong>NAME</strong>
+ suexec - Switch User For Exec
+
+<strong>SYNOPSIS</strong>
+ No synopsis for usage, because this program is used inter-
+ nally by Apache only.
+
+<strong>DESCRIPTION</strong>
+ <strong>suexec </strong>is the "wrapper" support program for the suEXEC
+ behaviour for Apache. It is run from within Apache automat-
+ ically to switch the user when an external program has to be
+ run under a different user. For more information about
+ suEXEC see the document `Apache suEXEC Support' under
+ http://httpd.apache.org/docs-2.0/suexec.html .
+
+<strong>SEE ALSO</strong>
+ <strong>httpd(8)</strong>
+
+</pre>
+<!--#include virtual="footer.html" -->
+</BODY></HTML>
diff --git a/docs/manual/server-wide.html b/docs/manual/server-wide.html
new file mode 100644
index 0000000000..4636e49ab0
--- /dev/null
+++ b/docs/manual/server-wide.html
@@ -0,0 +1,215 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Server-Wide Configuration</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<h1 align="center">Server-Wide Configuration</h1>
+
+<p>This document explains some of the directives provided by
+<a href="mod/core.html">core</A> server which are used to configure
+the basic operations of the server.</p>
+
+<ul>
+<li><a href="#identification">Server Identification</a></li>
+<li><a href="#locations">File Locations</a></li>
+<li><a href="#process">Process Creation</a></li>
+<li><a href="#network">Network Configuration</a></li>
+<li><a href="#resource">Limiting Resource Usage</a></li>
+</ul>
+
+<hr>
+
+<h2><a name="identfication">Server Identification</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<A HREF="mod/core.html#servername">ServerName</A><br>
+<A HREF="mod/core.html#serveradmin">ServerAdmin</A><br>
+<A HREF="mod/core.html#serversignature">ServerSignature</A><br>
+<A HREF="mod/core.html#servertokens">ServerTokens</A><br>
+<A HREF="mod/core.html#usecanonicalname">UseCanonicalName</A><br>
+</td></tr></table>
+
+<p>The <code>ServerAdmin</code> and <code>ServerTokens</code>
+directives control what information about the server will be presented
+in server-generated documents such as error messages.
+The <code>ServerTokens</code> directive sets the value of the
+Server HTTP response header field.</p>
+
+<p>The <code>ServerName</code> and <code>UseCanonicalName</code>
+directives are used by the server to determine how to construct
+self-referential URLs. For example, when a client requests a
+directory, but does not include the trailing slash in the directory
+name, Apache must redirect the client to the full name including the
+trailing slash so that the client will correctly resolve relative
+references in the document.</p>
+
+<hr>
+
+<h2><a name="locations">File Locations</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/core.html#coredumpdirectory">CoreDumpDirectory</a><br>
+<a href="mod/core.html#documentroot">DocumentRoot</a><br>
+<a href="mod/core.html#errorlog">ErrorLog</a><br>
+<a href="mod/core.html#lockfile">Lockfile</a><br>
+<a href="mod/core.html#pidfile">PidFile</a><br>
+<a href="mod/core.html#scoreboardfile">ScoreBoardFile</a><br>
+<a href="mod/core.html#serverroot">ServerRoot</a><br>
+</td></tr></table>
+
+<p>These directives control the locations of the various files that
+Apache needs for proper operation. When the pathname used does not
+begin with a slash "/", the files are located relative to the
+<code>ServerRoot</code>. Be careful about locating files in paths
+which are writable by non-root users. See the <a
+href="misc/security_tips.html">security tips</a> documentation for
+more details.</p>
+
+<hr>
+
+<h2><a name="process">Process Creation</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/core.html#bs2000account">BS2000Account</a><br>
+<a href="mod/core.html#group">Group</a><br>
+<a href="mod/core.html#baxclients">MaxClients</a><br>
+<a href="mod/core.html#maxrequestsperchild">MaxRequestsPerChild</a><br>
+<a href="mod/core.html#maxspareservers">MaxSpareServers</a><br>
+<a href="mod/core.html#minspareservers">MinSpareServers</a><br>
+<a href="mod/core.html#servertype">ServerType</a><br>
+<a href="mod/core.html#startservers">StartServers</a><br>
+<a href="mod/core.html#threadsperchild">ThreadsPerChild</a><br>
+<a href="mod/core.html#user">User</a><br>
+</td></tr></table>
+
+<p>When <code>ServerType</code> is set to its recommended value of
+<code>Standalone</code>, Apache 1.3 for Unix is a pre-forking web
+server. A single control process is responsible for launching child
+processes which listen for connections and serve them when they
+arrive. Apache always tries to maintain several <em>spare</em> or
+idle server processes, which stand ready to serve incoming requests.
+In this way, clients do not need to wait for a new child processes to
+be forked before their requests can be served.</p>
+
+<p>The <code>StartServers</code>, <code>MinSpareServers</code>,
+<code>MaxSpareServers</code>, and <code>MaxServers</code> regulate how
+the parent process creates children to serve requests. In general,
+Apache is very self-regulating, so most sites do not need to adjust
+these directives from their default values. Sites which need to serve
+more than 256 simultaneous requests may need to increase
+<code>MaxClients</code>, while sites with limited memory may need to
+decrease <code>MaxClients</code> to keep the server from thrashing
+(swapping memory to disk and back).</p>
+
+<p>While the parent process is usually started as root under Unix
+in order to bind to port 80, the child processes are launched
+by Apache as a less-privileged user. The <code>User</code> and
+<code>Group</code> directives are used to set the privileges
+of the Apache child processes. The child processes must
+be able to read all the content that will be served, but
+should have as few privileges beyond that as possible.
+In addition, unless <a href="suexec.html">suexec</a> is used,
+these directives also set the privileges which will be inherited
+by CGI scripts.</p>
+
+<p><code>MaxRequestsPerChild</code> controls how frequently the server
+recycles processes by killing old ones and launching new ones.</p>
+
+<p>Under Windows, Apache launches one control process and one
+child process. The child process creates multiple threads to
+serve requests. The number of threads is controlled by the
+<code>ThreadsPerChild</code> directive.</p>
+
+<hr>
+
+<h2><a name="network">Network Configuration</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/core.html#bindaddress">BindAddress</a><br>
+<a href="mod/core.html#keepalive">KeepAlive</a><br>
+<a href="mod/core.html#keepalivetimeout">KeepAliveTimeout</a><br>
+<a href="mod/core.html#listen">Listen</a><br>
+<a href="mod/core.html#listenbacklog">ListenBackLog</a><br>
+<a href="mod/core.html#maxKeepaliverequests">MaxKeepAliveRequests</a><br>
+<a href="mod/core.html#port">Port</a><br>
+<a href="mod/core.html#sendbuffersize">SendBufferSize</a><br>
+<a href="mod/core.html#timeOut">TimeOut</a><br>
+</td></tr></table>
+
+<p>When Apache starts, it connects to some port and address on the
+local machine and waits for incoming requests. By default, it listens
+to all addresses on the machine, and to the port as specified by the
+<code>Port</code> directive in the server configuration. However, it
+can be told to listen to more than one port, to listen to only
+selected addresses, or a combination. This is often combined with the
+<a href="vhosts/">Virtual Host</a> feature which determines how Apache
+responds to different IP addresses, hostnames and ports.</p>
+
+<p>There are two directives used to restrict or specify which addresses
+and ports Apache listens to. The <code>BindAddress</code> directive
+is used to restrict the server to listening to a single IP address.
+The <code>Listen</code> directive can be used to specify multiple
+IP addresses and/or Ports to which Apache will listen.</p>
+
+<p>The <code>ListenBackLog</code>, <code>SendBufferSize</code>, and
+<code>TimeOut</code> directives are used to adjust how Apache
+interacts with the network.</p>
+
+<p>The <code>KeepAlive</code>, <code>KeepAliveTimeout</code>,
+and <code>MaxKeepAliveRequests</code> directives are used to
+configure how Apache handles persistent connections.</p>
+
+<hr>
+<h2><a name="resource">Limiting Resource Usage</a></h2>
+<table border="1">
+<tr><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/core.html#limitrequestbody">LimitRequestBody</a><br>
+<a href="mod/core.html#limitrequestfields">LimitRequestFields</a><br>
+<a href="mod/core.html#limitrequestfieldsize">LimitRequestFieldsize</a><br>
+<a href="mod/core.html#limitrequestline">LimitRequestLine</a><br>
+<a href="mod/core.html#rlimitcpu">RLimitCPU</a><br>
+<a href="mod/core.html#rlimitmem">RLimitMEM</a><br>
+<a href="mod/core.html#rlimitnproc">RLimitNPROC</a><br>
+<a href="mod/core.html#threadstacksize">ThreadStackSize</a><br>
+</td></tr></table>
+
+<p>The <code>Limit</code>* directives are used to place limits
+on the amount of resources Apache will use in reading requests
+from clients. By limiting these values, some kinds of denial
+of service attacks can be mitigated.</p>
+
+<p>The <code>RLimit</code>* directives are used to limit the amount
+of resources which can be used by processes forked off from
+the Apache children. In particular, this will control
+resources used by CGI scripts and SSI exec commands.</p>
+
+<p>The <code>ThreadStackSize</code> directive is used only
+on Netware to control the stock size.</p>
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/server-wide.html.en b/docs/manual/server-wide.html.en
new file mode 100644
index 0000000000..4636e49ab0
--- /dev/null
+++ b/docs/manual/server-wide.html.en
@@ -0,0 +1,215 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Server-Wide Configuration</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<h1 align="center">Server-Wide Configuration</h1>
+
+<p>This document explains some of the directives provided by
+<a href="mod/core.html">core</A> server which are used to configure
+the basic operations of the server.</p>
+
+<ul>
+<li><a href="#identification">Server Identification</a></li>
+<li><a href="#locations">File Locations</a></li>
+<li><a href="#process">Process Creation</a></li>
+<li><a href="#network">Network Configuration</a></li>
+<li><a href="#resource">Limiting Resource Usage</a></li>
+</ul>
+
+<hr>
+
+<h2><a name="identfication">Server Identification</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<A HREF="mod/core.html#servername">ServerName</A><br>
+<A HREF="mod/core.html#serveradmin">ServerAdmin</A><br>
+<A HREF="mod/core.html#serversignature">ServerSignature</A><br>
+<A HREF="mod/core.html#servertokens">ServerTokens</A><br>
+<A HREF="mod/core.html#usecanonicalname">UseCanonicalName</A><br>
+</td></tr></table>
+
+<p>The <code>ServerAdmin</code> and <code>ServerTokens</code>
+directives control what information about the server will be presented
+in server-generated documents such as error messages.
+The <code>ServerTokens</code> directive sets the value of the
+Server HTTP response header field.</p>
+
+<p>The <code>ServerName</code> and <code>UseCanonicalName</code>
+directives are used by the server to determine how to construct
+self-referential URLs. For example, when a client requests a
+directory, but does not include the trailing slash in the directory
+name, Apache must redirect the client to the full name including the
+trailing slash so that the client will correctly resolve relative
+references in the document.</p>
+
+<hr>
+
+<h2><a name="locations">File Locations</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/core.html#coredumpdirectory">CoreDumpDirectory</a><br>
+<a href="mod/core.html#documentroot">DocumentRoot</a><br>
+<a href="mod/core.html#errorlog">ErrorLog</a><br>
+<a href="mod/core.html#lockfile">Lockfile</a><br>
+<a href="mod/core.html#pidfile">PidFile</a><br>
+<a href="mod/core.html#scoreboardfile">ScoreBoardFile</a><br>
+<a href="mod/core.html#serverroot">ServerRoot</a><br>
+</td></tr></table>
+
+<p>These directives control the locations of the various files that
+Apache needs for proper operation. When the pathname used does not
+begin with a slash "/", the files are located relative to the
+<code>ServerRoot</code>. Be careful about locating files in paths
+which are writable by non-root users. See the <a
+href="misc/security_tips.html">security tips</a> documentation for
+more details.</p>
+
+<hr>
+
+<h2><a name="process">Process Creation</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/core.html#bs2000account">BS2000Account</a><br>
+<a href="mod/core.html#group">Group</a><br>
+<a href="mod/core.html#baxclients">MaxClients</a><br>
+<a href="mod/core.html#maxrequestsperchild">MaxRequestsPerChild</a><br>
+<a href="mod/core.html#maxspareservers">MaxSpareServers</a><br>
+<a href="mod/core.html#minspareservers">MinSpareServers</a><br>
+<a href="mod/core.html#servertype">ServerType</a><br>
+<a href="mod/core.html#startservers">StartServers</a><br>
+<a href="mod/core.html#threadsperchild">ThreadsPerChild</a><br>
+<a href="mod/core.html#user">User</a><br>
+</td></tr></table>
+
+<p>When <code>ServerType</code> is set to its recommended value of
+<code>Standalone</code>, Apache 1.3 for Unix is a pre-forking web
+server. A single control process is responsible for launching child
+processes which listen for connections and serve them when they
+arrive. Apache always tries to maintain several <em>spare</em> or
+idle server processes, which stand ready to serve incoming requests.
+In this way, clients do not need to wait for a new child processes to
+be forked before their requests can be served.</p>
+
+<p>The <code>StartServers</code>, <code>MinSpareServers</code>,
+<code>MaxSpareServers</code>, and <code>MaxServers</code> regulate how
+the parent process creates children to serve requests. In general,
+Apache is very self-regulating, so most sites do not need to adjust
+these directives from their default values. Sites which need to serve
+more than 256 simultaneous requests may need to increase
+<code>MaxClients</code>, while sites with limited memory may need to
+decrease <code>MaxClients</code> to keep the server from thrashing
+(swapping memory to disk and back).</p>
+
+<p>While the parent process is usually started as root under Unix
+in order to bind to port 80, the child processes are launched
+by Apache as a less-privileged user. The <code>User</code> and
+<code>Group</code> directives are used to set the privileges
+of the Apache child processes. The child processes must
+be able to read all the content that will be served, but
+should have as few privileges beyond that as possible.
+In addition, unless <a href="suexec.html">suexec</a> is used,
+these directives also set the privileges which will be inherited
+by CGI scripts.</p>
+
+<p><code>MaxRequestsPerChild</code> controls how frequently the server
+recycles processes by killing old ones and launching new ones.</p>
+
+<p>Under Windows, Apache launches one control process and one
+child process. The child process creates multiple threads to
+serve requests. The number of threads is controlled by the
+<code>ThreadsPerChild</code> directive.</p>
+
+<hr>
+
+<h2><a name="network">Network Configuration</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/core.html#bindaddress">BindAddress</a><br>
+<a href="mod/core.html#keepalive">KeepAlive</a><br>
+<a href="mod/core.html#keepalivetimeout">KeepAliveTimeout</a><br>
+<a href="mod/core.html#listen">Listen</a><br>
+<a href="mod/core.html#listenbacklog">ListenBackLog</a><br>
+<a href="mod/core.html#maxKeepaliverequests">MaxKeepAliveRequests</a><br>
+<a href="mod/core.html#port">Port</a><br>
+<a href="mod/core.html#sendbuffersize">SendBufferSize</a><br>
+<a href="mod/core.html#timeOut">TimeOut</a><br>
+</td></tr></table>
+
+<p>When Apache starts, it connects to some port and address on the
+local machine and waits for incoming requests. By default, it listens
+to all addresses on the machine, and to the port as specified by the
+<code>Port</code> directive in the server configuration. However, it
+can be told to listen to more than one port, to listen to only
+selected addresses, or a combination. This is often combined with the
+<a href="vhosts/">Virtual Host</a> feature which determines how Apache
+responds to different IP addresses, hostnames and ports.</p>
+
+<p>There are two directives used to restrict or specify which addresses
+and ports Apache listens to. The <code>BindAddress</code> directive
+is used to restrict the server to listening to a single IP address.
+The <code>Listen</code> directive can be used to specify multiple
+IP addresses and/or Ports to which Apache will listen.</p>
+
+<p>The <code>ListenBackLog</code>, <code>SendBufferSize</code>, and
+<code>TimeOut</code> directives are used to adjust how Apache
+interacts with the network.</p>
+
+<p>The <code>KeepAlive</code>, <code>KeepAliveTimeout</code>,
+and <code>MaxKeepAliveRequests</code> directives are used to
+configure how Apache handles persistent connections.</p>
+
+<hr>
+<h2><a name="resource">Limiting Resource Usage</a></h2>
+<table border="1">
+<tr><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<a href="mod/core.html#limitrequestbody">LimitRequestBody</a><br>
+<a href="mod/core.html#limitrequestfields">LimitRequestFields</a><br>
+<a href="mod/core.html#limitrequestfieldsize">LimitRequestFieldsize</a><br>
+<a href="mod/core.html#limitrequestline">LimitRequestLine</a><br>
+<a href="mod/core.html#rlimitcpu">RLimitCPU</a><br>
+<a href="mod/core.html#rlimitmem">RLimitMEM</a><br>
+<a href="mod/core.html#rlimitnproc">RLimitNPROC</a><br>
+<a href="mod/core.html#threadstacksize">ThreadStackSize</a><br>
+</td></tr></table>
+
+<p>The <code>Limit</code>* directives are used to place limits
+on the amount of resources Apache will use in reading requests
+from clients. By limiting these values, some kinds of denial
+of service attacks can be mitigated.</p>
+
+<p>The <code>RLimit</code>* directives are used to limit the amount
+of resources which can be used by processes forked off from
+the Apache children. In particular, this will control
+resources used by CGI scripts and SSI exec commands.</p>
+
+<p>The <code>ThreadStackSize</code> directive is used only
+on Netware to control the stock size.</p>
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/server-wide.html.ja.jis b/docs/manual/server-wide.html.ja.jis
new file mode 100644
index 0000000000..1ed7d254ab
--- /dev/null
+++ b/docs/manual/server-wide.html.ja.jis
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Server-Wide Configuration</title>
+</head>
+<!-- English revision: 1.6 -->
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<body
+ bgcolor="#FFFFFF"
+ text="#000000"
+ link="#0000FF"
+ vlink="#000080"
+ alink="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<h1 align="center">$B%5!<%PA4BN$N@_Dj(B</h1>
+
+<p>$B$3$N%I%-%e%a%s%H$G$O(B<a href="mod/core.html">$B%3%"(B</a>$B%5!<%P$N(B
+$B%G%#%l%/%F%#%V$NCf$G!"4pK\F0:n$r@_Dj$9$k$?$a$N$b$N$r@bL@$7$^$9!#(B</p>
+
+<ul>
+<li><a href="#identification">$B%5!<%P(B ID</a></li>
+<li><a href="#locations">$B%U%!%$%k$N0LCV(B</a></li>
+<li><a href="#resource">$B%j%=!<%9$N@)8B(B</a></li>
+</ul>
+
+<hr>
+
+<h2><a name="identification">$B%5!<%P(B ID</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>$B4XO"%G%#%l%/%F%#%V(B</strong><br><br>
+
+<a href="mod/core.html#servername">ServerName</a><br>
+<a href="mod/core.html#serveradmin">ServerAdmin</a><br>
+<a href="mod/core.html#serversignature">ServerSignature</a><br>
+<a href="mod/core.html#servertokens">ServerTokens</a><br>
+<a href="mod/core.html#usecanonicalname">UseCanonicalName</a><br>
+</td></tr></table>
+
+<p><code>ServerAdmin</code> $B%G%#%l%/%F%#%V$H(B <code>ServerTokens</code>
+$B%G%#%l%/%F%#%V$O!"%(%i!<%a%C%;!<%8$J$I$N%5!<%P$,:n$k%I%-%e%a%s%H$K!"(B
+$B$I$N$h$&$J%5!<%P$N>pJs$rI=<($9$k$+$r@)8f$7$^$9!#(B<code>ServerTokens</code>
+$B%G%#%l%/%F%#%V$O!"(BServer HTTP $B%l%9%]%s%9%X%C%@%U%#!<%k%I$NCM$r(B
+$B@_Dj$7$^$9!#(B</p>
+
+<p><code>ServerName</code> $B%G%#%l%/%F%#%V$H(B <code>UseCanonicalName</code>
+$B%G%#%l%/%F%#%V$O!"%5!<%P$,<+J,<+?H$r;2>H$9$k(B URL $B$r:n$k$H$-$K(B
+$B;H$o$l$^$9!#$?$H$($P!"%/%i%$%"%s%H$,%G%#%l%/%H%j$rMW5a$7$F!"(B
+$B$=$N%G%#%l%/%H%jL>$N:G8e$K%9%i%C%7%e$,IU$$$F$$$J$$$h$&$J>l9g$K$O!"(B
+$B%I%-%e%a%s%H$NAjBPE*$J;2>H$r@5$7$/2r7h$G$-$k$h$&$K$9$k$?$a$K!"(B
+Apache $B$O:G8e$N%9%i%C%7%e$r4^$s$@40A4$J%Q%9$K%/%i%$%"%s%H$r(B
+$B%j%@%$%l%/%H$5$;$kI,MW$,$"$j$^$9!#(B</p>
+
+<hr>
+
+<h2><a name="locations">$B%U%!%$%k$N0LCV(B</a></h2>
+
+<table border="1">
+<tr><td valign="top">
+<strong>$B4XO"%G%#%l%/%F%#%V(B</strong><br><br>
+
+<a href="mod/core.html#coredumpdirectory">CoreDumpDirectory</a><br>
+<a href="mod/core.html#documentroot">DocumentRoot</a><br>
+<a href="mod/core.html#errorlog">ErrorLog</a><br>
+<a href="mod/core.html#lockfile">Lockfile</a><br>
+<a href="mod/core.html#pidfile">PidFile</a><br>
+<a href="mod/core.html#scoreboardfile">ScoreBoardFile</a><br>
+<a href="mod/core.html#serverroot">ServerRoot</a><br>
+</td></tr></table>
+
+<p>$B$3$l$i$N%G%#%l%/%F%#%V$O(B Apache $B$,E,@Z$JF0:n$r$9$k$?$a$KI,MW$J(B
+$B3F<o%U%!%$%k$N0LCV$r@)8f$7$^$9!#%Q%9$,%9%i%C%7%e(B "/" $B$G;O$^$C$F$$$J$$(B
+$B$H$-$O!"%U%!%$%k$O(B <code>ServerRoot</code> $B$+$i$NAjBP%Q%9$H$7$F(B
+$BC5$5$l$^$9!#(Broot $B0J30$N%f!<%6$,=q$-9~$_2DG=$J%Q%9$K%U%!%$%k$r(B
+$BCV$/>l9g$OCm0U$,I,MW$G$9!#>\:Y$O(B<a
+href="misc/security_tips.html">$B!V%;%-%e%j%F%#>pJs!W(B</a>$B$r(B
+$B;2>H$7$F$/$@$5$$!#(B</p>
+
+<hr>
+<h2><a name="resource">$B%j%=!<%9$N@)8B(B</a></h2>
+<table border="1">
+<tr><td valign="top">
+<strong>$B4XO"%G%#%l%/%F%#%V(B</strong><br><br>
+
+<a href="mod/core.html#limitrequestbody">LimitRequestBody</a><br>
+<a href="mod/core.html#limitrequestfields">LimitRequestFields</a><br>
+<a href="mod/core.html#limitrequestfieldsize">LimitRequestFieldsize</a><br>
+<a href="mod/core.html#limitrequestline">LimitRequestLine</a><br>
+<a href="mod/core.html#rlimitcpu">RLimitCPU</a><br>
+<a href="mod/core.html#rlimitmem">RLimitMEM</a><br>
+<a href="mod/core.html#rlimitnproc">RLimitNPROC</a><br>
+<a href="mod/core.html#threadstacksize">ThreadStackSize</a><br>
+</td></tr></table>
+
+<p><code>LimitRequest</code>* $B%G%#%l%/%F%#%V$O(B Apache $B$,(B
+$B%/%i%$%"%s%H$+$i$N%j%/%(%9%HFI$_9~$_$G;H$&%j%=!<%9$r@)8B$9$k$?$a$K(B
+$B;H$o$l$^$9!#$3$l$i$NCM$r@)8B$9$k$3$H$G!"$$$/$D$+$N%5!<%S%95qH]967b$O(B
+$B1F6A$rOB$i$2$k$3$H$,$G$-$^$9!#(B</p>
+
+<p><code>RLimit</code>* $B%G%#%l%/%F%#%V$O!"(BApache $B$N;R%W%m%;%9$+$i(B
+fork $B$5$l$?%W%m%;%9$,;HMQ$9$k%j%=!<%9$r@)8B$9$k$?$a$K(B
+$B;H$o$l$^$9!#FC$K!"$3$l$O(B CGI $B%9%/%j%W%H$H(B SSI exec $B%3%^%s%I$G(B
+$B;H$o$l$k%j%=!<%9$r@)8f$7$^$9!#(B</p>
+
+<p><code>ThreadStackSize</code> $B$O(B Netware $B$G$N$_!"%9%?%C%/$NBg$-$5$r(B
+$B@)8f$9$k$?$a$K;H$o$l$^$9!#(B</p>
+
+<!--#include virtual="footer.html" -->
+</body>
+</html>
diff --git a/docs/manual/upgrading.html b/docs/manual/upgrading.html
new file mode 100644
index 0000000000..990a064936
--- /dev/null
+++ b/docs/manual/upgrading.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD>
+<TITLE>Upgrading to 2.0 from 1.3</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<H1 ALIGN="CENTER">Upgrading to 2.0 from 1.3</H1>
+
+<P>In order to assist folks upgrading, we maintain a document
+describing information critical to existing Apache users. These are
+intended to be brief notes, and you should be able to find more
+information in either the <A HREF="new_features_2_0.html">New
+Features</A> document, or in the <CODE>src/CHANGES</CODE> file.
+
+<H3>Compile-Time Configuration Changes</H3>
+
+<UL>
+<LI>Apache now uses an <code>autoconf</code> and <code>libtool</code>
+system for configuring the build processes. Using this system
+is similar to, but not the same as, using the APACI system in
+Apache 1.3. Further documentation to follow.</li>
+
+<li>In addition to the usual selection of modules which you can
+choose to compile, Apache 2.0 has moved the main part of request
+processing into Multi-Processing-Modules (MPMs). Documentation
+on how to select an MPM is to follow.</li>
+</UL>
+
+<H3>Run-Time Configuration Changes</H3>
+
+<UL>
+<li>The <code>CacheNegotiatedDocs</code> directive now takes
+the argument <code>on</code> or <code>off</code>. Existing
+instances of <code>CacheNegotiatedDocs</code> should be replaced
+with <code>CachNegotiatedDocs on</code>.</li>
+
+<li>The <code>ErrorDocument</code> directive no longer uses a single
+quote at the beginning of the argument to indicate a text message.
+Instead, you should enclose the message in double quotes. For
+example, existing instances of <code>ErrorDocument 403 "Some
+Message</code> should be replaced with <code>ErrorDocument 403 "Some
+Message"</code>. As long as the second argument is not a valid URL or
+pathname, it will be treated as a text message.</li>
+
+<li>The <code>AccessConfig</code> and <code>ResourceConfig</code>
+directives no longer exist. Existing instances of these directives
+can be replaced with the <code>Include</code> directive which
+has equivalent functionality. If you were making use of the
+default values of these directives without including them in
+the configuration files, you may need to add
+<code>Include conf/access.conf</code> and <code>Include conf/srm.conf</code>
+to your httpd.conf.</li>
+
+<li>The <code>ExtendedStatus</code> directive no longer exists.</li>
+
+<li>The <code>ServerType</code> directive no longer exists.</li>
+
+<li>The <code>CoreDumpDirectory</code> directive no longer exists.</li>
+
+<li>Many directives that were in the core server in Apache 1.3
+are now in the MPMs.</li>
+
+
+</UL>
+
+<H3>Misc Changes</H3>
+
+<UL>
+<li>The module mod_auth_digest, which was experimental in Apache 1.3
+is now a standard module.</li>
+
+<li>The mod_mmap_static module, which was experimental in Apache 1.3
+has been replaced with mod_file_cache.</li>
+
+
+</UL>
+
+<H3>Third Party Modules</H3>
+
+<p>Extensive changes were made to the server API in Apache 2.0.
+Existing modules designed for the Apache 1.3 API will <strong>not</strong>
+work in Apache 2.0 without modification. Details to follow.
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/upgrading.html.en b/docs/manual/upgrading.html.en
new file mode 100644
index 0000000000..990a064936
--- /dev/null
+++ b/docs/manual/upgrading.html.en
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD>
+<TITLE>Upgrading to 2.0 from 1.3</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<H1 ALIGN="CENTER">Upgrading to 2.0 from 1.3</H1>
+
+<P>In order to assist folks upgrading, we maintain a document
+describing information critical to existing Apache users. These are
+intended to be brief notes, and you should be able to find more
+information in either the <A HREF="new_features_2_0.html">New
+Features</A> document, or in the <CODE>src/CHANGES</CODE> file.
+
+<H3>Compile-Time Configuration Changes</H3>
+
+<UL>
+<LI>Apache now uses an <code>autoconf</code> and <code>libtool</code>
+system for configuring the build processes. Using this system
+is similar to, but not the same as, using the APACI system in
+Apache 1.3. Further documentation to follow.</li>
+
+<li>In addition to the usual selection of modules which you can
+choose to compile, Apache 2.0 has moved the main part of request
+processing into Multi-Processing-Modules (MPMs). Documentation
+on how to select an MPM is to follow.</li>
+</UL>
+
+<H3>Run-Time Configuration Changes</H3>
+
+<UL>
+<li>The <code>CacheNegotiatedDocs</code> directive now takes
+the argument <code>on</code> or <code>off</code>. Existing
+instances of <code>CacheNegotiatedDocs</code> should be replaced
+with <code>CachNegotiatedDocs on</code>.</li>
+
+<li>The <code>ErrorDocument</code> directive no longer uses a single
+quote at the beginning of the argument to indicate a text message.
+Instead, you should enclose the message in double quotes. For
+example, existing instances of <code>ErrorDocument 403 "Some
+Message</code> should be replaced with <code>ErrorDocument 403 "Some
+Message"</code>. As long as the second argument is not a valid URL or
+pathname, it will be treated as a text message.</li>
+
+<li>The <code>AccessConfig</code> and <code>ResourceConfig</code>
+directives no longer exist. Existing instances of these directives
+can be replaced with the <code>Include</code> directive which
+has equivalent functionality. If you were making use of the
+default values of these directives without including them in
+the configuration files, you may need to add
+<code>Include conf/access.conf</code> and <code>Include conf/srm.conf</code>
+to your httpd.conf.</li>
+
+<li>The <code>ExtendedStatus</code> directive no longer exists.</li>
+
+<li>The <code>ServerType</code> directive no longer exists.</li>
+
+<li>The <code>CoreDumpDirectory</code> directive no longer exists.</li>
+
+<li>Many directives that were in the core server in Apache 1.3
+are now in the MPMs.</li>
+
+
+</UL>
+
+<H3>Misc Changes</H3>
+
+<UL>
+<li>The module mod_auth_digest, which was experimental in Apache 1.3
+is now a standard module.</li>
+
+<li>The mod_mmap_static module, which was experimental in Apache 1.3
+has been replaced with mod_file_cache.</li>
+
+
+</UL>
+
+<H3>Third Party Modules</H3>
+
+<p>Extensive changes were made to the server API in Apache 2.0.
+Existing modules designed for the Apache 1.3 API will <strong>not</strong>
+work in Apache 2.0 without modification. Details to follow.
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/upgrading.html.fr b/docs/manual/upgrading.html.fr
new file mode 100644
index 0000000000..eb4a1bc6fa
--- /dev/null
+++ b/docs/manual/upgrading.html.fr
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML><HEAD>
+<TITLE>Mise &agrave; jour de la version 1.3 &agrave; la version 2.0</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<H1 ALIGN="CENTER">Mise &agrave; de la version 1.3 &agrave; la version 2.0</H1>
+
+<P>Afin d'aider les personnes souhaitant se mettre &agrave; jour, nous
+maintenons un document d&eacute;crivant les informations critiques concernant
+les utilisateurs d'Apache. Ces informations sont sous la forme de br&egrave;ves
+notes, et vous pouvez trouver plus d'informations dans le document
+ <A HREF="new_features_2_0.html">Nouvelles fonctionnalit&eacute;s</A>
+ ou dans le fichier <CODE>src/CHANGES</CODE>.
+
+<H3>Changement de la configuration &agrave; la compilation</H3>
+
+<UL>
+<LI>Apache utilise maintenant <code>autoconf</code> et <code>libtool</code>
+afin de configurer la compilation. L'utilisation de ces outils est
+similaire, mais pas tout &agrave; fait identique, au syst&egrave;me APACI
+de configuration existant dans la version 1.3 d'Apache.
+</li>
+
+<li>En plus de l'habituelle s&eacute;lection de modules que vous pouvez choisir
+de compiler, Apache 2.0 a d&eacute;plac&eacute; la majeure partie du traitement
+des requ&ecirc;tes dans les <a href="mpm.html">modules multi-traitements</a> (MPMs).</li>
+</UL>
+
+<H3>Changement de la configuration &agrave; l'ex&eacute;cution</H3>
+
+<UL>
+<li>La directive <code>CacheNegotiatedDocs</code> prend maintenant un
+param&egrave;tre qui est soit <code>on</code> soit <code>off</code>. Les configurations
+existantes utilisant la directive <code>CacheNegotiatedDocs</code> doivent
+la remplacer par <code>CacheNegotiatedDocs on</code>.</li>
+
+<li>La directive <code>ErrorDocument</code> n'utilise plus de guillemets
+ou d'apostrophes au d&eacute;but du param&egrave;tre indiquant le message.
+Dor&eacute;navant, le message devra &ecirc;tre entre guillemets. Par exemple, la
+directive <blockquote><code>ErrorDocument 403 "Some Message</code></blockquote>
+devra &ecirc;tre remplac&eacute; par <blockquote><code>ErrorDocument 403 "Some
+Message"</code></blockquote> Si le second argument n'est pas une URL ou un chemin
+valide, il sera trait&eacute; comme un message.</li>
+
+<li>Les directives <code>AccessConfig</code> et <code>ResourceConfig</code>
+n'existent plus. Ces directives peuvent &ecirc;tre remplac&eacute;es de mani&egrave;re
+&eacute;quivalente par la directive
+<a href="mod/core.html#include"><code>Include</code></a>.
+Si vous utilisiez ces directives en utilisant les valeurs par d&eacute;faut sans
+les d&eacute;finir explicitement, vous devez
+ajouter les lignes <code>Include conf/access.conf</code> et
+<code>Include conf/srm.conf</code> dans votre fichier httpd.conf.
+Afin de garantir qu'Apache lit les diff&eacute;rents fichiers de configuration
+dans le m&ecirc;me ordre que celui pour les anciennes directives, Les directives
+<code>Include</code> doivent &ecirc;tre situ&eacute;es &agrave; la fin du fichier
+httpd.conf, celle repr&eacute;sentant <code>srm.conf</code> avant celle pour
+<code>access.conf</code>.</li>
+
+<li>La directive <code>BindAddress</code> n'existe plus. La m&ecirc;me
+fonctionnalit&eacute; est fournie par la directive
+<code><a href="mod/mpm_common.html">Listen</a></code>.</li>
+
+<li>La directive <code>ExtendedStatus</code> n'existe plus.
+Le suivi des statuts a &eacute;t&eacute; enti&egrave;rement r&eacute;&eacute;crit
+afin de b&eacute;n&eacute;ficier du nouveau syst&egrave;me MPM.</li>
+
+<li>La directive <code>ServerType</code> n'existe plus.
+La m&eacute;thode utilis&eacute;e pour servir les requ&ecirc;tes est
+d&eacute;termin&eacute;e maintenant par la s&eacute;lection d'un MPM.
+Il n'existe pas actuellement de MPM con&ccedil;u pour &ecirc;tre lanc&eacute;
+par inetd.</li>
+
+<li>Beaucoup de directives qui &eacute;taient situ&eacute;es dans le noyau
+du serveur pour la version 1.3 se trouvent maintenant dans les MPMs.</li>
+
+<li>Les modules mod_log_agent et mod_log_referer qui traitaient les directives
+<code>AgentLog</code>, <code>RefererLog</code> et
+<code>RefererIgnore</code> ont &eacute;t&eacute; supprim&eacute;s.
+Le tra&ccedil;age des agents et r&eacute;f&eacute;rants et toujours disponible
+en utilisant la directive
+<a href="mod/mod_log_config.html#customlog">CustomLog</a> du module
+mod_log_config.</li>
+
+</UL>
+
+<H3>Changements divers</H3>
+
+<UL>
+
+<li>Le red&eacute;marrage en douceur du serveur est maintenant trait&eacute; en envoyant
+au processus p&egrave;re le signal <code>WINCH</code> &agrave; la place du
+signal <code>USR1</code>.</li>
+
+<li>L'option <code>-S</code> du programme <code>httpd</code>
+qui servait &agrave; afficher la configuration des h&ocirc;tes virtuels est
+remplac&eacute; par <code>-t -D DUMP_VHOSTS</code>.</li>
+
+<li>L'option <code>-X</code> du programme <code>httpd</code>
+est supprime. La majorit&eacute; des MPM permettent la m&ecirc;me fonctionnalit&eacute;
+en d&eacute;finissant les variables d'environnement <code>ONE_PROCESS</code> et
+<code>NO_DETACH</code> avant de lancer <code>httpd</code>.</li>
+
+<li>Le module mod_auth_digest, qui &eacute;tait exp&eacute;rimental dans la version 1.3,
+est maintenant un module standard.</li>
+
+<li>Le module mod_mmap_static, qui &eacute;tait exp&eacute;rimental dans la version 1.3
+a &eacute;t&eacute; remplac&eacute; par le module mod_file_cache.</li>
+
+
+</UL>
+
+<H3>Modules tiers</H3>
+
+<p>D'&eacute;normes changements ont &eacute;t&eacute; r&eacute;alis&eacute;s
+sur l'API du serveur Apache 2.0. Les modules con&ccedil;us &agrave; l'aide de
+l'API Apache 1.3 <strong>ne fonctionneront pas</strong> sur Apache 2.0 sans
+modifications. Plus de d&eacute;tails sont fournis dans la
+<a href="developer/">documentation du d&eacute;veloppeur</a>.</p>
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/urlmapping.html b/docs/manual/urlmapping.html
new file mode 100755
index 0000000000..47e4873162
--- /dev/null
+++ b/docs/manual/urlmapping.html
@@ -0,0 +1,253 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Mapping URLs to Filesystem Locations - Apache HTTP Server</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<h1 align="center">Mapping URLs to Filesystem Locations</h1>
+
+<p>This document explains the method in which Apache determines
+what filesystem location to serve a file from based on the
+URL of a request.</p>
+
+<ul>
+<li><a href="#documentroot">DocumentRoot</a></li>
+<li><a href="#outside">Files Outside the DocumentRoot</a></li>
+<li><a href="#user">User Directories</a></li>
+<li><a href="#redirect">URL Redirection</a></li>
+<li><a href="#rewrite">Rewrite Engine</a></li>
+<li><a href="#notfound">File Not Found</a></li>
+</ul>
+
+<hr>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Modules</strong><br><br>
+
+<a href="mod/mod_alias.html">mod_alias</a><br>
+<a href="mod/mod_rewrite.html">mod_rewrite</a><br>
+<a href="mod/mod_userdir.html">mod_userdir</a><br>
+<a href="mod/mod_speling.html">mod_speling</a><br>
+<a href="mod/mod_vhost_alias.html">mod_vhost_alias</a><br>
+
+</td><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<A HREF="mod/mod_alias.html#alias">Alias</A><br>
+<A HREF="mod/mod_alias.html#aliasmatch">AliasMatch</A><br>
+<A HREF="mod/mod_speling.html#checkspelling">CheckSpelling</A><br>
+<A HREF="mod/core.html#documentroot">DocumentRoot</A><br>
+<A HREF="mod/core.html#errordocument">ErrorDocument</A><br>
+<a href="mod/core.html#options">Options</a><br>
+<A HREF="mod/mod_alias.html#redirect">Redirect</A><br>
+<A HREF="mod/mod_alias.html#redirectmatch">RedirectMatch</A><br>
+<A HREF="mod/mod_rewrite.html#RewriteCond">RewriteCond</A><br>
+<A HREF="mod/mod_rewrite.html#RewriteRule">RewriteRule</A><br>
+<A HREF="mod/mod_alias.html#scriptalias">ScriptAlias</A><br>
+<A HREF="mod/mod_alias.html#scriptaliasmatch">ScriptAliasMatch</A><br>
+<A HREF="mod/mod_userdir.html#userdir">UserDir</A><br>
+
+</td></tr></table>
+
+<h2><a name="documentroot">DocumentRoot</a></h2>
+
+<p>In deciding what file to serve for a given request, Apache's
+default behavior is to take the URL-Path for the request (the part of
+the URL following the first single slash) and add it to the end of the
+<a href="mod/core.html#documentroot">DocumentRoot</a> specified in
+your configuration files. Therefore, the files and directories
+underneath the <code>DocumentRoot</code> make up the basic document
+tree which will be visible from the web.</p>
+
+<p>Apache is also capable of <a href="vhosts/">Virtual Hosting</a>,
+where the server receives requests for more than one host. In this
+case, a different <code>DocumentRoot</code> can be specified for each
+virtual host, or alternatively, the directives provided by the module
+<a href="mod/mod_vhost_alias.html">mod_vhost_alias</a> can be used to
+dynamically determine the appropriate place from which to serve
+content based on the requested IP address or hostname.</p>
+
+<h2><a name="outside">Files Outside the DocumentRoot</a></h2>
+
+<p>There are frequently circumstances where it is necessary to allow
+web access to parts of the filesystem which are not strictly
+underneath the <a href="mod/core.html#documentroot">DocumentRoot</a>.
+Apache offers several different ways to accomplish this. On Unix
+systems, symbolic links can be used to bring other parts of the
+filesystem under the <code>DocumentRoot</code>. For security reasons,
+symbolic links will only be followed if the <a
+href="mod/core.html#options">Options</a> setting for the relevant
+directory includes <code>FollowSymLinks</code> or
+<code>SymLinksIfOwnerMatch</code>.</p>
+
+<p>Alternatively, the <a href="mod/mod_alias.html#alias">Alias</a>
+directive can be used to map any part of the filesystem into the web
+space. For example, with</p>
+
+<blockquote><code>Alias /docs /var/web/
+</blockquote></code>
+
+<p>the URL <code>http://www.example.com/docs/dir/file.html</code> will
+be served from <code>/var/web/dir/file.html</code>. The <a
+href="mod/mod_alias.html#scriptalias">ScriptAlias</a> directive works
+the same way, with the additional effect that all content located at
+the target path is treated as CGI scripts.</p>
+
+<p>For situations where additional flexibility is required, the <a
+href="mod/mod_alias.html#aliasmatch">AliasMatch</a> and <a
+href="mod/mod_alias.html#scriptaliasmatch">ScriptAliasMatch</a>
+directives can do powerful <a
+href="misc/FAQ.html#regex">regular-expression</a> based matching and
+substitution. For example,</p>
+
+<blockquote><code> ScriptAliasMatch ^/~([^/]*)/cgi-bin/(.*)
+/home/$1/cgi-bin/$2 </code></blockquote>
+
+<p>will map a request to
+<code>http://example.com/~user/cgi-bin/script.cgi</code> to the path
+<code>/home/user/cgi-bin/script.cgi</code> and will treat the
+resulting file as a CGI script.</p>
+
+<h2><a name="user">User Directories</a></h2>
+
+<p>Traditionally on Unix systems, the home directory of a particular
+<em>user</em> can be referred to as <code>~user/</code>. The module
+<a href="mod/mod_userdir.html">mod_userdir</a> extends this idea to
+the web by allowing files under each user's home directory to be
+accessed using URLs such as the following.</p>
+
+<blockquote><code>http://www.example.com/~user/file.html</code></blockquote>
+
+<p>For security reasons, it would be inappropriate to give direct
+access to a user's home directory from the web. Therefore, the <a
+href="mod/mod_userdir.html#userdir">UserDir</a> directive is used to
+specify a directory underneath the user's home directory where web
+files will be located. Using the default setting of <code>Userdir
+public_html</code>, the above URL would look for a file at a directory
+like <code>/home/user/public_html/file.html</code> where the
+</code>/home/user/</code> is the user's home directory as specified in
+<code>/etc/passwd</code>.</p>
+
+<p>There are also several other forms of the <code>Userdir</code>
+directive which can be used on systems where <code>/etc/passwd</code>
+cannot be used to find the location of the home directory.</p>
+
+<p>Some people find the "~" symbol (which is often encoded on the web
+as <code>%7e</code>) to be awkward and prefer to use an alternate
+string to represent user directories. This functionality is not
+supported by mod_userdir. However, if users' home directories are
+structured in a regular way, then it is possible to use the <a
+href="mod/mod_alias.html#aliasmatch">AliasMatch</a> directive to
+achieve the desired effect. For example, to make
+<code>http://www.example.com/upages/user/file.html</code> map to
+<code>/home/user/public_html/file.html</code>, the following
+<code>AliasMatch</code> directive can be used.</p>
+
+<blockquote><code>
+AliasMatch ^/upages/([^/]*)/?(.*) /home/$1/public_html/$2
+</code></blockquote>
+
+<h2><a name="redirect">URL Redirection</a></h2>
+
+<p>The configuration directives discussed in the above sections are
+used to tell Apache to get content from a specific place in the
+filesystem and return it to the client. Sometimes, it is desirable
+instead to inform the client that the content being requested is
+located at an different URL, and instruct the client to make a new
+request with the new URL. This is referred to as <em>redirection</em>
+and is implemented by the <a
+href="mod/mod_alias.html#redirect">Redirect</a> directive. For example,
+if the contents of the directory <code>/foo/</code> under the
+<code>DocumentRoot</code> have been moved to the new directory
+<code>/bar/</code>, clients can instructed to request the content at
+the new location as follows.</p>
+
+<blockquote><code>Redirect permanent
+/foo/ http://www.example.com/bar/</code></blockquote>
+
+<p>This will redirect any URL-Path starting in <code>/foo/</code> to
+the same URL path on the <code>www.example.com</code> server with
+<code>/bar/</code> substituted for <code>/foo/</code>. Note that
+clients can be redirected to any server, not only the origin
+server.</p>
+
+<p>Apache also provides a <a
+href="mod/mod_alias.html#redirectmatch">RedirectMatch</a> directive
+which can be used for more complicated rewriting problems. For
+example, to redirect requests for the site home page to a different
+site, but leave all other requests alone, the following configuration
+can be used.</p>
+
+<blockquote><code>
+RedirectMatch permanent ^/$ http://www.example.com/startpage.html
+</code></blockquote>
+
+<p>Alternatively, to temporarily redirect all pages on a site to one
+particular page, the following configuration is useful.</p>
+
+<blockquote><code>
+RedirectMatch temp .* http://www.example.com/startpage.html
+</code></blockquote>
+
+<h2><a name="rewrite">Rewriting Engine</a></h2>
+
+<p>When even more powerful substitution is required, the rewriting
+engine provided by <a href="mod/mod_rewrite.html">mod_rewrite</a> can
+be useful. The directives provided by this module can use
+characteristics of the request such as browser type or source IP
+address in deciding from where to serve content. In addition,
+mod_rewrite can use external database files or programs to determine
+how to handle a request. Many practical examples employing
+mod_rewrite are discussed in the <a href="misc/rewriteguide.html">URL
+Rewriting Guide</a>.</p>
+
+<h2><a name="notfound">File Not Found</a></h2>
+
+<p>Inevitably, URLs will be requested for which no matching file can
+be found in the filesystem. This can happen for several reasons. In
+some cases, it can be a result of moving documents from one location
+to another. In this case, it is best to use <a href="#redirect">URL
+redirection</a> to inform clients of the new location of the resource.
+In this way, you can assure that old bookmarks and links will continue
+to work, even though the resource is at a new location.</p>
+
+<p>Another common cause of "File Not Found" errors is accidental
+mistyping of URLs, either directly in the browser, or in HTML links.
+Apache provides the module <a href="mod/mod_speling">mod_speling</a>
+(sic) to help with this problem. When this module is activated, it
+will intercept "File Not Found" errors and look for a resource with a
+similar filename. If one such file is found, mod_speling will send an
+HTTP redirect to the client informing it of the correct location. If
+several "close" files are found, a list of available alternatives will
+be presented to the client.</p>
+
+<p>An especially useful feature of mod_speling, is that it will
+compare filenames without respect to case. This can be useful for
+systems where users are unaware of the case-sensitive nature of URLs
+and the unix filesystem. However, using mod_speling for anything more
+than the occasional URL correction can lead to additional load on the
+server, since each "incorrect" request is followed by a URL
+redirection and a new request from the client.</p>
+
+<p>If all attempts to locate the content fail, Apache returns an error
+page with HTTP status code 404 (file not found). The appearance of
+this page is controlled with the <a
+href="mod/core.html#errordocument">ErrorDocument</a> directive and can
+be customized in a flexible manner as discussed in the <a
+href="custom-error.html">Custom error responses</a> and <a
+href="misc/custom_errordocs.html">International Server Error
+Responses</a> documents.</p>
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/docs/manual/urlmapping.html.en b/docs/manual/urlmapping.html.en
new file mode 100755
index 0000000000..47e4873162
--- /dev/null
+++ b/docs/manual/urlmapping.html.en
@@ -0,0 +1,253 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>Mapping URLs to Filesystem Locations - Apache HTTP Server</TITLE>
+</HEAD>
+
+<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
+<BODY
+ BGCOLOR="#FFFFFF"
+ TEXT="#000000"
+ LINK="#0000FF"
+ VLINK="#000080"
+ ALINK="#FF0000"
+>
+<!--#include virtual="header.html" -->
+<h1 align="center">Mapping URLs to Filesystem Locations</h1>
+
+<p>This document explains the method in which Apache determines
+what filesystem location to serve a file from based on the
+URL of a request.</p>
+
+<ul>
+<li><a href="#documentroot">DocumentRoot</a></li>
+<li><a href="#outside">Files Outside the DocumentRoot</a></li>
+<li><a href="#user">User Directories</a></li>
+<li><a href="#redirect">URL Redirection</a></li>
+<li><a href="#rewrite">Rewrite Engine</a></li>
+<li><a href="#notfound">File Not Found</a></li>
+</ul>
+
+<hr>
+
+<table border="1">
+<tr><td valign="top">
+<strong>Related Modules</strong><br><br>
+
+<a href="mod/mod_alias.html">mod_alias</a><br>
+<a href="mod/mod_rewrite.html">mod_rewrite</a><br>
+<a href="mod/mod_userdir.html">mod_userdir</a><br>
+<a href="mod/mod_speling.html">mod_speling</a><br>
+<a href="mod/mod_vhost_alias.html">mod_vhost_alias</a><br>
+
+</td><td valign="top">
+<strong>Related Directives</strong><br><br>
+
+<A HREF="mod/mod_alias.html#alias">Alias</A><br>
+<A HREF="mod/mod_alias.html#aliasmatch">AliasMatch</A><br>
+<A HREF="mod/mod_speling.html#checkspelling">CheckSpelling</A><br>
+<A HREF="mod/core.html#documentroot">DocumentRoot</A><br>
+<A HREF="mod/core.html#errordocument">ErrorDocument</A><br>
+<a href="mod/core.html#options">Options</a><br>
+<A HREF="mod/mod_alias.html#redirect">Redirect</A><br>
+<A HREF="mod/mod_alias.html#redirectmatch">RedirectMatch</A><br>
+<A HREF="mod/mod_rewrite.html#RewriteCond">RewriteCond</A><br>
+<A HREF="mod/mod_rewrite.html#RewriteRule">RewriteRule</A><br>
+<A HREF="mod/mod_alias.html#scriptalias">ScriptAlias</A><br>
+<A HREF="mod/mod_alias.html#scriptaliasmatch">ScriptAliasMatch</A><br>
+<A HREF="mod/mod_userdir.html#userdir">UserDir</A><br>
+
+</td></tr></table>
+
+<h2><a name="documentroot">DocumentRoot</a></h2>
+
+<p>In deciding what file to serve for a given request, Apache's
+default behavior is to take the URL-Path for the request (the part of
+the URL following the first single slash) and add it to the end of the
+<a href="mod/core.html#documentroot">DocumentRoot</a> specified in
+your configuration files. Therefore, the files and directories
+underneath the <code>DocumentRoot</code> make up the basic document
+tree which will be visible from the web.</p>
+
+<p>Apache is also capable of <a href="vhosts/">Virtual Hosting</a>,
+where the server receives requests for more than one host. In this
+case, a different <code>DocumentRoot</code> can be specified for each
+virtual host, or alternatively, the directives provided by the module
+<a href="mod/mod_vhost_alias.html">mod_vhost_alias</a> can be used to
+dynamically determine the appropriate place from which to serve
+content based on the requested IP address or hostname.</p>
+
+<h2><a name="outside">Files Outside the DocumentRoot</a></h2>
+
+<p>There are frequently circumstances where it is necessary to allow
+web access to parts of the filesystem which are not strictly
+underneath the <a href="mod/core.html#documentroot">DocumentRoot</a>.
+Apache offers several different ways to accomplish this. On Unix
+systems, symbolic links can be used to bring other parts of the
+filesystem under the <code>DocumentRoot</code>. For security reasons,
+symbolic links will only be followed if the <a
+href="mod/core.html#options">Options</a> setting for the relevant
+directory includes <code>FollowSymLinks</code> or
+<code>SymLinksIfOwnerMatch</code>.</p>
+
+<p>Alternatively, the <a href="mod/mod_alias.html#alias">Alias</a>
+directive can be used to map any part of the filesystem into the web
+space. For example, with</p>
+
+<blockquote><code>Alias /docs /var/web/
+</blockquote></code>
+
+<p>the URL <code>http://www.example.com/docs/dir/file.html</code> will
+be served from <code>/var/web/dir/file.html</code>. The <a
+href="mod/mod_alias.html#scriptalias">ScriptAlias</a> directive works
+the same way, with the additional effect that all content located at
+the target path is treated as CGI scripts.</p>
+
+<p>For situations where additional flexibility is required, the <a
+href="mod/mod_alias.html#aliasmatch">AliasMatch</a> and <a
+href="mod/mod_alias.html#scriptaliasmatch">ScriptAliasMatch</a>
+directives can do powerful <a
+href="misc/FAQ.html#regex">regular-expression</a> based matching and
+substitution. For example,</p>
+
+<blockquote><code> ScriptAliasMatch ^/~([^/]*)/cgi-bin/(.*)
+/home/$1/cgi-bin/$2 </code></blockquote>
+
+<p>will map a request to
+<code>http://example.com/~user/cgi-bin/script.cgi</code> to the path
+<code>/home/user/cgi-bin/script.cgi</code> and will treat the
+resulting file as a CGI script.</p>
+
+<h2><a name="user">User Directories</a></h2>
+
+<p>Traditionally on Unix systems, the home directory of a particular
+<em>user</em> can be referred to as <code>~user/</code>. The module
+<a href="mod/mod_userdir.html">mod_userdir</a> extends this idea to
+the web by allowing files under each user's home directory to be
+accessed using URLs such as the following.</p>
+
+<blockquote><code>http://www.example.com/~user/file.html</code></blockquote>
+
+<p>For security reasons, it would be inappropriate to give direct
+access to a user's home directory from the web. Therefore, the <a
+href="mod/mod_userdir.html#userdir">UserDir</a> directive is used to
+specify a directory underneath the user's home directory where web
+files will be located. Using the default setting of <code>Userdir
+public_html</code>, the above URL would look for a file at a directory
+like <code>/home/user/public_html/file.html</code> where the
+</code>/home/user/</code> is the user's home directory as specified in
+<code>/etc/passwd</code>.</p>
+
+<p>There are also several other forms of the <code>Userdir</code>
+directive which can be used on systems where <code>/etc/passwd</code>
+cannot be used to find the location of the home directory.</p>
+
+<p>Some people find the "~" symbol (which is often encoded on the web
+as <code>%7e</code>) to be awkward and prefer to use an alternate
+string to represent user directories. This functionality is not
+supported by mod_userdir. However, if users' home directories are
+structured in a regular way, then it is possible to use the <a
+href="mod/mod_alias.html#aliasmatch">AliasMatch</a> directive to
+achieve the desired effect. For example, to make
+<code>http://www.example.com/upages/user/file.html</code> map to
+<code>/home/user/public_html/file.html</code>, the following
+<code>AliasMatch</code> directive can be used.</p>
+
+<blockquote><code>
+AliasMatch ^/upages/([^/]*)/?(.*) /home/$1/public_html/$2
+</code></blockquote>
+
+<h2><a name="redirect">URL Redirection</a></h2>
+
+<p>The configuration directives discussed in the above sections are
+used to tell Apache to get content from a specific place in the
+filesystem and return it to the client. Sometimes, it is desirable
+instead to inform the client that the content being requested is
+located at an different URL, and instruct the client to make a new
+request with the new URL. This is referred to as <em>redirection</em>
+and is implemented by the <a
+href="mod/mod_alias.html#redirect">Redirect</a> directive. For example,
+if the contents of the directory <code>/foo/</code> under the
+<code>DocumentRoot</code> have been moved to the new directory
+<code>/bar/</code>, clients can instructed to request the content at
+the new location as follows.</p>
+
+<blockquote><code>Redirect permanent
+/foo/ http://www.example.com/bar/</code></blockquote>
+
+<p>This will redirect any URL-Path starting in <code>/foo/</code> to
+the same URL path on the <code>www.example.com</code> server with
+<code>/bar/</code> substituted for <code>/foo/</code>. Note that
+clients can be redirected to any server, not only the origin
+server.</p>
+
+<p>Apache also provides a <a
+href="mod/mod_alias.html#redirectmatch">RedirectMatch</a> directive
+which can be used for more complicated rewriting problems. For
+example, to redirect requests for the site home page to a different
+site, but leave all other requests alone, the following configuration
+can be used.</p>
+
+<blockquote><code>
+RedirectMatch permanent ^/$ http://www.example.com/startpage.html
+</code></blockquote>
+
+<p>Alternatively, to temporarily redirect all pages on a site to one
+particular page, the following configuration is useful.</p>
+
+<blockquote><code>
+RedirectMatch temp .* http://www.example.com/startpage.html
+</code></blockquote>
+
+<h2><a name="rewrite">Rewriting Engine</a></h2>
+
+<p>When even more powerful substitution is required, the rewriting
+engine provided by <a href="mod/mod_rewrite.html">mod_rewrite</a> can
+be useful. The directives provided by this module can use
+characteristics of the request such as browser type or source IP
+address in deciding from where to serve content. In addition,
+mod_rewrite can use external database files or programs to determine
+how to handle a request. Many practical examples employing
+mod_rewrite are discussed in the <a href="misc/rewriteguide.html">URL
+Rewriting Guide</a>.</p>
+
+<h2><a name="notfound">File Not Found</a></h2>
+
+<p>Inevitably, URLs will be requested for which no matching file can
+be found in the filesystem. This can happen for several reasons. In
+some cases, it can be a result of moving documents from one location
+to another. In this case, it is best to use <a href="#redirect">URL
+redirection</a> to inform clients of the new location of the resource.
+In this way, you can assure that old bookmarks and links will continue
+to work, even though the resource is at a new location.</p>
+
+<p>Another common cause of "File Not Found" errors is accidental
+mistyping of URLs, either directly in the browser, or in HTML links.
+Apache provides the module <a href="mod/mod_speling">mod_speling</a>
+(sic) to help with this problem. When this module is activated, it
+will intercept "File Not Found" errors and look for a resource with a
+similar filename. If one such file is found, mod_speling will send an
+HTTP redirect to the client informing it of the correct location. If
+several "close" files are found, a list of available alternatives will
+be presented to the client.</p>
+
+<p>An especially useful feature of mod_speling, is that it will
+compare filenames without respect to case. This can be useful for
+systems where users are unaware of the case-sensitive nature of URLs
+and the unix filesystem. However, using mod_speling for anything more
+than the occasional URL correction can lead to additional load on the
+server, since each "incorrect" request is followed by a URL
+redirection and a new request from the client.</p>
+
+<p>If all attempts to locate the content fail, Apache returns an error
+page with HTTP status code 404 (file not found). The appearance of
+this page is controlled with the <a
+href="mod/core.html#errordocument">ErrorDocument</a> directive and can
+be customized in a flexible manner as discussed in the <a
+href="custom-error.html">Custom error responses</a> and <a
+href="misc/custom_errordocs.html">International Server Error
+Responses</a> documents.</p>
+
+<!--#include virtual="footer.html" -->
+</BODY>
+</HTML>
diff --git a/include/ap_compat.h b/include/ap_compat.h
new file mode 100644
index 0000000000..e6187c97ae
--- /dev/null
+++ b/include/ap_compat.h
@@ -0,0 +1,11 @@
+#ifndef APR_COMPAT_H
+#define APR_COMPAT_H
+
+/* Drag in apu (and therefore apr) renamed symbols */
+#include "apu_compat.h"
+
+/* redefine 1.3.x symbols to the new symbol names */
+
+#define MODULE_VAR_EXPORT AP_MODULE_DECLARE_DATA
+
+#endif /* APR_COMPAT_H */ \ No newline at end of file
diff --git a/include/ap_release.h b/include/ap_release.h
new file mode 100644
index 0000000000..8ff04158e8
--- /dev/null
+++ b/include/ap_release.h
@@ -0,0 +1,74 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef AP_RELEASE_H
+#define AP_RELEASE_H
+
+#define AP_SERVER_BASEVENDOR "Apache Software Foundation"
+#define AP_SERVER_BASEPRODUCT "Apache"
+#define AP_SERVER_BASEREVISION "2.0b1-dev"
+#define AP_SERVER_BASEVERSION AP_SERVER_BASEPRODUCT "/" AP_SERVER_BASEREVISION
+#define AP_SERVER_VERSION AP_SERVER_BASEVERSION
+
+/* Numeric release version identifier: MMNNFFRBB: major minor fix final beta
+ * Always increases along the same track as the source branch.
+ * For example, Apache 1.4.2 would be '10402100', 2.5b7 would be '20500007'.
+ */
+#define APACHE_RELEASE 20000009
+
+#endif
diff --git a/include/mpm_common.h b/include/mpm_common.h
new file mode 100644
index 0000000000..f693f6011c
--- /dev/null
+++ b/include/mpm_common.h
@@ -0,0 +1,84 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/* The purpose of this file is to store the code that MOST mpm's will need
+ * this does not mean a function only goes into this file if every MPM needs
+ * it. It means that if a function is needed by more than one MPM, and
+ * future maintenance would be served by making the code common, then the
+ * function belongs here.
+ *
+ * This is going in src/main because it is not platform specific, it is
+ * specific to multi-process servers, but NOT to Unix. Which is why it
+ * does not belong in src/os/unix
+ */
+
+#ifndef APACHE_MPM_COMMON_H
+#define APACHE_MPM_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ap_reclaim_child_processes(int terminate);
+ap_proc_t *ap_wait_or_timeout(ap_wait_t *status, ap_pool_t *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !APACHE_MPM_COMMON_H */
diff --git a/include/scoreboard.h b/include/scoreboard.h
new file mode 100644
index 0000000000..affca95451
--- /dev/null
+++ b/include/scoreboard.h
@@ -0,0 +1,242 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef APACHE_SCOREBOARD_H
+#define APACHE_SCOREBOARD_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_SYS_TIMES_H
+#include <sys/time.h>
+#include <sys/times.h>
+#elif defined(TPF)
+#include <time.h>
+#endif
+
+#include "mpm_default.h" /* For HARD_.*_LIMIT */
+#include "apr_thread_proc.h"
+
+/*The optimized timeout code only works if we're not using a scoreboard file*/
+#if defined(AP_USE_MEM_BASED_SCOREBOARD)
+#define OPTIMIZE_TIMEOUTS
+#endif
+
+/* Scoreboard info on a process is, for now, kept very brief ---
+ * just status value and pid (the latter so that the caretaker process
+ * can properly update the scoreboard when a process dies). We may want
+ * to eventually add a separate set of long_score structures which would
+ * give, for each process, the number of requests serviced, and info on
+ * the current, or most recent, request.
+ *
+ * Status values:
+ */
+
+#define SERVER_DEAD 0
+#define SERVER_STARTING 1 /* Server Starting up */
+#define SERVER_READY 2 /* Waiting for connection (or accept() lock) */
+#define SERVER_BUSY_READ 3 /* Reading a client request */
+#define SERVER_BUSY_WRITE 4 /* Processing a client request */
+#define SERVER_BUSY_KEEPALIVE 5 /* Waiting for more requests via keepalive */
+#define SERVER_BUSY_LOG 6 /* Logging the request */
+#define SERVER_BUSY_DNS 7 /* Looking up a hostname */
+#define SERVER_GRACEFUL 8 /* server is gracefully finishing request */
+#define SERVER_ACCEPTING 9 /* thread is accepting connections */
+#define SERVER_QUEUEING 10 /* thread is putting connection on the queue */
+#define SERVER_NUM_STATUS 11 /* number of status settings */
+
+/* A "virtual time" is simply a counter that indicates that a child is
+ * making progress. The parent checks up on each child, and when they have
+ * made progress it resets the last_rtime element. But when the child hasn't
+ * made progress in a time that's roughly timeout_len seconds long, it is
+ * sent a SIGALRM.
+ *
+ * vtime is an optimization that is used only when the scoreboard is in
+ * shared memory (it's not easy/feasible to do it in a scoreboard file).
+ * The essential observation is that timeouts rarely occur, the vast majority
+ * of hits finish before any timeout happens. So it really sucks to have to
+ * ask the operating system to set up and destroy alarms many times during
+ * a request.
+ */
+typedef unsigned vtime_t;
+
+/* Type used for generation indicies. Startup and every restart cause a
+ * new generation of children to be spawned. Children within the same
+ * generation share the same configuration information -- pointers to stuff
+ * created at config time in the parent are valid across children. For
+ * example, the vhostrec pointer in the scoreboard below is valid in all
+ * children of the same generation.
+ *
+ * The safe way to access the vhost pointer is like this:
+ *
+ * short_score *ss = pointer to whichver slot is interesting;
+ * parent_score *ps = pointer to whichver slot is interesting;
+ * server_rec *vh = ss->vhostrec;
+ *
+ * if (ps->generation != ap_my_generation) {
+ * vh = NULL;
+ * }
+ *
+ * then if vh is not NULL it's valid in this child.
+ *
+ * This avoids various race conditions around restarts.
+ */
+typedef int ap_generation_t;
+
+/* stuff which is thread/process specific */
+typedef struct {
+#ifdef OPTIMIZE_TIMEOUTS
+ vtime_t cur_vtime; /* the child's current vtime */
+ unsigned short timeout_len; /* length of the timeout */
+#endif
+ int thread_num;
+ unsigned char status;
+ unsigned long access_count;
+ unsigned long bytes_served;
+ unsigned long my_access_count;
+ unsigned long my_bytes_served;
+ unsigned long conn_bytes;
+ unsigned short conn_count;
+ apr_time_t start_time;
+ apr_time_t stop_time;
+#ifdef HAVE_TIMES
+ struct tms times;
+#endif
+#ifndef OPTIMIZE_TIMEOUTS
+ time_t last_used;
+#endif
+ char client[32]; /* Keep 'em small... */
+ char request[64]; /* We just want an idea... */
+ server_rec *vhostrec; /* What virtual host is being accessed? */
+ /* SEE ABOVE FOR SAFE USAGE! */
+} short_score;
+
+typedef struct {
+ ap_generation_t running_generation; /* the generation of children which
+ * should still be serving requests. */
+} global_score;
+
+/* stuff which the parent generally writes and the children rarely read */
+typedef struct {
+ pid_t pid;
+ ap_generation_t generation; /* generation of this child */
+ int worker_threads;
+#ifdef OPTIMIZE_TIMEOUTS
+ time_t last_rtime; /* time(0) of the last change */
+ vtime_t last_vtime; /* the last vtime the parent has seen */
+#endif
+} parent_score;
+
+typedef struct {
+ short_score servers[HARD_SERVER_LIMIT][HARD_THREAD_LIMIT];
+ parent_score parent[HARD_SERVER_LIMIT];
+ global_score global;
+} scoreboard;
+
+#define KEY_LENGTH 16
+#define VALUE_LENGTH 64
+typedef struct {
+ char key[KEY_LENGTH];
+ char value[VALUE_LENGTH];
+} status_table_entry;
+
+#define STATUSES_PER_CONNECTION 10
+
+typedef struct {
+ status_table_entry
+ table[HARD_SERVER_LIMIT*HARD_THREAD_LIMIT][STATUSES_PER_CONNECTION];
+} new_scoreboard;
+
+#define SCOREBOARD_SIZE sizeof(scoreboard)
+#define NEW_SCOREBOARD_SIZE sizeof(new_scoreboard)
+#ifdef TPF
+#define SCOREBOARD_NAME "SCOREBRD"
+#define SCOREBOARD_FRAMES SCOREBOARD_SIZE/4095 + 1
+#endif
+
+AP_DECLARE(int) ap_exists_scoreboard_image(void);
+void reinit_scoreboard(apr_pool_t *p);
+apr_status_t ap_cleanup_shared_mem(void *d);
+AP_DECLARE(void) ap_sync_scoreboard_image(void);
+
+AP_DECLARE(void) reopen_scoreboard(apr_pool_t *p);
+
+apr_inline void ap_sync_scoreboard_image(void);
+void increment_counts(int child_num, int thread_num, request_rec *r);
+void update_scoreboard_global(void);
+AP_DECLARE(int) find_child_by_pid(apr_proc_t *pid);
+int ap_update_child_status(int child_num, int thread_num, int status, request_rec *r);
+void ap_time_process_request(int child_num, int thread_num, int status);
+
+
+AP_DECLARE_DATA extern scoreboard *ap_scoreboard_image;
+AP_DECLARE_DATA extern const char *ap_scoreboard_fname;
+AP_DECLARE_DATA extern int ap_extended_status;
+AP_DECLARE_DATA apr_time_t ap_restart_time;
+
+AP_DECLARE_DATA extern ap_generation_t volatile ap_my_generation;
+
+/* for time_process_request() in http_main.c */
+#define START_PREQUEST 1
+#define STOP_PREQUEST 2
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !APACHE_SCOREBOARD_H */
diff --git a/include/util_charset.h b/include/util_charset.h
new file mode 100644
index 0000000000..aea3f23356
--- /dev/null
+++ b/include/util_charset.h
@@ -0,0 +1,109 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef APACHE_UTIL_CHARSET_H
+#define APACHE_UTIL_CHARSET_H
+
+#ifdef APACHE_XLATE
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "apr_xlate.h"
+
+extern ap_xlate_t *ap_hdrs_to_ascii, *ap_hdrs_from_ascii;
+extern ap_xlate_t *ap_locale_to_ascii, *ap_locale_from_ascii;
+
+/* Save & Restore the current conversion settings
+ *
+ * On an EBCDIC machine:
+ *
+ * "input" means: ASCII -> EBCDIC (when reading MIME Headers and
+ * PUT/POST data)
+ * "output" means: EBCDIC -> ASCII (when sending MIME Headers and Chunks)
+ *
+ * On an ASCII machine:
+ *
+ * no conversion of headers, so we need to set the translation handle
+ * to NULL
+ */
+
+#define AP_PUSH_INPUTCONVERSION_STATE(_buff, _newx) \
+ ap_xlate_t *saved_input_xlate; \
+ ap_bgetopt(_buff, BO_RXLATE, &saved_input_xlate); \
+ ap_bsetopt(_buff, BO_RXLATE, &(_newx))
+
+#define AP_POP_INPUTCONVERSION_STATE(_buff) \
+ ap_bsetopt(_buff, BO_RXLATE, &saved_input_xlate)
+
+#define AP_PUSH_OUTPUTCONVERSION_STATE(_buff, _newx) \
+ ap_xlate_t *saved_output_xlate; \
+ ap_bgetopt(_buff, BO_WXLATE, &saved_output_xlate); \
+ ap_bsetopt(_buff, BO_WXLATE, &(_newx))
+
+#define AP_POP_OUTPUTCONVERSION_STATE(_buff) \
+ ap_bsetopt(_buff, BO_WXLATE, &saved_output_xlate)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APACHE_XLATE */
+
+#endif /* !APACHE_UTIL_CHARSET_H */
diff --git a/include/util_ebcdic.h b/include/util_ebcdic.h
new file mode 100644
index 0000000000..21360e2bf9
--- /dev/null
+++ b/include/util_ebcdic.h
@@ -0,0 +1,81 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef APACHE_UTIL_EBCDIC_H
+#define APACHE_UTIL_EBCDIC_H
+
+#ifdef CHARSET_EBCDIC
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "apr_xlate.h"
+
+extern ap_xlate_t *hdrs_to_ascii, *hdrs_from_ascii;
+extern ap_xlate_t *locale_to_ascii, *locale_from_ascii;
+
+ap_status_t ap_init_ebcdic(ap_pool_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CHARSET_EBCDIC */
+
+#endif /* !APACHE_UTIL_EBCDIC_H */
diff --git a/include/util_filter.h b/include/util_filter.h
new file mode 100644
index 0000000000..f2f564a5a2
--- /dev/null
+++ b/include/util_filter.h
@@ -0,0 +1,220 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+#ifndef AP_FILTER_H
+#define AP_FILTER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef APR_HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#include "httpd.h"
+#include "apr.h"
+
+/*
+ * FILTER CHAIN
+ *
+ * Filters operate using a "chaining" mechanism. The filters are chained
+ * together into a sequence. When output is generated, it is passed through
+ * each of the filters on this chain, until it reaches the end (or "bottom")
+ * and is placed onto the network.
+ *
+ * The top of the chain, the code generating the output, is typically called
+ * a "content generator." The content generator's output is fed into the
+ * filter chain using the standard Apache output mechanisms: ap_rputs(),
+ * ap_rprintf(), ap_rwrite(), etc.
+ *
+ * Each filter is defined by a callback. This callback takes the output from
+ * the previous filter (or the content generator if there is no previous
+ * filter), operates on it, and passes the result to the next filter in the
+ * chain. This pass-off is performed using the ap_fc_* functions, such as
+ * ap_fc_puts(), ap_fc_printf(), ap_fc_write(), etc.
+ *
+ * When content generation is complete, the system will pass an "end of
+ * stream" marker into the filter chain. The filters will use this to flush
+ * out any internal state and to detect incomplete syntax (for example, an
+ * unterminated SSI directive).
+ */
+
+/* forward declare the filter type */
+typedef struct ap_filter_t ap_filter_t;
+
+/*
+ * ap_filter_func:
+ *
+ * This function type is used for filter callbacks. It will be passed a
+ * pointer to "this" filter, and a "bucket" containing the content to be
+ * filtered.
+ *
+ * In filter->ctx, the callback will find its context. This context is
+ * provided here, so that a filter may be installed multiple times, each
+ * receiving its own per-install context pointer.
+ *
+ * Callbacks are associated with a filter definition, which is specified
+ * by name. See ap_register_filter() for setting the association between
+ * a name for a filter and its associated callback (and other information).
+ *
+ * The *bucket structure (and all those referenced by ->next and ->prev)
+ * should be considered "const". The filter is allowed to modify the
+ * next/prev to insert/remove/replace elements in the bucket list, but
+ * the types and values of the individual buckets should not be altered.
+ */
+typedef ap_status_t (*ap_filter_func)();
+
+/*
+ * ap_filter_type:
+ *
+ * Filters have different types/classifications. These are used to group
+ * and sort the filters to properly sequence their operation.
+ *
+ * AP_FTYPE_CONTENT:
+ * These filters are used to alter the content that is passed through
+ * them. Examples are SSI or PHP.
+ *
+ * AP_FTYPE_CONNECTION:
+ * These filters will alter the content, but in ways that are more
+ * strongly associated with the output connection. Examples are
+ * compression, character recoding, or chunked transfer coding.
+ *
+ * It is important to note that these types of filters are not allowed
+ * in a sub-request. A sub-requests output can certainly be filtered
+ * by AP_FTYPE_CONTENT filters, but all of the "final processing" is
+ * determined by the main request.
+ *
+ * The types have a particular sort order, which allows us to insert them
+ * into the filter chain in a determistic order. Within a particular grouping,
+ * the ordering is equivalent to the order of calls to ap_add_filter().
+ */
+typedef enum {
+ AP_FTYPE_CONTENT,
+ AP_FTYPE_CONNECTION
+} ap_filter_type;
+
+/*
+ * ap_filter_t:
+ *
+ * This is the request-time context structure for an installed filter (in
+ * the output filter chain). It provides the callback to use for filtering,
+ * the request this filter is associated with (which is important when
+ * an output chain also includes sub-request filters), the context for this
+ * installed filter, and the filter ordering/chaining fields.
+ *
+ * Filter callbacks are free to use ->ctx as they please, to store context
+ * during the filter process. Generally, this is superior over associating
+ * the state directly with the request. A callback should not change any of
+ * the other fields.
+ */
+struct ap_filter_t {
+ ap_filter_func filter_func;
+
+ void *ctx;
+
+ ap_filter_type ftype;
+ ap_filter_t *next;
+};
+
+/*
+ * ap_register_filter():
+ *
+ * This function is used to register a filter with the system. After this
+ * registration is performed, then a filter may be added into the filter
+ * chain by using ap_add_filter() and simply specifying the name.
+ *
+ * The filter's callback and type should be passed.
+ */
+API_EXPORT(void) ap_register_filter(const char *name,
+ ap_filter_func filter_func,
+ ap_filter_type ftype);
+
+/*
+ * ap_add_filter():
+ *
+ * Adds a named filter into the filter chain on the specified request record.
+ * The filter will be installed with the specified context pointer.
+ *
+ * Filters added in this way will always be placed at the end of the filters
+ * that have the same type (thus, the filters have the same order as the
+ * calls to ap_add_filter). If the current filter chain contains filters
+ * from another request, then this filter will be added before those other
+ * filters.
+ */
+API_EXPORT(void) ap_add_filter(const char *name, void *ctx, request_rec *r);
+
+
+/*
+ * Things to do later:
+ * Add parameters to ap_filter_func type. Those parameters will be something
+ * like:
+ * (request_rec *r, ap_filter_t *filter, ap_data_list *the_data)
+ * obviously, the request_rec is the current request, and the filter
+ * is the current filter stack. The data_list is a bucket list or
+ * bucket_brigade, but I am trying to keep this patch neutral. (If this
+ * comment breaks that, well sorry, but the information must be there
+ * somewhere. :-)
+ *
+ * Add a function like ap_pass_data. This function will basically just
+ * call the next filter in the chain, until the current filter is NULL. If the
+ * current filter is NULL, that means that nobody wrote to the network, and
+ * we have a HUGE bug, so we need to return an error and log it to the
+ * log file.
+ */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !AP_FILTER_H */
diff --git a/include/util_xml.h b/include/util_xml.h
new file mode 100644
index 0000000000..f7e45d4769
--- /dev/null
+++ b/include/util_xml.h
@@ -0,0 +1,220 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+#ifndef UTIL_XML_H
+#define UTIL_XML_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "httpd.h"
+#include "apr_lib.h"
+
+
+/* -------------------------------------------------------------------- */
+
+/* ### these will need to move at some point to a more logical spot */
+
+/* simple strutures to keep a linked list of pieces of text */
+typedef struct ap_text
+{
+ const char *text;
+ struct ap_text *next;
+} ap_text;
+
+typedef struct
+{
+ ap_text *first;
+ ap_text *last;
+} ap_text_header;
+
+API_EXPORT(void) ap_text_append(ap_pool_t *p, ap_text_header *hdr,
+ const char *text);
+
+
+/* --------------------------------------------------------------------
+**
+** XML PARSING
+*/
+
+/*
+** Qualified namespace values
+**
+** AP_XML_NS_DAV_ID
+** We always insert the "DAV:" namespace URI at the head of the
+** namespace array. This means that it will always be at ID==0,
+** making it much easier to test for.
+**
+** AP_XML_NS_NONE
+** This special ID is used for two situations:
+**
+** 1) The namespace prefix begins with "xml" (and we do not know
+** what it means). Namespace prefixes with "xml" (any case) as
+** their first three characters are reserved by the XML Namespaces
+** specification for future use. mod_dav will pass these through
+** unchanged. When this identifier is used, the prefix is LEFT in
+** the element/attribute name. Downstream processing should not
+** prepend another prefix.
+**
+** 2) The element/attribute does not have a namespace.
+**
+** a) No prefix was used, and a default namespace has not been
+** defined.
+** b) No prefix was used, and the default namespace was specified
+** to mean "no namespace". This is done with a namespace
+** declaration of: xmlns=""
+** (this declaration is typically used to override a previous
+** specification for the default namespace)
+**
+** In these cases, we need to record that the elem/attr has no
+** namespace so that we will not attempt to prepend a prefix.
+** All namespaces that are used will have a prefix assigned to
+** them -- mod_dav will never set or use the default namespace
+** when generating XML. This means that "no prefix" will always
+** mean "no namespace".
+**
+** In both cases, the XML generation will avoid prepending a prefix.
+** For the first case, this means the original prefix/name will be
+** inserted into the output stream. For the latter case, it means
+** the name will have no prefix, and since we never define a default
+** namespace, this means it will have no namespace.
+**
+** Note: currently, mod_dav understands the "xmlns" prefix and the
+** "xml:lang" attribute. These are handled specially (they aren't
+** left within the XML tree), so the AP_XML_NS_NONE value won't ever
+** really apply to these values.
+*/
+#define AP_XML_NS_DAV_ID 0 /* namespace ID for "DAV:" */
+#define AP_XML_NS_NONE -10 /* no namespace for this elem/attr */
+
+#define AP_XML_NS_ERROR_BASE -100 /* used only during processing */
+#define AP_XML_NS_IS_ERROR(e) ((e) <= AP_XML_NS_ERROR_BASE)
+
+/*
+** ap_xml_doc: holds a parsed XML document
+** ap_xml_elem: holds a parsed XML element
+** ap_xml_attr: holds a parsed XML attribute
+*/
+
+typedef struct ap_xml_attr
+{
+ const char *name; /* attribute name */
+ int ns; /* index into namespace array */
+
+ const char *value; /* attribute value */
+
+ struct ap_xml_attr *next; /* next attribute */
+} ap_xml_attr;
+
+typedef struct ap_xml_elem
+{
+ const char *name; /* element name */
+ int ns; /* index into namespace array */
+ const char *lang; /* xml:lang for attrs/contents */
+
+ ap_text_header first_cdata; /* cdata right after start tag */
+ ap_text_header following_cdata; /* cdata after MY end tag */
+
+ struct ap_xml_elem *parent; /* parent element */
+ struct ap_xml_elem *next; /* next (sibling) element */
+ struct ap_xml_elem *first_child; /* first child element */
+ struct ap_xml_attr *attr; /* first attribute */
+
+ /* used only during parsing */
+ struct ap_xml_elem *last_child; /* last child element */
+ struct ap_xml_ns_scope *ns_scope; /* namespaces scoped by this elem */
+
+ /* used by modules during request processing */
+ void *private;
+} ap_xml_elem;
+
+#define AP_XML_ELEM_IS_EMPTY(e) ((e)->first_child == NULL && \
+ (e)->first_cdata.first == NULL)
+
+typedef struct ap_xml_doc
+{
+ ap_xml_elem *root; /* root element */
+ ap_array_header_t *namespaces; /* array of namespaces used */
+} ap_xml_doc;
+
+API_EXPORT(int) ap_xml_parse_input(request_rec *r, ap_xml_doc **pdoc);
+
+
+/* Converts an XML element tree to flat text */
+API_EXPORT(void) ap_xml_to_text(ap_pool_t *p, const ap_xml_elem *elem,
+ int style, ap_array_header_t *namespaces,
+ int *ns_map, const char **pbuf, size_t *psize);
+
+/* style argument values: */
+#define AP_XML_X2T_FULL 0 /* start tag, contents, end tag */
+#define AP_XML_X2T_INNER 1 /* contents only */
+#define AP_XML_X2T_LANG_INNER 2 /* xml:lang + inner contents */
+#define AP_XML_X2T_FULL_NS_LANG 3 /* FULL + ns defns + xml:lang */
+
+API_EXPORT(const char *) ap_xml_empty_elem(ap_pool_t *p,
+ const ap_xml_elem *elem);
+
+API_EXPORT(const char *) ap_xml_quote_string(ap_pool_t *p, const char *s,
+ int quotes);
+API_EXPORT(void) ap_xml_quote_elem(ap_pool_t *p, ap_xml_elem *elem);
+
+/* manage an array of unique URIs: ap_xml_insert_uri() and AP_XML_URI_ITEM() */
+
+/* return the URI's (existing) index, or insert it and return a new index */
+API_EXPORT(int) ap_xml_insert_uri(ap_array_header_t *uri_array,
+ const char *uri);
+#define AP_XML_GET_URI_ITEM(ary, i) (((const char * const *)(ary)->elts)[i])
+
+#endif /* UTIL_XML_H */
diff --git a/libhttpd.dsp b/libhttpd.dsp
new file mode 100644
index 0000000000..3664cee98a
--- /dev/null
+++ b/libhttpd.dsp
@@ -0,0 +1,100 @@
+# Microsoft Developer Studio Project File - Name="ApacheCoreDll" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheCoreDll - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "ApacheCoreDll.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "ApacheCoreDll.mak" CFG="ApacheCoreDll - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ApacheCoreDll - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheCoreDll - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ApacheCoreDll - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\CoreR"
+# PROP BASE Intermediate_Dir ".\CoreR"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\CoreR"
+# PROP Intermediate_Dir ".\CoreR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I ".\include" /I ".\lib\apr\include" /I ".\os\win32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "API_EXPORT_SYMBOLS" /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I ".\include" /I ".\lib\apr\include" /I ".\os\win32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "API_EXPORT_SYMBOLS" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /map /machine:I386 /base:@"os\win32\BaseAddr.ref",ApacheCore
+# ADD LINK32 kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:".\CoreR/ApacheCore.dll" /base:@"os\win32\BaseAddr.ref",ApacheCore
+
+!ELSEIF "$(CFG)" == "ApacheCoreDll - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\CoreD"
+# PROP BASE Intermediate_Dir ".\CoreD"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\CoreD"
+# PROP Intermediate_Dir ".\CoreD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /ZI /Od /I ".\include" /I ".\lib\apr\include" /I ".\os\win32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "API_EXPORT_SYMBOLS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /ZI /Od /I ".\include" /I ".\lib\apr\include" /I ".\os\win32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "API_EXPORT_SYMBOLS" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /map /debug /machine:I386 /base:@"os\win32\BaseAddr.ref",ApacheCore
+# ADD LINK32 kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /map /debug /machine:I386 /out:".\CoreD/ApacheCore.dll" /base:@"os\win32\BaseAddr.ref",ApacheCore
+
+!ENDIF
+
+# Begin Target
+
+# Name "ApacheCoreDll - Win32 Release"
+# Name "ApacheCoreDll - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\os\win32\ApacheCore.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ApacheCore.def
+# End Source File
+# End Target
+# End Project
diff --git a/modules/cache/.cvsignore b/modules/cache/.cvsignore
new file mode 100644
index 0000000000..f2f7a70d2c
--- /dev/null
+++ b/modules/cache/.cvsignore
@@ -0,0 +1,10 @@
+.deps
+.libs
+*.la
+modules.mk
+Makefile
+*.lo
+*.slo
+*.so
+*.dll
+*.def
diff --git a/modules/cache/Makefile.in b/modules/cache/Makefile.in
new file mode 100644
index 0000000000..167b343d0d
--- /dev/null
+++ b/modules/cache/Makefile.in
@@ -0,0 +1,3 @@
+
+include $(top_srcdir)/build/special.mk
+
diff --git a/modules/cache/config.m4 b/modules/cache/config.m4
new file mode 100644
index 0000000000..30160ba70b
--- /dev/null
+++ b/modules/cache/config.m4
@@ -0,0 +1,17 @@
+dnl modules enabled in this directory by default
+
+dnl AC_DEFUN(modulename, modulestructname, defaultonoroff, configmacros)
+dnl XXX - Need to allow --enable-module to fail if optional config fails
+
+AC_DEFUN(APACHE_CHECK_STANDARD_MODULE, [
+ APACHE_MODULE([$1],[$2],,[$3],[$4],[$5])
+])
+
+APACHE_MODPATH_INIT(cache)
+
+APACHE_CHECK_STANDARD_MODULE(file_cache, File cache, , no)
+
+LTFLAGS="$LTFLAGS -export-dynamic"
+APACHE_MODPATH_FINISH
+
+APACHE_SUBST(STANDARD_LIBS)
diff --git a/modules/config5.m4 b/modules/config5.m4
new file mode 100644
index 0000000000..caa40eaf70
--- /dev/null
+++ b/modules/config5.m4
@@ -0,0 +1,43 @@
+AC_MSG_CHECKING(for extra modules)
+AC_ARG_WITH(module,
+ [ --with-module=location Include the specified module. location is the
+ path to the new module.],
+ [
+ modtype=`echo $withval | sed -e's/\(.*\):.*/\1/'`
+ pkg=`echo $withval | sed -e's/.*:\(.*\)/\1/'`
+ modfilec=`echo $pkg | sed -e 's;^.*/;;'`
+ modfileo=`echo $pkg | sed -e 's;^.*/;;' -e 's;\.c$;.o;'`
+
+ if test "x$withval" != "xmodules/$modtype/$modfilec"; then
+ cp $pkg modules/$modtype/$modfilec
+ fi
+ module=`echo $pkg | sed -e 's;.*/mod_\(.*\).c;\1;'`
+ objects="mod_$module.lo"
+ libname="mod_$module.la"
+ modpath_current="modules/$modtype"
+ BUILTIN_LIBS="$BUILTIN_LIBS $modpath_current/$libname"
+ if test ! -s "$modpath_current/modules.mk"; then
+ cat >>$modpath_current/modules.mk<<EOF
+$libname: $objects
+ \$(MOD_LINK) $objects
+DISTCLEAN_TARGETS = modules.mk
+static = $libname
+shared =
+EOF
+ else
+ cat >>$modpath_current/modules.mk.tmp<<EOF
+$libname: $objects
+ \$(MOD_LINK) $objects
+EOF
+ cat $modpath_current/modules.mk >> $modpath_current/modules.mk.tmp
+ rm $modpath_current/modules.mk
+ mv $modpath_current/modules.mk.tmp $modpath_current/modules.mk
+ sed -e "s/\(static =.*\)/\1 $libname/" $modpath_current/modules.mk > $modpath_current/modules.mk.tmp
+ rm $modpath_current/modules.mk
+ mv $modpath_current/modules.mk.tmp $modpath_current/modules.mk
+ fi
+ MODLIST="$MODLIST $module"
+ AC_MSG_RESULT(added $withval)
+ ],
+ [ AC_MSG_RESULT(no extra modules)
+ ])
diff --git a/modules/dav/fs/.cvsignore b/modules/dav/fs/.cvsignore
new file mode 100644
index 0000000000..f2f7a70d2c
--- /dev/null
+++ b/modules/dav/fs/.cvsignore
@@ -0,0 +1,10 @@
+.deps
+.libs
+*.la
+modules.mk
+Makefile
+*.lo
+*.slo
+*.so
+*.dll
+*.def
diff --git a/modules/dav/fs/Makefile.in b/modules/dav/fs/Makefile.in
new file mode 100644
index 0000000000..d149b757bf
--- /dev/null
+++ b/modules/dav/fs/Makefile.in
@@ -0,0 +1,5 @@
+
+LTLIBRARY_NAME = libapachemod_dav_fs.la
+LTLIBRARY_SOURCES = dbm.c lock.c repos.c
+
+include $(top_srcdir)/build/ltlib.mk
diff --git a/modules/dav/fs/config6.m4 b/modules/dav/fs/config6.m4
new file mode 100644
index 0000000000..394b02dc14
--- /dev/null
+++ b/modules/dav/fs/config6.m4
@@ -0,0 +1,11 @@
+dnl modules enabled in this directory by default
+
+APACHE_MODPATH_INIT(dav/fs)
+
+dav_fs_objects="mod_dav_fs.lo dbm.lo lock.lo repos.lo"
+
+dnl ### we want to default this based on whether dav is being used...
+dnl ### but there is no ordering to the config.m4 files right now...
+APACHE_MODULE(dav_fs, DAV provider for the filesystem, $dav_fs_objects, , most)
+
+APACHE_MODPATH_FINISH
diff --git a/modules/dav/fs/dbm.c b/modules/dav/fs/dbm.c
new file mode 100644
index 0000000000..ff69ba7559
--- /dev/null
+++ b/modules/dav/fs/dbm.c
@@ -0,0 +1,315 @@
+/*
+** Copyright (C) 1998-2000 Greg Stein. All Rights Reserved.
+**
+** By using this file, you agree to the terms and conditions set forth in
+** the LICENSE.html file which can be found at the top level of the mod_dav
+** distribution or at http://www.webdav.org/mod_dav/license-1.html.
+**
+** Contact information:
+** Greg Stein, PO Box 760, Palo Alto, CA, 94302
+** gstein@lyra.org, http://www.webdav.org/mod_dav/
+*/
+
+/*
+** DAV extension module for Apache 1.3.*
+** - Database support using DBM-style databases,
+** part of the filesystem repository implementation
+**
+** Written by Greg Stein, gstein@lyra.org, http://www.lyra.org/
+*/
+
+/*
+** This implementation uses a SDBM or GDBM database per file and directory to
+** record the properties. These databases are kept in a subdirectory (of
+** the directory in question or the directory that holds the file in
+** question) named by the macro DAV_FS_STATE_DIR (.DAV). The filename of the
+** database is equivalent to the target filename, and is
+** DAV_FS_STATE_FILE_FOR_DIR (.state_for_dir) for the directory itself.
+*/
+
+#ifdef DAV_USE_GDBM
+#include <gdbm.h>
+#else
+#include <fcntl.h> /* for O_RDONLY, O_WRONLY */
+#include "sdbm/sdbm.h"
+#endif
+
+#include "mod_dav.h"
+#include "dav_fs_repos.h"
+
+
+#ifdef DAV_USE_GDBM
+
+typedef GDBM_FILE dav_dbm_file;
+
+#define DAV_DBM_CLOSE(f) gdbm_close(f)
+#define DAV_DBM_FETCH(f, k) gdbm_fetch((f), (k))
+#define DAV_DBM_STORE(f, k, v) gdbm_store((f), (k), (v), GDBM_REPLACE)
+#define DAV_DBM_DELETE(f, k) gdbm_delete((f), (k))
+#define DAV_DBM_FIRSTKEY(f) gdbm_firstkey(f)
+#define DAV_DBM_NEXTKEY(f, k) gdbm_nextkey((f), (k))
+#define DAV_DBM_CLEARERR(f) if (0) ; else /* stop "no effect" warning */
+#define DAV_DBM_FREEDATUM(f, d) ((d).dptr ? free((d).dptr) : 0)
+
+#else
+
+typedef DBM *dav_dbm_file;
+
+#define DAV_DBM_CLOSE(f) sdbm_close(f)
+#define DAV_DBM_FETCH(f, k) sdbm_fetch((f), (k))
+#define DAV_DBM_STORE(f, k, v) sdbm_store((f), (k), (v), DBM_REPLACE)
+#define DAV_DBM_DELETE(f, k) sdbm_delete((f), (k))
+#define DAV_DBM_FIRSTKEY(f) sdbm_firstkey(f)
+#define DAV_DBM_NEXTKEY(f, k) sdbm_nextkey(f)
+#define DAV_DBM_CLEARERR(f) sdbm_clearerr(f)
+#define DAV_DBM_FREEDATUM(f, d) if (0) ; else /* stop "no effect" warning */
+
+#endif
+
+struct dav_db {
+ pool *pool;
+ dav_dbm_file file;
+};
+
+#define D2G(d) (*(datum*)&(d))
+
+
+void dav_dbm_get_statefiles(pool *p, const char *fname,
+ const char **state1, const char **state2)
+{
+ char *work;
+
+ if (fname == NULL)
+ fname = DAV_FS_STATE_FILE_FOR_DIR;
+
+#ifndef DAV_USE_GDBM
+ fname = ap_pstrcat(p, fname, DIRFEXT, NULL);
+#endif
+
+ *state1 = fname;
+
+#ifdef DAV_USE_GDBM
+ *state2 = NULL;
+#else
+ {
+ int extension;
+
+ work = ap_pstrdup(p, fname);
+
+ /* we know the extension is 4 characters -- len(DIRFEXT) */
+ extension = strlen(work) - 4;
+ memcpy(&work[extension], PAGFEXT, 4);
+ *state2 = work;
+ }
+#endif
+}
+
+static dav_error * dav_fs_dbm_error(dav_db *db, pool *p)
+{
+ int save_errno = errno;
+ int errcode;
+ const char *errstr;
+ dav_error *err;
+
+ p = db ? db->pool : p;
+
+#ifdef DAV_USE_GDBM
+ errcode = gdbm_errno;
+ errstr = gdbm_strerror(gdbm_errno);
+#else
+ /* There might not be a <db> if we had problems creating it. */
+ errcode = !db || sdbm_error(db->file);
+ if (errcode)
+ errstr = "I/O error occurred.";
+ else
+ errstr = "No error.";
+#endif
+
+ err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, errcode, errstr);
+ err->save_errno = save_errno;
+ return err;
+}
+
+/* ensure that our state subdirectory is present */
+/* ### does this belong here or in dav_fs_repos.c ?? */
+void dav_fs_ensure_state_dir(pool * p, const char *dirname)
+{
+ const char *pathname = ap_pstrcat(p, dirname, "/" DAV_FS_STATE_DIR, NULL);
+
+ /* ### do we need to deal with the umask? */
+
+ /* just try to make it, ignoring any resulting errors */
+ mkdir(pathname, DAV_FS_MODE_DIR);
+}
+
+/* dav_dbm_open_direct: Opens a *dbm database specified by path.
+ * ro = boolean read-only flag.
+ */
+dav_error * dav_dbm_open_direct(pool *p, const char *pathname, int ro,
+ dav_db **pdb)
+{
+ dav_dbm_file file;
+
+ *pdb = NULL;
+
+ /* NOTE: stupid cast to get rid of "const" on the pathname */
+#ifdef DAV_USE_GDBM
+ file = gdbm_open((char *) pathname,
+ 0,
+ ro ? GDBM_READER : GDBM_WRCREAT,
+ DAV_FS_MODE_FILE,
+ NULL);
+#else
+ file = sdbm_open((char *) pathname,
+ ro ? O_RDONLY : (O_RDWR | O_CREAT),
+ DAV_FS_MODE_FILE);
+#endif
+
+ /* we can't continue if we couldn't open the file and we need to write */
+ if (file == NULL && !ro) {
+ return dav_fs_dbm_error(NULL, p);
+ }
+
+ /* may be NULL if we tried to open a non-existent db as read-only */
+ if (file != NULL) {
+ /* we have an open database... return it */
+ *pdb = ap_pcalloc(p, sizeof(**pdb));
+ (*pdb)->pool = p;
+ (*pdb)->file = file;
+ }
+
+ return NULL;
+}
+
+static dav_error * dav_dbm_open(pool * p, const dav_resource *resource, int ro,
+ dav_db **pdb)
+{
+ const char *dirpath;
+ const char *fname;
+ const char *pathname;
+
+ /* Get directory and filename for resource */
+ dav_fs_dir_file_name(resource, &dirpath, &fname);
+
+ /* If not opening read-only, ensure the state dir exists */
+ if (!ro) {
+ /* ### what are the perf implications of always checking this? */
+ dav_fs_ensure_state_dir(p, dirpath);
+ }
+
+ pathname = ap_pstrcat(p,
+ dirpath,
+ "/" DAV_FS_STATE_DIR "/",
+ fname ? fname : DAV_FS_STATE_FILE_FOR_DIR,
+ NULL);
+
+ /* ### readers cannot open while a writer has this open; we should
+ ### perform a few retries with random pauses. */
+
+ /* ### do we need to deal with the umask? */
+
+ return dav_dbm_open_direct(p, pathname, ro, pdb);
+}
+
+static void dav_dbm_close(dav_db *db)
+{
+ DAV_DBM_CLOSE(db->file);
+}
+
+static dav_error * dav_dbm_fetch(dav_db *db, dav_datum key, dav_datum *pvalue)
+{
+ *(datum *) pvalue = DAV_DBM_FETCH(db->file, D2G(key));
+
+ /* we don't need the error; we have *pvalue to tell */
+ DAV_DBM_CLEARERR(db->file);
+
+ return NULL;
+}
+
+static dav_error * dav_dbm_store(dav_db *db, dav_datum key, dav_datum value)
+{
+ int rv;
+
+ rv = DAV_DBM_STORE(db->file, D2G(key), D2G(value));
+
+ /* ### fetch more specific error information? */
+
+ /* we don't need the error; we have rv to tell */
+ DAV_DBM_CLEARERR(db->file);
+
+ if (rv == -1) {
+ return dav_fs_dbm_error(db, NULL);
+ }
+ return NULL;
+}
+
+static dav_error * dav_dbm_delete(dav_db *db, dav_datum key)
+{
+ int rv;
+
+ rv = DAV_DBM_DELETE(db->file, D2G(key));
+
+ /* ### fetch more specific error information? */
+
+ /* we don't need the error; we have rv to tell */
+ DAV_DBM_CLEARERR(db->file);
+
+ if (rv == -1) {
+ return dav_fs_dbm_error(db, NULL);
+ }
+ return NULL;
+}
+
+static int dav_dbm_exists(dav_db *db, dav_datum key)
+{
+ int exists;
+
+#ifdef DAV_USE_GDBM
+ exists = gdbm_exists(db->file, D2G(key)) != 0;
+#else
+ {
+ datum value = sdbm_fetch(db->file, D2G(key));
+ sdbm_clearerr(db->file); /* unneeded */
+ exists = value.dptr != NULL;
+ }
+#endif
+ return exists;
+}
+
+static dav_error * dav_dbm_firstkey(dav_db *db, dav_datum *pkey)
+{
+ *(datum *) pkey = DAV_DBM_FIRSTKEY(db->file);
+
+ /* we don't need the error; we have *pkey to tell */
+ DAV_DBM_CLEARERR(db->file);
+
+ return NULL;
+}
+
+static dav_error * dav_dbm_nextkey(dav_db *db, dav_datum *pkey)
+{
+ *(datum *) pkey = DAV_DBM_NEXTKEY(db->file, D2G(*pkey));
+
+ /* we don't need the error; we have *pkey to tell */
+ DAV_DBM_CLEARERR(db->file);
+
+ return NULL;
+}
+
+static void dav_dbm_freedatum(dav_db *db, dav_datum data)
+{
+ DAV_DBM_FREEDATUM(db, data);
+}
+
+const dav_hooks_db dav_hooks_db_dbm =
+{
+ dav_dbm_open,
+ dav_dbm_close,
+ dav_dbm_fetch,
+ dav_dbm_store,
+ dav_dbm_delete,
+ dav_dbm_exists,
+ dav_dbm_firstkey,
+ dav_dbm_nextkey,
+ dav_dbm_freedatum,
+};
diff --git a/modules/dav/fs/lock.c b/modules/dav/fs/lock.c
new file mode 100644
index 0000000000..3b2d83c18e
--- /dev/null
+++ b/modules/dav/fs/lock.c
@@ -0,0 +1,1486 @@
+/*
+** Copyright (C) 1998-2000 Greg Stein. All Rights Reserved.
+**
+** By using this file, you agree to the terms and conditions set forth in
+** the LICENSE.html file which can be found at the top level of the mod_dav
+** distribution or at http://www.webdav.org/mod_dav/license-1.html.
+**
+** Contact information:
+** Greg Stein, PO Box 760, Palo Alto, CA, 94302
+** gstein@lyra.org, http://www.webdav.org/mod_dav/
+*/
+
+/*
+** DAV filesystem lock implementation
+**
+** Written 06/99 by Keith Wannamaker, wannamak@us.ibm.com
+**
+** Modified 08/99 by John Vasta, vasta@rational.com. to extract
+** repository-dependent code from dav_lock.c
+*/
+
+#include <sys/stat.h>
+
+#include "httpd.h"
+#include "http_log.h"
+
+#include "mod_dav.h"
+#include "dav_opaquelock.h"
+#include "dav_fs_repos.h"
+
+
+/* ---------------------------------------------------------------
+**
+** Lock database primitives
+**
+*/
+
+/*
+** LOCK DATABASES
+**
+** Lockdiscovery information is stored in the single lock database specified
+** by the DAVLockDB directive. Information about this db is stored in the
+** global server configuration.
+**
+** KEY
+**
+** The database is keyed by a key_type unsigned char (DAV_TYPE_INODE or
+** DAV_TYPE_FNAME) followed by inode and device number if possible,
+** otherwise full path (in the case of Win32 or lock-null resources).
+**
+** VALUE
+**
+** The value consists of a list of elements.
+** DIRECT LOCK: [char (DAV_LOCK_DIRECT),
+** char (dav_lock_scope),
+** char (dav_lock_type),
+** int depth,
+** time_t expires,
+** uuid_t locktoken,
+** char[] owner,
+** char[] auth_user]
+**
+** INDIRECT LOCK: [char (DAV_LOCK_INDIRECT),
+** uuid_t locktoken,
+** time_t expires,
+** int key_size,
+** char[] key]
+** The key is to the collection lock that resulted in this indirect lock
+*/
+
+#define DAV_TRUE 1
+#define DAV_FALSE 0
+
+#define DAV_CREATE_LIST 23
+#define DAV_APPEND_LIST 24
+
+/* Stored lock_discovery prefix */
+#define DAV_LOCK_DIRECT 1
+#define DAV_LOCK_INDIRECT 2
+
+#define DAV_TYPE_INODE 10
+#define DAV_TYPE_FNAME 11
+
+
+/* ack. forward declare. */
+static dav_error * dav_fs_remove_locknull_member(pool *p,
+ const char *filename,
+ dav_buffer *pbuf);
+
+/*
+** Use the opaquelock scheme for locktokens
+*/
+struct dav_locktoken {
+ uuid_t uuid;
+};
+
+
+/* #################################################################
+** ### keep these structures (internal) or move fully to dav_lock?
+*/
+
+/*
+** We need to reliably size the fixed-length portion of
+** dav_lock_discovery; best to separate it into another
+** struct for a convenient sizeof, unless we pack lock_discovery.
+*/
+typedef struct dav_lock_discovery_fixed
+{
+ char scope;
+ char type;
+ int depth;
+ time_t timeout;
+} dav_lock_discovery_fixed;
+
+typedef struct dav_lock_discovery
+{
+ struct dav_lock_discovery_fixed f;
+
+ dav_locktoken *locktoken;
+ const char *owner; /* owner field from activelock */
+ const char *auth_user; /* authenticated user who created the lock */
+ struct dav_lock_discovery *next;
+} dav_lock_discovery;
+
+/* Indirect locks represent locks inherited from containing collections.
+ * They reference the lock token for the collection the lock is
+ * inherited from. A lock provider may also define a key to the
+ * inherited lock, for fast datbase lookup. The key is opaque outside
+ * the lock provider.
+ */
+typedef struct dav_lock_indirect
+{
+ dav_locktoken *locktoken;
+ dav_datum key;
+ struct dav_lock_indirect *next;
+ time_t timeout;
+} dav_lock_indirect;
+
+/* ################################################################# */
+
+
+/*
+** Stored direct lock info - full lock_discovery length:
+** prefix + Fixed length + lock token + 2 strings + 2 nulls (one for each string)
+*/
+#define dav_size_direct(a) (1 + sizeof(dav_lock_discovery_fixed) \
+ + sizeof(uuid_t) \
+ + ((a)->owner ? strlen((a)->owner) : 0) \
+ + ((a)->auth_user ? strlen((a)->auth_user) : 0) \
+ + 2)
+
+/* Stored indirect lock info - lock token and dav_datum */
+#define dav_size_indirect(a) (1 + sizeof(uuid_t) \
+ + sizeof(time_t) \
+ + sizeof(int) + (a)->key.dsize)
+
+/*
+** The lockdb structure.
+**
+** The <db> field may be NULL, meaning one of two things:
+** 1) That we have not actually opened the underlying database (yet). The
+** <opened> field should be false.
+** 2) We opened it readonly and it wasn't present.
+**
+** The delayed opening (determined by <opened>) makes creating a lockdb
+** quick, while deferring the underlying I/O until it is actually required.
+**
+** We export the notion of a lockdb, but hide the details of it. Most
+** implementations will use a database of some kind, but it is certainly
+** possible that alternatives could be used.
+*/
+struct dav_lockdb_private
+{
+ request_rec *r; /* for accessing the uuid state */
+ pool *pool; /* a pool to use */
+ const char *lockdb_path; /* where is the lock database? */
+
+ int opened; /* we opened the database */
+ dav_db *db; /* if non-NULL, the lock database */
+};
+typedef struct
+{
+ dav_lockdb pub;
+ dav_lockdb_private priv;
+} dav_lockdb_combined;
+
+/*
+** The private part of the lock structure.
+*/
+struct dav_lock_private
+{
+ dav_datum key; /* key into the lock database */
+};
+typedef struct
+{
+ dav_lock pub;
+ dav_lock_private priv;
+ dav_locktoken token;
+} dav_lock_combined;
+
+/*
+** This must be forward-declared so the open_lockdb function can use it.
+*/
+extern const dav_hooks_locks dav_hooks_locks_fs;
+
+
+/* internal function for creating locks */
+static dav_lock *dav_fs_alloc_lock(dav_lockdb *lockdb, dav_datum key,
+ const dav_locktoken *locktoken)
+{
+ dav_lock_combined *comb;
+
+ comb = ap_pcalloc(lockdb->info->pool, sizeof(*comb));
+ comb->pub.rectype = DAV_LOCKREC_DIRECT;
+ comb->pub.info = &comb->priv;
+ comb->priv.key = key;
+
+ if (locktoken == NULL) {
+ comb->pub.locktoken = &comb->token;
+ dav_create_opaquelocktoken(dav_get_uuid_state(lockdb->info->r),
+ &comb->token.uuid);
+ }
+ else {
+ comb->pub.locktoken = locktoken;
+ }
+
+ return &comb->pub;
+}
+
+/*
+** dav_fs_parse_locktoken
+**
+** Parse an opaquelocktoken URI into a locktoken.
+*/
+static dav_error * dav_fs_parse_locktoken(
+ pool *p,
+ const char *char_token,
+ dav_locktoken **locktoken_p)
+{
+ dav_locktoken *locktoken;
+
+ if (strstr(char_token, "opaquelocktoken:") != char_token) {
+ return dav_new_error(p,
+ HTTP_BAD_REQUEST, DAV_ERR_LOCK_UNK_STATE_TOKEN,
+ "The lock token uses an unknown State-token "
+ "format and could not be parsed.");
+ }
+ char_token += 16;
+
+ locktoken = ap_pcalloc(p, sizeof(*locktoken));
+ if (dav_parse_opaquelocktoken(char_token, &locktoken->uuid)) {
+ return dav_new_error(p, HTTP_BAD_REQUEST, DAV_ERR_LOCK_PARSE_TOKEN,
+ "The opaquelocktoken has an incorrect format "
+ "and could not be parsed.");
+ }
+
+ *locktoken_p = locktoken;
+ return NULL;
+}
+
+/*
+** dav_fs_format_locktoken
+**
+** Generate the URI for a locktoken
+*/
+static const char *dav_fs_format_locktoken(
+ pool *p,
+ const dav_locktoken *locktoken)
+{
+ const char *uuid_token = dav_format_opaquelocktoken(p, &locktoken->uuid);
+ return ap_pstrcat(p, "opaquelocktoken:", uuid_token, NULL);
+}
+
+/*
+** dav_fs_compare_locktoken
+**
+** Determine whether two locktokens are the same
+*/
+static int dav_fs_compare_locktoken(
+ const dav_locktoken *lt1,
+ const dav_locktoken *lt2)
+{
+ return dav_compare_opaquelocktoken(lt1->uuid, lt2->uuid);
+}
+
+/*
+** dav_fs_really_open_lockdb:
+**
+** If the database hasn't been opened yet, then open the thing.
+*/
+static dav_error * dav_fs_really_open_lockdb(dav_lockdb *lockdb)
+{
+ dav_error *err;
+
+ if (lockdb->info->opened)
+ return NULL;
+
+ err = dav_dbm_open_direct(lockdb->info->pool,
+ lockdb->info->lockdb_path,
+ lockdb->ro,
+ &lockdb->info->db);
+ if (err != NULL) {
+ return dav_push_error(lockdb->info->pool,
+ HTTP_INTERNAL_SERVER_ERROR,
+ DAV_ERR_LOCK_OPENDB,
+ "Could not open the lock database.",
+ err);
+ }
+
+ /* all right. it is opened now. */
+ lockdb->info->opened = 1;
+
+ return NULL;
+}
+
+/*
+** dav_fs_open_lockdb:
+**
+** "open" the lock database, as specified in the global server configuration.
+** If force is TRUE, then the database is opened now, rather than lazily.
+**
+** Note that only one can be open read/write.
+*/
+static dav_error * dav_fs_open_lockdb(request_rec *r, int ro, int force,
+ dav_lockdb **lockdb)
+{
+ dav_lockdb_combined *comb;
+
+ comb = ap_pcalloc(r->pool, sizeof(*comb));
+ comb->pub.hooks = &dav_hooks_locks_fs;
+ comb->pub.ro = ro;
+ comb->pub.info = &comb->priv;
+ comb->priv.r = r;
+ comb->priv.pool = r->pool;
+
+ comb->priv.lockdb_path = dav_get_lockdb_path(r);
+ if (comb->priv.lockdb_path == NULL) {
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR,
+ DAV_ERR_LOCK_NO_DB,
+ "A lock database was not specified with the "
+ "DAVLockDB directive. One must be specified "
+ "to use the locking functionality.");
+ }
+
+ /* done initializing. return it. */
+ *lockdb = &comb->pub;
+
+ if (force) {
+ /* ### add a higher-level comment? */
+ return dav_fs_really_open_lockdb(*lockdb);
+ }
+
+ return NULL;
+}
+
+/*
+** dav_fs_close_lockdb:
+**
+** Close it. Duh.
+*/
+static void dav_fs_close_lockdb(dav_lockdb *lockdb)
+{
+ if (lockdb->info->db != NULL)
+ (*dav_hooks_db_dbm.close)(lockdb->info->db);
+}
+
+/*
+** dav_fs_build_fname_key
+**
+** Given a pathname, build a DAV_TYPE_FNAME lock database key.
+*/
+static dav_datum dav_fs_build_fname_key(pool *p, const char *pathname)
+{
+ dav_datum key;
+
+ /* ### does this allocation have a proper lifetime? need to check */
+ /* ### can we use a buffer for this? */
+
+ /* size is TYPE + pathname + null */
+ key.dsize = strlen(pathname) + 2;
+ key.dptr = ap_palloc(p, key.dsize);
+ *key.dptr = DAV_TYPE_FNAME;
+ memcpy(key.dptr + 1, pathname, key.dsize - 1);
+ if (key.dptr[key.dsize - 2] == '/')
+ key.dptr[--key.dsize - 1] = '\0';
+ return key;
+}
+
+/*
+** dav_fs_build_key: Given a resource, return a dav_datum key
+** to look up lock information for this file.
+**
+** (Win32 or file is lock-null):
+** dav_datum->dvalue = full path
+**
+** (non-Win32 and file exists ):
+** dav_datum->dvalue = inode, dev_major, dev_minor
+*/
+static dav_datum dav_fs_build_key(pool *p, const dav_resource *resource)
+{
+ const char *file = dav_fs_pathname(resource);
+#ifndef WIN32
+ dav_datum key;
+ struct stat finfo;
+
+ /* ### use lstat() ?? */
+ if (stat(file, &finfo) == 0) {
+
+ /* ### can we use a buffer for this? */
+ key.dsize = 1 + sizeof(finfo.st_ino) + sizeof(finfo.st_dev);
+ key.dptr = ap_palloc(p, key.dsize);
+ *key.dptr = DAV_TYPE_INODE;
+ memcpy(key.dptr + 1, &finfo.st_ino, sizeof(finfo.st_ino));
+ memcpy(key.dptr + 1 + sizeof(finfo.st_ino), &finfo.st_dev,
+ sizeof(finfo.st_dev));
+
+ return key;
+ }
+#endif
+
+ return dav_fs_build_fname_key(p, file);
+}
+
+/*
+** dav_fs_lock_expired: return 1 (true) if the given timeout is in the past
+** or present (the lock has expired), or 0 (false) if in the future
+** (the lock has not yet expired).
+*/
+static int dav_fs_lock_expired(time_t expires)
+{
+ return expires != DAV_TIMEOUT_INFINITE && time(NULL) >= expires;
+}
+
+/*
+** dav_fs_save_lock_record: Saves the lock information specified in the
+** direct and indirect lock lists about path into the lock database.
+** If direct and indirect == NULL, the key is removed.
+*/
+static dav_error * dav_fs_save_lock_record(dav_lockdb *lockdb, dav_datum key,
+ dav_lock_discovery *direct,
+ dav_lock_indirect *indirect)
+{
+ dav_error *err;
+ dav_datum val = { 0 };
+ char *ptr;
+ dav_lock_discovery *dp = direct;
+ dav_lock_indirect *ip = indirect;
+
+#if DAV_DEBUG
+ if (lockdb->ro) {
+ return dav_new_error(lockdb->info->pool,
+ HTTP_INTERNAL_SERVER_ERROR, 0,
+ "INTERNAL DESIGN ERROR: the lockdb was opened "
+ "readonly, but an attempt to save locks was "
+ "performed.");
+ }
+#endif
+
+ if ((err = dav_fs_really_open_lockdb(lockdb)) != NULL) {
+ /* ### add a higher-level error? */
+ return err;
+ }
+
+ /* If nothing to save, delete key */
+ if (dp == NULL && ip == NULL) {
+ /* don't fail if the key is not present */
+ /* ### but what about other errors? */
+ (void) (*dav_hooks_db_dbm.remove)(lockdb->info->db, key);
+ return NULL;
+ }
+
+ while(dp) {
+ val.dsize += dav_size_direct(dp);
+ dp = dp->next;
+ }
+ while(ip) {
+ val.dsize += dav_size_indirect(ip);
+ ip = ip->next;
+ }
+
+ /* ### can this be ap_palloc() ? */
+ /* ### hmmm.... investigate the use of a buffer here */
+ ptr = val.dptr = ap_pcalloc(lockdb->info->pool, val.dsize);
+ dp = direct;
+ ip = indirect;
+
+ while(dp) {
+ *ptr++ = DAV_LOCK_DIRECT; /* Direct lock - lock_discovery struct follows */
+ memcpy(ptr, dp, sizeof(dp->f)); /* Fixed portion of struct */
+ ptr += sizeof(dp->f);
+ memcpy(ptr, dp->locktoken, sizeof(*dp->locktoken));
+ ptr += sizeof(*dp->locktoken);
+ if (dp->owner == NULL) {
+ *ptr++ = '\0';
+ }
+ else {
+ memcpy(ptr, dp->owner, strlen(dp->owner) + 1);
+ ptr += strlen(dp->owner) + 1;
+ }
+ if (dp->auth_user == NULL) {
+ *ptr++ = '\0';
+ }
+ else {
+ memcpy(ptr, dp->auth_user, strlen(dp->auth_user) + 1);
+ ptr += strlen(dp->auth_user) + 1;
+ }
+
+ dp = dp->next;
+ }
+
+ while(ip) {
+ *ptr++ = DAV_LOCK_INDIRECT; /* Indirect lock prefix */
+ memcpy(ptr, ip->locktoken, sizeof(*ip->locktoken)); /* Locktoken */
+ ptr += sizeof(*ip->locktoken);
+ memcpy(ptr, &ip->timeout, sizeof(ip->timeout)); /* Expire time */
+ ptr += sizeof(ip->timeout);
+ memcpy(ptr, &ip->key.dsize, sizeof(ip->key.dsize)); /* Size of key */
+ ptr += sizeof(ip->key.dsize);
+ memcpy(ptr, ip->key.dptr, ip->key.dsize); /* Key data */
+ ptr += ip->key.dsize;
+ ip = ip->next;
+ }
+
+ if ((err = (*dav_hooks_db_dbm.store)(lockdb->info->db,
+ key, val)) != NULL) {
+ /* ### more details? add an error_id? */
+ return dav_push_error(lockdb->info->pool,
+ HTTP_INTERNAL_SERVER_ERROR,
+ DAV_ERR_LOCK_SAVE_LOCK,
+ "Could not save lock information.",
+ err);
+ }
+
+ return NULL;
+}
+
+/*
+** dav_load_lock_record: Reads lock information about key from lock db;
+** creates linked lists of the direct and indirect locks.
+**
+** If add_method = DAV_APPEND_LIST, the result will be appended to the
+** head of the direct and indirect lists supplied.
+**
+** Passive lock removal: If lock has timed out, it will not be returned.
+** ### How much "logging" does RFC 2518 require?
+*/
+static dav_error * dav_fs_load_lock_record(dav_lockdb *lockdb, dav_datum key,
+ int add_method,
+ dav_lock_discovery **direct,
+ dav_lock_indirect **indirect)
+{
+ dav_error *err;
+ size_t offset = 0;
+ int need_save = DAV_FALSE;
+ dav_datum val = { 0 };
+ dav_lock_discovery *dp;
+ dav_lock_indirect *ip;
+ dav_buffer buf = { 0 };
+
+ if (add_method != DAV_APPEND_LIST) {
+ *direct = NULL;
+ *indirect = NULL;
+ }
+
+ if ((err = dav_fs_really_open_lockdb(lockdb)) != NULL) {
+ /* ### add a higher-level error? */
+ return err;
+ }
+
+ /*
+ ** If we opened readonly and the db wasn't there, then there are no
+ ** locks for this resource. Just exit.
+ */
+ if (lockdb->info->db == NULL)
+ return NULL;
+
+ if ((err = (*dav_hooks_db_dbm.fetch)(lockdb->info->db, key, &val)) != NULL)
+ return err;
+
+ if (!val.dsize)
+ return NULL;
+
+ while (offset < val.dsize) {
+ switch (*(val.dptr + offset++)) {
+ case DAV_LOCK_DIRECT:
+ /* Create and fill a dav_lock_discovery structure */
+
+ dp = ap_pcalloc(lockdb->info->pool, sizeof(*dp));
+ memcpy(dp, val.dptr + offset, sizeof(dp->f));
+ offset += sizeof(dp->f);
+ dp->locktoken = ap_palloc(lockdb->info->pool, sizeof(*dp->locktoken));
+ memcpy(dp->locktoken, val.dptr + offset, sizeof(*dp->locktoken));
+ offset += sizeof(*dp->locktoken);
+ if (*(val.dptr + offset) == '\0') {
+ ++offset;
+ }
+ else {
+ dp->owner = ap_pstrdup(lockdb->info->pool, val.dptr + offset);
+ offset += strlen(dp->owner) + 1;
+ }
+
+ if (*(val.dptr + offset) == '\0') {
+ ++offset;
+ }
+ else {
+ dp->auth_user = ap_pstrdup(lockdb->info->pool, val.dptr + offset);
+ offset += strlen(dp->auth_user) + 1;
+ }
+
+ if (!dav_fs_lock_expired(dp->f.timeout)) {
+ dp->next = *direct;
+ *direct = dp;
+ }
+ else {
+ need_save = DAV_TRUE;
+
+ /* Remove timed-out locknull fm .locknull list */
+ if (*key.dptr == DAV_TYPE_FNAME) {
+ const char *fname = key.dptr + 1;
+ struct stat finfo;
+
+ /* if we don't see the file, then it's a locknull */
+ if (lstat(fname, &finfo) != 0) {
+ if ((err = dav_fs_remove_locknull_member(lockdb->info->pool, fname, &buf)) != NULL) {
+ /* ### push a higher-level description? */
+ return err;
+ }
+ }
+ }
+ }
+ break;
+
+ case DAV_LOCK_INDIRECT:
+ /* Create and fill a dav_lock_indirect structure */
+
+ ip = ap_pcalloc(lockdb->info->pool, sizeof(*ip));
+ ip->locktoken = ap_palloc(lockdb->info->pool, sizeof(*ip->locktoken));
+ memcpy(ip->locktoken, val.dptr + offset, sizeof(*ip->locktoken));
+ offset += sizeof(*ip->locktoken);
+ memcpy(&ip->timeout, val.dptr + offset, sizeof(ip->timeout));
+ offset += sizeof(ip->timeout);
+ ip->key.dsize = *((int *) (val.dptr + offset)); /* length of datum */
+ offset += sizeof(ip->key.dsize);
+ ip->key.dptr = ap_palloc(lockdb->info->pool, ip->key.dsize);
+ memcpy(ip->key.dptr, val.dptr + offset, ip->key.dsize);
+ offset += ip->key.dsize;
+
+ if (!dav_fs_lock_expired(ip->timeout)) {
+ ip->next = *indirect;
+ *indirect = ip;
+ }
+ else {
+ need_save = DAV_TRUE;
+ /* A locknull resource will never be locked indirectly */
+ }
+
+ break;
+
+ default:
+ (*dav_hooks_db_dbm.freedatum)(lockdb->info->db, val);
+
+ /* ### should use a computed_desc and insert corrupt token data */
+ --offset;
+ return dav_new_error(lockdb->info->pool,
+ HTTP_INTERNAL_SERVER_ERROR,
+ DAV_ERR_LOCK_CORRUPT_DB,
+ ap_psprintf(lockdb->info->pool,
+ "The lock database was found to "
+ "be corrupt. offset %i, c=%02x",
+ offset, val.dptr[offset]));
+ }
+ }
+
+ (*dav_hooks_db_dbm.freedatum)(lockdb->info->db, val);
+
+ /* Clean up this record if we found expired locks */
+ /*
+ ** ### shouldn't do this if we've been opened READONLY. elide the
+ ** ### timed-out locks from the response, but don't save that info back
+ */
+ if (need_save == DAV_TRUE) {
+ return dav_fs_save_lock_record(lockdb, key, *direct, *indirect);
+ }
+
+ return NULL;
+}
+
+/* resolve <indirect>, returning <*direct> */
+static dav_error * dav_fs_resolve(dav_lockdb *lockdb,
+ dav_lock_indirect *indirect,
+ dav_lock_discovery **direct,
+ dav_lock_discovery **ref_dp,
+ dav_lock_indirect **ref_ip)
+{
+ dav_error *err;
+ dav_lock_discovery *dir;
+ dav_lock_indirect *ind;
+
+ if ((err = dav_fs_load_lock_record(lockdb, indirect->key,
+ DAV_CREATE_LIST,
+ &dir, &ind)) != NULL) {
+ /* ### insert a higher-level description? */
+ return err;
+ }
+ if (ref_dp != NULL) {
+ *ref_dp = dir;
+ *ref_ip = ind;
+ }
+
+ for (; dir != NULL; dir = dir->next) {
+ if (!dav_compare_opaquelocktoken(indirect->locktoken->uuid,
+ dir->locktoken->uuid)) {
+ *direct = dir;
+ return NULL;
+ }
+ }
+
+ /* No match found (but we should have found one!) */
+
+ /* ### use a different description and/or error ID? */
+ return dav_new_error(lockdb->info->pool,
+ HTTP_INTERNAL_SERVER_ERROR,
+ DAV_ERR_LOCK_CORRUPT_DB,
+ "The lock database was found to be corrupt. "
+ "An indirect lock's direct lock could not "
+ "be found.");
+}
+
+/* ---------------------------------------------------------------
+**
+** Property-related lock functions
+**
+*/
+
+/*
+** dav_fs_get_supportedlock: Returns a static string for all supportedlock
+** properties. I think we save more returning a static string than
+** constructing it every time, though it might look cleaner.
+*/
+static const char *dav_fs_get_supportedlock(void)
+{
+ static const char supported[] = DEBUG_CR
+ "<D:lockentry>" DEBUG_CR
+ "<D:lockscope><D:exclusive/></D:lockscope>" DEBUG_CR
+ "<D:locktype><D:write/></D:locktype>" DEBUG_CR
+ "</D:lockentry>" DEBUG_CR
+ "<D:lockentry>" DEBUG_CR
+ "<D:lockscope><D:shared/></D:lockscope>" DEBUG_CR
+ "<D:locktype><D:write/></D:locktype>" DEBUG_CR
+ "</D:lockentry>" DEBUG_CR;
+
+ return supported;
+}
+
+/* ---------------------------------------------------------------
+**
+** General lock functions
+**
+*/
+
+/* ---------------------------------------------------------------
+**
+** Functions dealing with lock-null resources
+**
+*/
+
+/*
+** dav_fs_load_locknull_list: Returns a dav_buffer dump of the locknull file
+** for the given directory.
+*/
+static dav_error * dav_fs_load_locknull_list(pool *p, const char *dirpath,
+ dav_buffer *pbuf)
+{
+ struct stat finfo;
+ int fd;
+ dav_error *err = NULL;
+
+ dav_buffer_init(p, pbuf, dirpath);
+
+ if (pbuf->buf[pbuf->cur_len - 1] == '/')
+ pbuf->buf[--pbuf->cur_len] = '\0';
+
+ dav_buffer_place(p, pbuf, "/" DAV_FS_STATE_DIR "/" DAV_FS_LOCK_NULL_FILE);
+
+ /* reset this in case we leave w/o reading into the buffer */
+ pbuf->cur_len = 0;
+
+ if ((fd = open(pbuf->buf, O_RDONLY | O_BINARY)) == -1) {
+ return NULL;
+ }
+
+ if (fstat(fd, &finfo) == -1) {
+ err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ ap_psprintf(p,
+ "Opened but could not stat file %s",
+ pbuf->buf));
+ goto loaderror;
+ }
+
+ dav_set_bufsize(p, pbuf, finfo.st_size);
+ if (read(fd, pbuf->buf, finfo.st_size) != finfo.st_size) {
+ err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ ap_psprintf(p,
+ "Failure reading locknull file "
+ "for %s", dirpath));
+
+ /* just in case the caller disregards the returned error */
+ pbuf->cur_len = 0;
+ goto loaderror;
+ }
+
+ loaderror:
+ close(fd);
+ return err;
+}
+
+/*
+** dav_fs_save_locknull_list: Saves contents of pbuf into the
+** locknull file for dirpath.
+*/
+static dav_error * dav_fs_save_locknull_list(pool *p, const char *dirpath,
+ dav_buffer *pbuf)
+{
+ const char *pathname;
+ int fd;
+ dav_error *err = NULL;
+
+ if (pbuf->buf == NULL)
+ return NULL;
+
+ dav_fs_ensure_state_dir(p, dirpath);
+ pathname = ap_pstrcat(p,
+ dirpath,
+ dirpath[strlen(dirpath) - 1] == '/' ? "" : "/",
+ DAV_FS_STATE_DIR "/" DAV_FS_LOCK_NULL_FILE,
+ NULL);
+
+ if (pbuf->cur_len == 0) {
+ /* delete the file if cur_len == 0 */
+ if (remove(pathname) != 0) {
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ ap_psprintf(p,
+ "Error removing %s", pathname));
+ }
+ return NULL;
+ }
+
+ if ((fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ DAV_FS_MODE_FILE)) == -1) {
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ ap_psprintf(p,
+ "Error opening %s for writing",
+ pathname));
+ }
+
+ if (write(fd, pbuf->buf, pbuf->cur_len) != pbuf->cur_len) {
+ err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ ap_psprintf(p,
+ "Error writing %i bytes to %s",
+ pbuf->cur_len, pathname));
+ }
+
+ close(fd);
+ return err;
+}
+
+/*
+** dav_fs_remove_locknull_member: Removes filename from the locknull list
+** for directory path.
+*/
+static dav_error * dav_fs_remove_locknull_member(pool *p, const char *filename,
+ dav_buffer *pbuf)
+{
+ dav_error *err;
+ size_t len;
+ size_t scanlen;
+ char *scan;
+ const char *scanend;
+ char *dirpath = ap_pstrdup(p, filename);
+ char *fname = strrchr(dirpath, '/');
+ int dirty = 0;
+
+ if (fname != NULL)
+ *fname++ = '\0';
+ else
+ fname = dirpath;
+ len = strlen(fname) + 1;
+
+ if ((err = dav_fs_load_locknull_list(p, dirpath, pbuf)) != NULL) {
+ /* ### add a higher level description? */
+ return err;
+ }
+
+ for (scan = pbuf->buf, scanend = scan + pbuf->cur_len;
+ scan < scanend;
+ scan += scanlen) {
+ scanlen = strlen(scan) + 1;
+ if (len == scanlen && memcmp(fname, scan, scanlen) == 0) {
+ pbuf->cur_len -= scanlen;
+ memmove(scan, scan + scanlen, scanend - (scan + scanlen));
+ dirty = 1;
+ break;
+ }
+ }
+
+ if (dirty) {
+ if ((err = dav_fs_save_locknull_list(p, dirpath, pbuf)) != NULL) {
+ /* ### add a higher level description? */
+ return err;
+ }
+ }
+
+ return NULL;
+}
+
+/* Note: used by dav_fs_repos.c */
+dav_error * dav_fs_get_locknull_members(
+ const dav_resource *resource,
+ dav_buffer *pbuf)
+{
+ const char *dirpath;
+
+ dav_fs_dir_file_name(resource, &dirpath, NULL);
+ return dav_fs_load_locknull_list(dav_fs_pool(resource), dirpath, pbuf);
+}
+
+/* ### fold into append_lock? */
+/* ### take an optional buf parameter? */
+static dav_error * dav_fs_add_locknull_state(
+ dav_lockdb *lockdb,
+ const dav_resource *resource)
+{
+ dav_buffer buf = { 0 };
+ pool *p = lockdb->info->pool;
+ const char *dirpath;
+ const char *fname;
+ dav_error *err;
+
+ dav_fs_dir_file_name(resource, &dirpath, &fname);
+
+ if ((err = dav_fs_load_locknull_list(p, dirpath, &buf)) != NULL) {
+ return dav_push_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not load .locknull file.", err);
+ }
+
+ dav_buffer_append(p, &buf, fname);
+ buf.cur_len++; /* we want the null-term here */
+
+ if ((err = dav_fs_save_locknull_list(p, dirpath, &buf)) != NULL) {
+ return dav_push_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not save .locknull file.", err);
+ }
+
+ return NULL;
+}
+
+/*
+** dav_fs_remove_locknull_state: Given a request, check to see if r->filename
+** is/was a lock-null resource. If so, return it to an existant state.
+**
+** ### this function is broken... it doesn't check!
+**
+** In this implementation, this involves two things:
+** (a) remove it from the list in the appropriate .DAV/locknull file
+** (b) on *nix, convert the key from a filename to an inode.
+*/
+static dav_error * dav_fs_remove_locknull_state(
+ dav_lockdb *lockdb,
+ const dav_resource *resource)
+{
+ dav_buffer buf = { 0 };
+ dav_error *err;
+ pool *p = lockdb->info->pool;
+ const char *pathname = dav_fs_pathname(resource);
+
+ if ((err = dav_fs_remove_locknull_member(p, pathname, &buf)) != NULL) {
+ /* ### add a higher-level description? */
+ return err;
+ }
+
+#ifndef WIN32
+ {
+ dav_lock_discovery *ld;
+ dav_lock_indirect *id;
+ dav_datum key;
+
+ /*
+ ** Fetch the lock(s) that made the resource lock-null. Remove
+ ** them under the filename key. Obtain the new inode key, and
+ ** save the same lock information under it.
+ */
+ key = dav_fs_build_fname_key(p, pathname);
+ if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST,
+ &ld, &id)) != NULL) {
+ /* ### insert a higher-level error description */
+ return err;
+ }
+
+ if ((err = dav_fs_save_lock_record(lockdb, key, NULL, NULL)) != NULL) {
+ /* ### insert a higher-level error description */
+ return err;
+ }
+
+ key = dav_fs_build_key(p, resource);
+ if ((err = dav_fs_save_lock_record(lockdb, key, ld, id)) != NULL) {
+ /* ### insert a higher-level error description */
+ return err;
+ }
+ }
+#endif
+
+ return NULL;
+}
+
+static dav_error * dav_fs_create_lock(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ dav_lock **lock)
+{
+ dav_datum key;
+
+ key = dav_fs_build_key(lockdb->info->pool, resource);
+
+ *lock = dav_fs_alloc_lock(lockdb,
+ key,
+ NULL);
+
+ (*lock)->is_locknull = !resource->exists;
+
+ return NULL;
+}
+
+static dav_error * dav_fs_get_locks(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int calltype,
+ dav_lock **locks)
+{
+ pool *p = lockdb->info->pool;
+ dav_datum key;
+ dav_error *err;
+ dav_lock *lock = NULL;
+ dav_lock *newlock;
+ dav_lock_discovery *dp;
+ dav_lock_indirect *ip;
+
+#if DAV_DEBUG
+ if (calltype == DAV_GETLOCKS_COMPLETE) {
+ return dav_new_error(lockdb->info->pool,
+ HTTP_INTERNAL_SERVER_ERROR, 0,
+ "INTERNAL DESIGN ERROR: DAV_GETLOCKS_COMPLETE "
+ "is not yet supported");
+ }
+#endif
+
+ key = dav_fs_build_key(p, resource);
+ if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST,
+ &dp, &ip)) != NULL) {
+ /* ### push a higher-level desc? */
+ return err;
+ }
+
+ /* copy all direct locks to the result list */
+ for (; dp != NULL; dp = dp->next) {
+ newlock = dav_fs_alloc_lock(lockdb, key, dp->locktoken);
+ newlock->is_locknull = !resource->exists;
+ newlock->scope = dp->f.scope;
+ newlock->type = dp->f.type;
+ newlock->depth = dp->f.depth;
+ newlock->timeout = dp->f.timeout;
+ newlock->owner = dp->owner;
+ newlock->auth_user = dp->auth_user;
+
+ /* hook into the result list */
+ newlock->next = lock;
+ lock = newlock;
+ }
+
+ /* copy all the indirect locks to the result list. resolve as needed. */
+ for (; ip != NULL; ip = ip->next) {
+ newlock = dav_fs_alloc_lock(lockdb, ip->key, ip->locktoken);
+ newlock->is_locknull = !resource->exists;
+
+ if (calltype == DAV_GETLOCKS_RESOLVED) {
+ if ((err = dav_fs_resolve(lockdb, ip, &dp, NULL, NULL)) != NULL) {
+ /* ### push a higher-level desc? */
+ return err;
+ }
+
+ newlock->scope = dp->f.scope;
+ newlock->type = dp->f.type;
+ newlock->depth = dp->f.depth;
+ newlock->timeout = dp->f.timeout;
+ newlock->owner = dp->owner;
+ newlock->auth_user = dp->auth_user;
+ }
+ else {
+ /* DAV_GETLOCKS_PARTIAL */
+ newlock->rectype = DAV_LOCKREC_INDIRECT_PARTIAL;
+ }
+
+ /* hook into the result list */
+ newlock->next = lock;
+ lock = newlock;
+ }
+
+ *locks = lock;
+ return NULL;
+}
+
+static dav_error * dav_fs_find_lock(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ const dav_locktoken *locktoken,
+ int partial_ok,
+ dav_lock **lock)
+{
+ dav_error *err;
+ dav_datum key;
+ dav_lock_discovery *dp;
+ dav_lock_indirect *ip;
+
+ *lock = NULL;
+
+ key = dav_fs_build_key(lockdb->info->pool, resource);
+ if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST,
+ &dp, &ip)) != NULL) {
+ /* ### push a higher-level desc? */
+ return err;
+ }
+
+ for (; dp != NULL; dp = dp->next) {
+ if (!dav_compare_opaquelocktoken(locktoken->uuid,
+ dp->locktoken->uuid)) {
+ *lock = dav_fs_alloc_lock(lockdb, key, locktoken);
+ (*lock)->is_locknull = !resource->exists;
+ (*lock)->scope = dp->f.scope;
+ (*lock)->type = dp->f.type;
+ (*lock)->depth = dp->f.depth;
+ (*lock)->timeout = dp->f.timeout;
+ (*lock)->owner = dp->owner;
+ (*lock)->auth_user = dp->auth_user;
+ return NULL;
+ }
+ }
+
+ for (; ip != NULL; ip = ip->next) {
+ if (!dav_compare_opaquelocktoken(locktoken->uuid,
+ ip->locktoken->uuid)) {
+ *lock = dav_fs_alloc_lock(lockdb, ip->key, locktoken);
+ (*lock)->is_locknull = !resource->exists;
+
+ /* ### nobody uses the resolving right now! */
+ if (partial_ok) {
+ (*lock)->rectype = DAV_LOCKREC_INDIRECT_PARTIAL;
+ }
+ else {
+ (*lock)->rectype = DAV_LOCKREC_INDIRECT;
+ if ((err = dav_fs_resolve(lockdb, ip, &dp,
+ NULL, NULL)) != NULL) {
+ /* ### push a higher-level desc? */
+ return err;
+ }
+ (*lock)->scope = dp->f.scope;
+ (*lock)->type = dp->f.type;
+ (*lock)->depth = dp->f.depth;
+ (*lock)->timeout = dp->f.timeout;
+ (*lock)->owner = dp->owner;
+ (*lock)->auth_user = dp->auth_user;
+ }
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static dav_error * dav_fs_has_locks(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int *locks_present)
+{
+ dav_error *err;
+ dav_datum key;
+
+ *locks_present = 0;
+
+ if ((err = dav_fs_really_open_lockdb(lockdb)) != NULL) {
+ /* ### insert a higher-level error description */
+ return err;
+ }
+
+ /*
+ ** If we opened readonly and the db wasn't there, then there are no
+ ** locks for this resource. Just exit.
+ */
+ if (lockdb->info->db == NULL)
+ return NULL;
+
+ key = dav_fs_build_key(lockdb->info->pool, resource);
+
+ *locks_present = (*dav_hooks_db_dbm.exists)(lockdb->info->db, key);
+
+ return NULL;
+}
+
+static dav_error * dav_fs_append_locks(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int make_indirect,
+ const dav_lock *lock)
+{
+ pool *p = lockdb->info->pool;
+ dav_error *err;
+ dav_lock_indirect *ip;
+ dav_lock_discovery *dp;
+ dav_datum key;
+
+ key = dav_fs_build_key(lockdb->info->pool, resource);
+ if ((err = dav_fs_load_lock_record(lockdb, key, 0, &dp, &ip)) != NULL) {
+ /* ### maybe add in a higher-level description */
+ return err;
+ }
+
+ /*
+ ** ### when we store the lock more directly, we need to update
+ ** ### lock->rectype and lock->is_locknull
+ */
+
+ if (make_indirect) {
+ for (; lock != NULL; lock = lock->next) {
+
+ /* ### this works for any <lock> rectype */
+ dav_lock_indirect *newi = ap_pcalloc(p, sizeof(*newi));
+
+ /* ### shut off the const warning for now */
+ newi->locktoken = (dav_locktoken *)lock->locktoken;
+ newi->timeout = lock->timeout;
+ newi->key = lock->info->key;
+ newi->next = ip;
+ ip = newi;
+ }
+ }
+ else {
+ for (; lock != NULL; lock = lock->next) {
+ /* create and link in the right kind of lock */
+
+ if (lock->rectype == DAV_LOCKREC_DIRECT) {
+ dav_lock_discovery *newd = ap_pcalloc(p, sizeof(*newd));
+
+ newd->f.scope = lock->scope;
+ newd->f.type = lock->type;
+ newd->f.depth = lock->depth;
+ newd->f.timeout = lock->timeout;
+ /* ### shut off the const warning for now */
+ newd->locktoken = (dav_locktoken *)lock->locktoken;
+ newd->owner = lock->owner;
+ newd->auth_user = lock->auth_user;
+ newd->next = dp;
+ dp = newd;
+ }
+ else {
+ /* DAV_LOCKREC_INDIRECT(_PARTIAL) */
+
+ dav_lock_indirect *newi = ap_pcalloc(p, sizeof(*newi));
+
+ /* ### shut off the const warning for now */
+ newi->locktoken = (dav_locktoken *)lock->locktoken;
+ newi->key = lock->info->key;
+ newi->next = ip;
+ ip = newi;
+ }
+ }
+ }
+
+ if ((err = dav_fs_save_lock_record(lockdb, key, dp, ip)) != NULL) {
+ /* ### maybe add a higher-level description */
+ return err;
+ }
+
+ /* we have a special list for recording locknull resources */
+ /* ### ack! this can add two copies to the locknull list */
+ if (!resource->exists
+ && (err = dav_fs_add_locknull_state(lockdb, resource)) != NULL) {
+ /* ### maybe add a higher-level description */
+ return err;
+ }
+
+ return NULL;
+}
+
+static dav_error * dav_fs_remove_lock(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ const dav_locktoken *locktoken)
+{
+ dav_error *err;
+ dav_buffer buf = { 0 };
+ dav_lock_discovery *dh = NULL;
+ dav_lock_indirect *ih = NULL;
+ dav_datum key;
+
+ key = dav_fs_build_key(lockdb->info->pool, resource);
+
+ if (locktoken != NULL) {
+ dav_lock_discovery *dp;
+ dav_lock_discovery *dprev = NULL;
+ dav_lock_indirect *ip;
+ dav_lock_indirect *iprev = NULL;
+
+ if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST,
+ &dh, &ih)) != NULL) {
+ /* ### maybe add a higher-level description */
+ return err;
+ }
+
+ for (dp = dh; dp != NULL; dp = dp->next) {
+ if (dav_compare_opaquelocktoken(locktoken->uuid,
+ dp->locktoken->uuid) == 0) {
+ if (dprev)
+ dprev->next = dp->next;
+ else
+ dh = dh->next;
+ }
+ dprev = dp;
+ }
+
+ for (ip = ih; ip != NULL; ip = ip->next) {
+ if (dav_compare_opaquelocktoken(locktoken->uuid,
+ ip->locktoken->uuid) == 0) {
+ if (iprev)
+ iprev->next = ip->next;
+ else
+ ih = ih->next;
+ }
+ iprev = ip;
+ }
+
+ }
+
+ /* save the modified locks, or remove all locks (dh=ih=NULL). */
+ if ((err = dav_fs_save_lock_record(lockdb, key, dh, ih)) != NULL) {
+ /* ### maybe add a higher-level description */
+ return err;
+ }
+
+ /*
+ ** If this resource is a locknull resource AND no more locks exist,
+ ** then remove the locknull member.
+ **
+ ** Note: remove_locknull_state() attempts to convert a locknull member
+ ** to a real member. In this case, all locks are gone, so the
+ ** locknull resource returns to the null state (ie. doesn't exist),
+ ** so there is no need to update the lockdb (and it won't find
+ ** any because a precondition is that none exist).
+ */
+ if (!resource->exists && dh == NULL && ih == NULL
+ && (err = dav_fs_remove_locknull_member(lockdb->info->pool,
+ dav_fs_pathname(resource),
+ &buf)) != NULL) {
+ /* ### maybe add a higher-level description */
+ return err;
+ }
+
+ return NULL;
+}
+
+static int dav_fs_do_refresh(dav_lock_discovery *dp,
+ const dav_locktoken_list *ltl,
+ time_t new_time)
+{
+ int dirty = 0;
+
+ for (; ltl != NULL; ltl = ltl->next) {
+ if (dav_compare_opaquelocktoken(dp->locktoken->uuid,
+ ltl->locktoken->uuid) == 0)
+ {
+ dp->f.timeout = new_time;
+ dirty = 1;
+ }
+ }
+
+ return dirty;
+}
+
+static dav_error * dav_fs_refresh_locks(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ const dav_locktoken_list *ltl,
+ time_t new_time,
+ dav_lock **locks)
+{
+ dav_error *err;
+ dav_datum key;
+ dav_lock_discovery *dp;
+ dav_lock_discovery *dp_scan;
+ dav_lock_indirect *ip;
+ int dirty = 0;
+ dav_lock *newlock;
+
+ *locks = NULL;
+
+ key = dav_fs_build_key(lockdb->info->pool, resource);
+ if ((err = dav_fs_load_lock_record(lockdb, key, DAV_CREATE_LIST,
+ &dp, &ip)) != NULL) {
+ /* ### maybe add in a higher-level description */
+ return err;
+ }
+
+ /* ### we should be refreshing direct AND (resolved) indirect locks! */
+
+ /* refresh all of the direct locks on this resource */
+ for (dp_scan = dp; dp_scan != NULL; dp_scan = dp_scan->next) {
+ if (dav_fs_do_refresh(dp_scan, ltl, new_time)) {
+ /* the lock was refreshed. return the lock. */
+ newlock = dav_fs_alloc_lock(lockdb, key, dp_scan->locktoken);
+ newlock->is_locknull = !resource->exists;
+ newlock->scope = dp_scan->f.scope;
+ newlock->type = dp_scan->f.type;
+ newlock->depth = dp_scan->f.depth;
+ newlock->timeout = dp_scan->f.timeout;
+ newlock->owner = dp_scan->owner;
+ newlock->auth_user = dp_scan->auth_user;
+
+ newlock->next = *locks;
+ *locks = newlock;
+
+ dirty = 1;
+ }
+ }
+
+ /* if we refreshed any locks, then save them back. */
+ if (dirty
+ && (err = dav_fs_save_lock_record(lockdb, key, dp, ip)) != NULL) {
+ /* ### maybe add in a higher-level description */
+ return err;
+ }
+
+ /* for each indirect lock, find its direct lock and refresh it. */
+ for (; ip != NULL; ip = ip->next) {
+ dav_lock_discovery *ref_dp;
+ dav_lock_indirect *ref_ip;
+
+ if ((err = dav_fs_resolve(lockdb, ip, &dp_scan,
+ &ref_dp, &ref_ip)) != NULL) {
+ /* ### push a higher-level desc? */
+ return err;
+ }
+ if (dav_fs_do_refresh(dp_scan, ltl, new_time)) {
+ /* the lock was refreshed. return the lock. */
+ newlock = dav_fs_alloc_lock(lockdb, ip->key, dp->locktoken);
+ newlock->is_locknull = !resource->exists;
+ newlock->scope = dp->f.scope;
+ newlock->type = dp->f.type;
+ newlock->depth = dp->f.depth;
+ newlock->timeout = dp->f.timeout;
+ newlock->owner = dp->owner;
+ newlock->auth_user = dp_scan->auth_user;
+
+ newlock->next = *locks;
+ *locks = newlock;
+
+ /* save the (resolved) direct lock back */
+ if ((err = dav_fs_save_lock_record(lockdb, ip->key, ref_dp,
+ ref_ip)) != NULL) {
+ /* ### push a higher-level desc? */
+ return err;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+const dav_hooks_locks dav_hooks_locks_fs =
+{
+ dav_fs_get_supportedlock,
+ dav_fs_parse_locktoken,
+ dav_fs_format_locktoken,
+ dav_fs_compare_locktoken,
+ dav_fs_open_lockdb,
+ dav_fs_close_lockdb,
+ dav_fs_remove_locknull_state,
+ dav_fs_create_lock,
+ dav_fs_get_locks,
+ dav_fs_find_lock,
+ dav_fs_has_locks,
+ dav_fs_append_locks,
+ dav_fs_remove_lock,
+ dav_fs_refresh_locks,
+ NULL, /* get_resource */
+};
diff --git a/modules/dav/fs/mod_dav_fs.c b/modules/dav/fs/mod_dav_fs.c
new file mode 100644
index 0000000000..0fc9fecd05
--- /dev/null
+++ b/modules/dav/fs/mod_dav_fs.c
@@ -0,0 +1,137 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+
+#include "mod_dav.h"
+#include "repos.h"
+
+/* per-server configuration */
+typedef struct {
+ const char *lockdb_path;
+
+} dav_fs_server_conf;
+
+extern module MODULE_VAR_EXPORT dav_fs_module;
+
+const char *dav_get_lockdb_path(const request_rec *r)
+{
+ dav_fs_server_conf *conf;
+
+ conf = ap_get_module_config(r->server->module_config, &dav_fs_module);
+ return conf->lockdb_path;
+}
+
+static void *dav_fs_create_server_config(ap_pool_t *p, server_rec *s)
+{
+ return ap_pcalloc(p, sizeof(dav_fs_server_conf));
+}
+
+static void *dav_fs_merge_server_config(ap_pool_t *p,
+ void *base, void *overrides)
+{
+ dav_fs_server_conf *parent = base;
+ dav_fs_server_conf *child = overrides;
+ dav_fs_server_conf *newconf;
+
+ newconf = ap_pcalloc(p, sizeof(*newconf));
+
+ newconf->lockdb_path =
+ child->lockdb_path ? child->lockdb_path : parent->lockdb_path;
+
+ return newconf;
+}
+
+/*
+ * Command handler for the DAVLockDB directive, which is TAKE1
+ */
+static const char *dav_fs_cmd_davlockdb(cmd_parms *cmd, void *config,
+ const char *arg1)
+{
+ dav_fs_server_conf *conf;
+
+ conf = ap_get_module_config(cmd->server->module_config,
+ &dav_fs_module);
+ arg1 = ap_os_canonical_filename(cmd->pool, arg1);
+ conf->lockdb_path = ap_server_root_relative(cmd->pool, arg1);
+
+ return NULL;
+}
+
+static const command_rec dav_fs_cmds[] =
+{
+ /* per server */
+ AP_INIT_TAKE1("DAVLockDB", dav_fs_cmd_davlockdb, NULL, RSRC_CONF,
+ "specify a lock database"),
+
+ { NULL }
+};
+
+static void register_hooks(void)
+{
+ /* nothing yet */
+}
+
+module MODULE_VAR_EXPORT dav_fs_module =
+{
+ STANDARD20_MODULE_STUFF,
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ dav_fs_create_server_config, /* server config */
+ dav_fs_merge_server_config, /* merge server config */
+ dav_fs_cmds, /* command table */
+ NULL, /* handlers */
+ register_hooks, /* register hooks */
+};
diff --git a/modules/dav/fs/mod_dav_fs.dsp b/modules/dav/fs/mod_dav_fs.dsp
new file mode 100644
index 0000000000..ac52774d5a
--- /dev/null
+++ b/modules/dav/fs/mod_dav_fs.dsp
@@ -0,0 +1,120 @@
+# Microsoft Developer Studio Project File - Name="mod_dav_fs" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_dav_fs - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "mod_dav_fs.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "mod_dav_fs.mak" CFG="mod_dav_fs - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_dav_fs - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_dav_fs - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "mod_dav_fs - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\ApacheMo"
+# PROP BASE Intermediate_Dir ".\ApacheMo"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\mod_dav_fsR"
+# PROP Intermediate_Dir ".\mod_dav_fsR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\modules\dav\main" /I "..\..\lib\sdbm" /I "..\..\lib\expat-lite" /I "..\..\lib\apr\include" /I "..\..\include" /I "..\..\os\win32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 ApacheCore.lib aprlib.lib kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /map /machine:I386 /libpath:"..\..\CoreR" /libpath:"..\..\lib\apr\Release" /base:@BaseAddr.ref,mod_dav_fs
+# ADD LINK32 ApacheCore.lib aprlib.lib kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /map /machine:I386 /libpath:"..\..\CoreR" /libpath:"..\..\lib\apr\Release" /base:@BaseAddr.ref,mod_dav_fs
+
+!ELSEIF "$(CFG)" == "mod_dav_fs - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\ApacheM0"
+# PROP BASE Intermediate_Dir ".\ApacheM0"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\mod_dav_fsD"
+# PROP Intermediate_Dir ".\mod_dav_fsD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\modules\dav\main" /I "..\..\lib\sdbm" /I "..\..\lib\expat-lite" /I "..\..\lib\apr\include" /I "..\..\include" /I "..\..\os\win32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 ApacheCore.lib aprlib.lib kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /map /debug /machine:I386 /libpath:"..\..\CoreD" /libpath:"..\..\lib\apr\Debug" /base:@BaseAddr.ref,mod_dav_fs
+# ADD LINK32 ApacheCore.lib aprlib.lib kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /debug /machine:I386 /libpath:"..\..\CoreD" /libpath:"..\..\lib\apr\Debug" /base:@BaseAddr.ref,mod_dav_fs
+# SUBTRACT LINK32 /incremental:no /map
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_dav_fs - Win32 Release"
+# Name "mod_dav_fs - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\dav\fs\dbm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\modules\dav\fs\lock.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\modules\dav\fs\mod_dav_fs.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\modules\dav\fs\repos.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=..\..\modules\dav\fs\repos.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/modules/dav/fs/repos.c b/modules/dav/fs/repos.c
new file mode 100644
index 0000000000..26471ac901
--- /dev/null
+++ b/modules/dav/fs/repos.c
@@ -0,0 +1,2007 @@
+/*
+** Copyright (C) 1998-2000 Greg Stein. All Rights Reserved.
+**
+** By using this file, you agree to the terms and conditions set forth in
+** the LICENSE.html file which can be found at the top level of the mod_dav
+** distribution or at http://www.webdav.org/mod_dav/license-1.html.
+**
+** Contact information:
+** Greg Stein, PO Box 760, Palo Alto, CA, 94302
+** gstein@lyra.org, http://www.webdav.org/mod_dav/
+**
+*/
+
+/*
+** DAV filesystem-based repository provider
+**
+** Written 08/99 by John Vasta, vasta@rational.com, by separating
+** mod_dav into repository-independent and provider modules.
+*/
+
+#include <string.h>
+
+#include "httpd.h"
+#include "http_log.h"
+#include "http_protocol.h" /* for ap_set_* (in dav_fs_set_headers) */
+#include "http_request.h" /* for ap_update_mtime() */
+
+#include "mod_dav.h"
+#include "dav_fs_repos.h"
+
+
+/* to assist in debugging mod_dav's GET handling */
+#define DEBUG_GET_HANDLER 0
+#define DEBUG_PATHNAME_STYLE 0
+
+#define DAV_FS_COPY_BLOCKSIZE 16384 /* copy 16k at a time */
+
+/* context needed to identify a resource */
+struct dav_resource_private {
+ pool *pool; /* memory storage pool associated with request */
+ const char *pathname; /* full pathname to resource */
+ struct stat finfo; /* filesystem info */
+};
+
+/* private context for doing a filesystem walk */
+typedef struct {
+ dav_walker_ctx *wctx;
+
+ dav_resource res1;
+ dav_resource res2;
+ dav_resource_private info1;
+ dav_resource_private info2;
+ dav_buffer path1;
+ dav_buffer path2;
+
+ dav_buffer locknull_buf;
+
+} dav_fs_walker_context;
+
+/* pull this in from the other source file */
+extern const dav_hooks_locks dav_hooks_locks_fs;
+
+/* forward-declare this sucker */
+static const dav_hooks_repository dav_hooks_repository_fs;
+
+/*
+** The Provider ID is used to differentiate "logical" providers that use
+** the same set of hook functions. Essentially, the ID is an instance
+** handle and the hooks are a vtable.
+**
+** In this module, we only have a single provider for each type, so we
+** actually ignore the Provider ID.
+*/
+#define DAV_FS_PROVIDER_ID 0
+
+/*
+** The namespace URIs that we use. This list and the enumeration must
+** stay in sync.
+*/
+static const char * const dav_fs_namespace_uris[] =
+{
+ "DAV:",
+ "http://apache.org/dav/props/",
+
+ NULL /* sentinel */
+};
+enum {
+ DAV_FS_URI_DAV, /* the DAV: namespace URI */
+ DAV_FS_URI_MYPROPS /* the namespace URI for our custom props */
+};
+
+/*
+** The properties that we define.
+*/
+enum {
+ /* using DAV_FS_URI_DAV */
+ DAV_PROPID_FS_creationdate = DAV_PROPID_FS,
+ DAV_PROPID_FS_displayname,
+ DAV_PROPID_FS_getcontentlength,
+ DAV_PROPID_FS_getetag,
+ DAV_PROPID_FS_getlastmodified,
+ DAV_PROPID_FS_source,
+
+ /* using DAV_FS_URI_MYPROPS */
+ DAV_PROPID_FS_executable
+};
+/* NOTE: the magic "200" is derived from the ranges in mod_dav.h */
+#define DAV_PROPID_FS_OURS(id) (DAV_PROPID_FS <= (id) && \
+ (id) < DAV_PROPID_FS + 200)
+
+typedef struct {
+ int ns;
+ const char * name;
+
+ int propid;
+} dav_fs_liveprop_name;
+
+static const dav_fs_liveprop_name dav_fs_props[] =
+{
+ { DAV_FS_URI_DAV, "creationdate", DAV_PROPID_FS_creationdate },
+ { DAV_FS_URI_DAV, "getcontentlength", DAV_PROPID_FS_getcontentlength },
+ { DAV_FS_URI_DAV, "getetag", DAV_PROPID_FS_getetag },
+ { DAV_FS_URI_DAV, "getlastmodified", DAV_PROPID_FS_getlastmodified },
+
+ { DAV_FS_URI_MYPROPS, "executable", DAV_PROPID_FS_executable },
+
+ /* ### these aren't FS specific */
+ { DAV_FS_URI_DAV, "displayname", DAV_PROPID_FS_displayname },
+ { DAV_FS_URI_DAV, "source", DAV_PROPID_FS_source },
+
+ { 0 } /* sentinel */
+};
+
+
+/* define the dav_stream structure for our use */
+struct dav_stream {
+ pool *p;
+ int fd;
+ const char *pathname; /* we may need to remove it at close time */
+};
+
+/* forward declaration for internal treewalkers */
+static dav_error * dav_fs_walk(dav_walker_ctx *wctx, int depth);
+
+/* --------------------------------------------------------------------
+**
+** PRIVATE REPOSITORY FUNCTIONS
+*/
+pool *dav_fs_pool(const dav_resource *resource)
+{
+ return resource->info->pool;
+}
+
+const char *dav_fs_pathname(const dav_resource *resource)
+{
+ return resource->info->pathname;
+}
+
+void dav_fs_dir_file_name(
+ const dav_resource *resource,
+ const char **dirpath_p,
+ const char **fname_p)
+{
+ dav_resource_private *ctx = resource->info;
+
+ if (resource->collection) {
+ *dirpath_p = ctx->pathname;
+ if (fname_p != NULL)
+ *fname_p = NULL;
+ }
+ else {
+ char *dirpath = ap_make_dirstr_parent(ctx->pool, ctx->pathname);
+ size_t dirlen = strlen(dirpath);
+
+ if (fname_p != NULL)
+ *fname_p = ctx->pathname + dirlen;
+ *dirpath_p = dirpath;
+
+ /* remove trailing slash from dirpath, unless it's the root dir */
+ /* ### Win32 check */
+ if (dirlen > 1 && dirpath[dirlen - 1] == '/') {
+ dirpath[dirlen - 1] = '\0';
+ }
+ }
+}
+
+/* Note: picked up from ap_gm_timestr_822() */
+/* NOTE: buf must be at least DAV_TIMEBUF_SIZE chars in size */
+static void dav_format_time(int style, time_t sec, char *buf)
+{
+ struct tm *tms;
+
+ tms = gmtime(&sec);
+
+ if (style == DAV_STYLE_ISO8601) {
+ /* ### should we use "-00:00" instead of "Z" ?? */
+
+ /* 20 chars plus null term */
+ sprintf(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2dZ",
+ tms->tm_year + 1900, tms->tm_mon + 1, tms->tm_mday,
+ tms->tm_hour, tms->tm_min, tms->tm_sec);
+ return;
+ }
+
+ /* RFC 822 date format; as strftime '%a, %d %b %Y %T GMT' */
+
+ /* 29 chars plus null term */
+ sprintf(buf,
+ "%s, %.2d %s %d %.2d:%.2d:%.2d GMT",
+ ap_day_snames[tms->tm_wday],
+ tms->tm_mday, ap_month_snames[tms->tm_mon],
+ tms->tm_year + 1900,
+ tms->tm_hour, tms->tm_min, tms->tm_sec);
+}
+
+static int dav_sync_write(int fd, const char *buf, ssize_t bufsize)
+{
+ ssize_t amt;
+
+ do {
+ amt = write(fd, buf, bufsize);
+ if (amt > 0) {
+ bufsize -= amt;
+ buf += amt;
+ }
+ } while (amt > 0 && bufsize > 0);
+
+ return amt < 0 ? -1 : 0;
+}
+
+static dav_error * dav_fs_copymove_file(
+ int is_move,
+ pool * p,
+ const char *src,
+ const char *dst,
+ dav_buffer *pbuf)
+{
+ dav_buffer work_buf = { 0 };
+ int fdi;
+ int fdo;
+
+ if (pbuf == NULL)
+ pbuf = &work_buf;
+
+ dav_set_bufsize(p, pbuf, DAV_FS_COPY_BLOCKSIZE);
+
+ if ((fdi = open(src, O_RDONLY | O_BINARY)) == -1) {
+ /* ### use something besides 500? */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not open file for reading");
+ }
+
+ /* ### do we need to deal with the umask? */
+ if ((fdo = open(dst, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ DAV_FS_MODE_FILE)) == -1) {
+ close(fdi);
+
+ /* ### use something besides 500? */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not open file for writing");
+ }
+
+ while (1) {
+ ssize_t len = read(fdi, pbuf->buf, DAV_FS_COPY_BLOCKSIZE);
+
+ if (len == -1) {
+ close(fdi);
+ close(fdo);
+
+ if (remove(dst) != 0) {
+ /* ### ACK! Inconsistent state... */
+
+ /* ### use something besides 500? */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not delete output after read "
+ "failure. Server is now in an "
+ "inconsistent state.");
+ }
+
+ /* ### use something besides 500? */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not read input file");
+ }
+ if (len == 0)
+ break;
+
+ if (dav_sync_write(fdo, pbuf->buf, len) != 0) {
+ int save_errno = errno;
+
+ close(fdi);
+ close(fdo);
+
+ if (remove(dst) != 0) {
+ /* ### ACK! Inconsistent state... */
+
+ /* ### use something besides 500? */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not delete output after write "
+ "failure. Server is now in an "
+ "inconsistent state.");
+ }
+
+ if (save_errno == ENOSPC) {
+ return dav_new_error(p, HTTP_INSUFFICIENT_STORAGE, 0,
+ "There is not enough storage to write to "
+ "this resource.");
+ }
+
+ /* ### use something besides 500? */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not write output file");
+ }
+ }
+
+ close(fdi);
+ close(fdo);
+
+ if (is_move && remove(src) != 0) {
+ dav_error *err;
+ int save_errno = errno; /* save the errno that got us here */
+
+ if (remove(dst) != 0) {
+ /* ### ACK. this creates an inconsistency. do more!? */
+
+ /* ### use something besides 500? */
+ /* Note that we use the latest errno */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not remove source or destination "
+ "file. Server is now in an inconsistent "
+ "state.");
+ }
+
+ /* ### use something besides 500? */
+ err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not remove source file after move. "
+ "Destination was removed to ensure consistency.");
+ err->save_errno = save_errno;
+ return err;
+ }
+
+ return NULL;
+}
+
+/* copy/move a file from within a state dir to another state dir */
+/* ### need more buffers to replace the pool argument */
+static dav_error * dav_fs_copymove_state(
+ int is_move,
+ pool * p,
+ const char *src_dir, const char *src_file,
+ const char *dst_dir, const char *dst_file,
+ dav_buffer *pbuf)
+{
+ struct stat src_finfo; /* finfo for source file */
+ struct stat dst_state_finfo; /* finfo for STATE directory */
+ const char *src;
+ const char *dst;
+
+ /* build the propset pathname for the source file */
+ src = ap_pstrcat(p, src_dir, "/" DAV_FS_STATE_DIR "/", src_file, NULL);
+
+ /* the source file doesn't exist */
+ if (stat(src, &src_finfo) != 0) {
+ return NULL;
+ }
+
+ /* build the pathname for the destination state dir */
+ dst = ap_pstrcat(p, dst_dir, "/" DAV_FS_STATE_DIR, NULL);
+
+ /* ### do we need to deal with the umask? */
+
+ /* ensure that it exists */
+ if (mkdir(dst, DAV_FS_MODE_DIR) != 0) {
+ if (errno != EEXIST) {
+ /* ### use something besides 500? */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not create internal state directory");
+ }
+ }
+
+ /* get info about the state directory */
+ if (stat(dst, &dst_state_finfo) != 0) {
+ /* Ack! Where'd it go? */
+ /* ### use something besides 500? */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "State directory disappeared");
+ }
+
+ /* The mkdir() may have failed because a *file* exists there already */
+ if (!S_ISDIR(dst_state_finfo.st_mode)) {
+ /* ### try to recover by deleting this file? (and mkdir again) */
+ /* ### use something besides 500? */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "State directory is actually a file");
+ }
+
+ /* append the target file to the state directory pathname */
+ dst = ap_pstrcat(p, dst, "/", dst_file, NULL);
+
+ /* copy/move the file now */
+ if (is_move && src_finfo.st_dev == dst_state_finfo.st_dev) {
+ /* simple rename is possible since it is on the same device */
+ if (rename(src, dst) != 0) {
+ /* ### use something besides 500? */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not move state file.");
+ }
+ }
+ else {
+ /* gotta copy (and delete) */
+ return dav_fs_copymove_file(is_move, p, src, dst, pbuf);
+ }
+
+ return NULL;
+}
+
+static dav_error *dav_fs_copymoveset(int is_move, pool *p,
+ const dav_resource *src,
+ const dav_resource *dst,
+ dav_buffer *pbuf)
+{
+ const char *src_dir;
+ const char *src_file;
+ const char *src_state1;
+ const char *src_state2;
+ const char *dst_dir;
+ const char *dst_file;
+ const char *dst_state1;
+ const char *dst_state2;
+ dav_error *err;
+
+ /* Get directory and filename for resources */
+ dav_fs_dir_file_name(src, &src_dir, &src_file);
+ dav_fs_dir_file_name(dst, &dst_dir, &dst_file);
+
+ /* Get the corresponding state files for each resource */
+ dav_dbm_get_statefiles(p, src_file, &src_state1, &src_state2);
+ dav_dbm_get_statefiles(p, dst_file, &dst_state1, &dst_state2);
+#if DAV_DEBUG
+ if ((src_state2 != NULL && dst_state2 == NULL) ||
+ (src_state2 == NULL && dst_state2 != NULL)) {
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "DESIGN ERROR: dav_dbm_get_statefiles() "
+ "returned inconsistent results.");
+ }
+#endif
+
+ err = dav_fs_copymove_state(is_move, p,
+ src_dir, src_state1,
+ dst_dir, dst_state1,
+ pbuf);
+
+ if (err == NULL && src_state2 != NULL) {
+ err = dav_fs_copymove_state(is_move, p,
+ src_dir, src_state2,
+ dst_dir, dst_state2,
+ pbuf);
+
+ if (err != NULL) {
+ /* ### CRAP. inconsistency. */
+ /* ### should perform some cleanup at the target if we still
+ ### have the original files */
+
+ /* Change the error to reflect the bad server state. */
+ err->status = HTTP_INTERNAL_SERVER_ERROR;
+ err->desc =
+ "Could not fully copy/move the properties. "
+ "The server is now in an inconsistent state.";
+ }
+ }
+
+ return err;
+}
+
+static dav_error *dav_fs_deleteset(pool *p, const dav_resource *resource)
+{
+ const char *dirpath;
+ const char *fname;
+ const char *state1;
+ const char *state2;
+ const char *pathname;
+
+ /* Get directory, filename, and state-file names for the resource */
+ dav_fs_dir_file_name(resource, &dirpath, &fname);
+ dav_dbm_get_statefiles(p, fname, &state1, &state2);
+
+ /* build the propset pathname for the file */
+ pathname = ap_pstrcat(p,
+ dirpath,
+ "/" DAV_FS_STATE_DIR "/",
+ state1,
+ NULL);
+
+ /* note: we may get ENOENT if the state dir is not present */
+ if (remove(pathname) != 0 && errno != ENOENT) {
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not remove properties.");
+ }
+
+ if (state2 != NULL) {
+ /* build the propset pathname for the file */
+ pathname = ap_pstrcat(p,
+ dirpath,
+ "/" DAV_FS_STATE_DIR "/",
+ state2,
+ NULL);
+
+ if (remove(pathname) != 0 && errno != ENOENT) {
+ /* ### CRAP. only removed half. */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not fully remove properties. "
+ "The server is now in an inconsistent "
+ "state.");
+ }
+ }
+
+ return NULL;
+}
+
+/* --------------------------------------------------------------------
+**
+** REPOSITORY HOOK FUNCTIONS
+*/
+
+static dav_resource * dav_fs_get_resource(
+ request_rec *r,
+ const char *root_dir,
+ const char *workspace)
+{
+ dav_resource_private *ctx;
+ dav_resource *resource;
+ char *s;
+ char *filename;
+ size_t len;
+
+ /* ### optimize this into a single allocation! */
+
+ /* Create private resource context descriptor */
+ ctx = ap_pcalloc(r->pool, sizeof(*ctx));
+ ctx->pool = r->pool;
+ ctx->finfo = r->finfo;
+
+ /* Preserve case on OSes which fold canonical filenames */
+#if MODULE_MAGIC_NUMBER_MAJOR > 19990320 || (MODULE_MAGIC_NUMBER_MAJOR == 19990320 && MODULE_MAGIC_NUMBER_MINOR >= 8)
+ filename = r->case_preserved_filename;
+#else
+ filename = r->filename;
+#endif
+
+ /*
+ ** If there is anything in the path_info, then this indicates that the
+ ** entire path was not used to specify the file/dir. We want to append
+ ** it onto the filename so that we get a "valid" pathname for null
+ ** resources.
+ */
+ s = ap_pstrcat(r->pool, filename, r->path_info, NULL);
+
+ /* make sure the pathname does not have a trailing "/" */
+ len = strlen(s);
+ if (len > 1 && s[len - 1] == '/') {
+ s[len - 1] = '\0';
+ }
+ ctx->pathname = s;
+
+ /* Create resource descriptor */
+ resource = ap_pcalloc(r->pool, sizeof(*resource));
+ resource->type = DAV_RESOURCE_TYPE_REGULAR;
+ resource->info = ctx;
+ resource->hooks = &dav_hooks_repository_fs;
+
+ /* make sure the URI does not have a trailing "/" */
+ len = strlen(r->uri);
+ if (len > 1 && r->uri[len - 1] == '/') {
+ s = ap_pstrdup(r->pool, r->uri);
+ s[len - 1] = '\0';
+ resource->uri = s;
+ }
+ else {
+ resource->uri = r->uri;
+ }
+
+ if (r->finfo.st_mode != 0) {
+ resource->exists = 1;
+ resource->collection = S_ISDIR(r->finfo.st_mode);
+
+ /* unused info in the URL will indicate a null resource */
+
+ if (r->path_info != NULL && *r->path_info != '\0') {
+ if (resource->collection) {
+ /* only a trailing "/" is allowed */
+ if (*r->path_info != '/' || r->path_info[1] != '\0') {
+
+ /*
+ ** This URL/filename represents a locknull resource or
+ ** possibly a destination of a MOVE/COPY
+ */
+ resource->exists = 0;
+ resource->collection = 0;
+ }
+ }
+ else
+ {
+ /*
+ ** The base of the path refers to a file -- nothing should
+ ** be in path_info. The resource is simply an error: it
+ ** can't be a null or a locknull resource.
+ */
+ return NULL; /* becomes HTTP_NOT_FOUND */
+ }
+
+ /* retain proper integrity across the structures */
+ if (!resource->exists) {
+ ctx->finfo.st_mode = 0;
+ }
+ }
+ }
+
+ return resource;
+}
+
+static dav_resource * dav_fs_get_parent_resource(const dav_resource *resource)
+{
+ dav_resource_private *ctx = resource->info;
+ dav_resource_private *parent_ctx;
+ dav_resource *parent_resource;
+ char *dirpath;
+
+ /* If given resource is root, then there is no parent */
+ if (strcmp(resource->uri, "/") == 0 ||
+#ifdef WIN32
+ (strlen(ctx->pathname) == 3 && ctx->pathname[1] == ':' && ctx->pathname[2] == '/')
+#else
+ strcmp(ctx->pathname, "/") == 0
+#endif
+ )
+ return NULL;
+
+ /* ### optimize this into a single allocation! */
+
+ /* Create private resource context descriptor */
+ parent_ctx = ap_pcalloc(ctx->pool, sizeof(*parent_ctx));
+ parent_ctx->pool = ctx->pool;
+
+ dirpath = ap_make_dirstr_parent(ctx->pool, ctx->pathname);
+ if (strlen(dirpath) > 1 && dirpath[strlen(dirpath) - 1] == '/')
+ dirpath[strlen(dirpath) - 1] = '\0';
+ parent_ctx->pathname = dirpath;
+
+ parent_resource = ap_pcalloc(ctx->pool, sizeof(*parent_resource));
+ parent_resource->info = parent_ctx;
+ parent_resource->collection = 1;
+ parent_resource->hooks = &dav_hooks_repository_fs;
+
+ if (resource->uri != NULL) {
+ char *uri = ap_make_dirstr_parent(ctx->pool, resource->uri);
+ if (strlen(uri) > 1 && uri[strlen(uri) - 1] == '/')
+ uri[strlen(uri) - 1] = '\0';
+ parent_resource->uri = uri;
+ }
+
+ if (stat(parent_ctx->pathname, &parent_ctx->finfo) == 0) {
+ parent_resource->exists = 1;
+ }
+
+ return parent_resource;
+}
+
+static int dav_fs_is_same_resource(
+ const dav_resource *res1,
+ const dav_resource *res2)
+{
+ dav_resource_private *ctx1 = res1->info;
+ dav_resource_private *ctx2 = res2->info;
+
+ if (res1->hooks != res2->hooks)
+ return 0;
+
+#ifdef WIN32
+ return stricmp(ctx1->pathname, ctx2->pathname) == 0;
+#else
+ if (ctx1->finfo.st_mode != 0)
+ return ctx1->finfo.st_ino == ctx2->finfo.st_ino;
+ else
+ return strcmp(ctx1->pathname, ctx2->pathname) == 0;
+#endif
+}
+
+static int dav_fs_is_parent_resource(
+ const dav_resource *res1,
+ const dav_resource *res2)
+{
+ dav_resource_private *ctx1 = res1->info;
+ dav_resource_private *ctx2 = res2->info;
+ size_t len1 = strlen(ctx1->pathname);
+ size_t len2;
+
+ if (res1->hooks != res2->hooks)
+ return 0;
+
+ /* it is safe to use ctx2 now */
+ len2 = strlen(ctx2->pathname);
+
+ return (len2 > len1
+ && memcmp(ctx1->pathname, ctx2->pathname, len1) == 0
+ && ctx2->pathname[len1] == '/');
+}
+
+static dav_error * dav_fs_open_stream(const dav_resource *resource,
+ dav_stream_mode mode,
+ dav_stream **stream)
+{
+ pool *p = resource->info->pool;
+ dav_stream *ds = ap_palloc(p, sizeof(*ds));
+ int flags;
+
+ switch (mode) {
+ case DAV_MODE_READ:
+ case DAV_MODE_READ_SEEKABLE:
+ default:
+ flags = O_RDONLY;
+ break;
+
+ case DAV_MODE_WRITE_TRUNC:
+ flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
+ break;
+ case DAV_MODE_WRITE_SEEKABLE:
+ flags = O_WRONLY | O_CREAT | O_BINARY;
+ break;
+ }
+
+ ds->p = p;
+ ds->pathname = resource->info->pathname;
+ ds->fd = open(ds->pathname, flags, DAV_FS_MODE_FILE);
+ if (ds->fd == -1) {
+ /* ### use something besides 500? */
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "An error occurred while opening a resource.");
+ }
+ ap_note_cleanups_for_fd(p, ds->fd);
+
+ *stream = ds;
+ return NULL;
+}
+
+static dav_error * dav_fs_close_stream(dav_stream *stream, int commit)
+{
+ ap_kill_cleanups_for_fd(stream->p, stream->fd);
+ close(stream->fd);
+
+ if (!commit) {
+ if (remove(stream->pathname) != 0) {
+ /* ### use a better description? */
+ return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "There was a problem removing (rolling "
+ "back) the resource "
+ "when it was being closed.");
+ }
+ }
+
+ return NULL;
+}
+
+static dav_error * dav_fs_read_stream(dav_stream *stream,
+ void *buf, size_t *bufsize)
+{
+ ssize_t amt;
+
+ amt = read(stream->fd, buf, *bufsize);
+ if (amt == -1) {
+ /* ### use something besides 500? */
+ return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "An error occurred while reading from a "
+ "resource.");
+ }
+ *bufsize = (size_t)amt;
+ return NULL;
+}
+
+static dav_error * dav_fs_write_stream(dav_stream *stream,
+ const void *buf, size_t bufsize)
+{
+ if (dav_sync_write(stream->fd, buf, bufsize) != 0) {
+ if (errno == ENOSPC) {
+ return dav_new_error(stream->p, HTTP_INSUFFICIENT_STORAGE, 0,
+ "There is not enough storage to write to "
+ "this resource.");
+ }
+
+ /* ### use something besides 500? */
+ return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "An error occurred while writing to a "
+ "resource.");
+ }
+ return NULL;
+}
+
+static dav_error * dav_fs_seek_stream(dav_stream *stream, off_t abs_pos)
+{
+ if (lseek(stream->fd, abs_pos, SEEK_SET) == (off_t)-1) {
+ /* ### use something besides 500? */
+ return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not seek to specified position in the "
+ "resource.");
+ }
+ return NULL;
+}
+
+static dav_error * dav_fs_set_headers(request_rec *r,
+ const dav_resource *resource)
+{
+ /* ### this function isn't really used since we have a get_pathname */
+#if DEBUG_GET_HANDLER
+ if (!resource->exists)
+ return NULL;
+
+ /* make sure the proper mtime is in the request record */
+ ap_update_mtime(r, resource->info->finfo.st_mtime);
+
+ /* ### note that these use r->filename rather than <resource> */
+ ap_set_last_modified(r);
+ ap_set_etag(r);
+
+ /* we accept byte-ranges */
+ ap_table_setn(r->headers_out, "Accept-Ranges", "bytes");
+
+ /* set up the Content-Length header */
+ ap_set_content_length(r, resource->info->finfo.st_size);
+
+ /* ### how to set the content type? */
+ /* ### until this is resolved, the Content-Type header is busted */
+
+#endif
+
+ return NULL;
+}
+
+#if DEBUG_PATHNAME_STYLE
+static const char * dav_fs_get_pathname(
+ const dav_resource *resource,
+ void **free_handle_p)
+{
+ return resource->info->pathname;
+}
+#endif
+
+static void dav_fs_free_file(void *free_handle)
+{
+ /* nothing to free ... */
+}
+
+static dav_error * dav_fs_create_collection(pool *p, dav_resource *resource)
+{
+ dav_resource_private *ctx = resource->info;
+
+ if (mkdir(ctx->pathname, DAV_FS_MODE_DIR) != 0) {
+ if (errno == ENOSPC)
+ return dav_new_error(p, HTTP_INSUFFICIENT_STORAGE, 0,
+ "There is not enough storage to create "
+ "this collection.");
+
+ /* ### refine this error message? */
+ return dav_new_error(p, HTTP_FORBIDDEN, 0,
+ "Unable to create collection.");
+ }
+
+ /* update resource state to show it exists as a collection */
+ resource->exists = 1;
+ resource->collection = 1;
+
+ return NULL;
+}
+
+static dav_error * dav_fs_copymove_walker(dav_walker_ctx *ctx, int calltype)
+{
+ dav_resource_private *srcinfo = ctx->resource->info;
+ dav_resource_private *dstinfo = ctx->res2->info;
+ dav_error *err = NULL;
+
+ if (ctx->resource->collection) {
+ if (calltype == DAV_CALLTYPE_POSTFIX) {
+ /* Postfix call for MOVE. delete the source dir.
+ * Note: when copying, we do not enable the postfix-traversal.
+ */
+ /* ### we are ignoring any error here; what should we do? */
+ (void) rmdir(srcinfo->pathname);
+ }
+ else {
+ /* copy/move of a collection. Create the new, target collection */
+ if (mkdir(dstinfo->pathname, DAV_FS_MODE_DIR) != 0) {
+ /* ### assume it was a permissions problem */
+ /* ### need a description here */
+ err = dav_new_error(ctx->pool, HTTP_FORBIDDEN, 0, NULL);
+ }
+ }
+ }
+ else {
+ err = dav_fs_copymove_file(ctx->is_move, ctx->pool, srcinfo->pathname,
+ dstinfo->pathname, &ctx->work_buf);
+ /* ### push a higher-level description? */
+ }
+
+ /*
+ ** If we have a "not so bad" error, then it might need to go into a
+ ** multistatus response.
+ **
+ ** For a MOVE, it will always go into the multistatus. It could be
+ ** that everything has been moved *except* for the root. Using a
+ ** multistatus (with no errors for the other resources) will signify
+ ** this condition.
+ **
+ ** For a COPY, we are traversing in a prefix fashion. If the root fails,
+ ** then we can just bail out now.
+ */
+ if (err != NULL
+ && !ap_is_HTTP_SERVER_ERROR(err->status)
+ && (ctx->is_move
+ || !dav_fs_is_same_resource(ctx->resource, ctx->root))) {
+ /* ### use errno to generate DAV:responsedescription? */
+ dav_add_response(ctx, ctx->resource->uri, err->status, NULL);
+
+ /* the error is in the multistatus now. do not stop the traversal. */
+ return NULL;
+ }
+
+ return err;
+}
+
+static dav_error *dav_fs_copymove_resource(
+ int is_move,
+ const dav_resource *src,
+ const dav_resource *dst,
+ int depth,
+ dav_response **response)
+{
+ dav_error *err = NULL;
+ dav_buffer work_buf = { 0 };
+
+ *response = NULL;
+
+ /* if a collection, recursively copy/move it and its children,
+ * including the state dirs
+ */
+ if (src->collection) {
+ dav_walker_ctx ctx = { 0 };
+
+ ctx.walk_type = DAV_WALKTYPE_ALL | DAV_WALKTYPE_HIDDEN;
+ ctx.func = dav_fs_copymove_walker;
+ ctx.pool = src->info->pool;
+ ctx.resource = src;
+ ctx.res2 = dst;
+ ctx.is_move = is_move;
+ ctx.postfix = is_move; /* needed for MOVE to delete source dirs */
+
+ /* copy over the source URI */
+ dav_buffer_init(ctx.pool, &ctx.uri, src->uri);
+
+ if ((err = dav_fs_walk(&ctx, depth)) != NULL) {
+ /* on a "real" error, then just punt. nothing else to do. */
+ return err;
+ }
+
+ if ((*response = ctx.response) != NULL) {
+ /* some multistatus responses exist. wrap them in a 207 */
+ return dav_new_error(src->info->pool, HTTP_MULTI_STATUS, 0,
+ "Error(s) occurred on some resources during "
+ "the COPY/MOVE process.");
+ }
+
+ return NULL;
+ }
+
+ /* not a collection */
+ if ((err = dav_fs_copymove_file(is_move, src->info->pool,
+ src->info->pathname, dst->info->pathname,
+ &work_buf)) != NULL) {
+ /* ### push a higher-level description? */
+ return err;
+ }
+
+ /* copy/move properties as well */
+ return dav_fs_copymoveset(is_move, src->info->pool, src, dst, &work_buf);
+}
+
+static dav_error * dav_fs_copy_resource(
+ const dav_resource *src,
+ dav_resource *dst,
+ int depth,
+ dav_response **response)
+{
+ dav_error *err;
+
+#if DAV_DEBUG
+ if (src->hooks != dst->hooks) {
+ /*
+ ** ### strictly speaking, this is a design error; we should not
+ ** ### have reached this point.
+ */
+ return dav_new_error(src->info->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "DESIGN ERROR: a mix of repositories "
+ "was passed to copy_resource.");
+ }
+#endif
+
+ if ((err = dav_fs_copymove_resource(0, src, dst, depth,
+ response)) == NULL) {
+
+ /* update state of destination resource to show it exists */
+ dst->exists = 1;
+ dst->collection = src->collection;
+ }
+
+ return err;
+}
+
+static dav_error * dav_fs_move_resource(
+ dav_resource *src,
+ dav_resource *dst,
+ dav_response **response)
+{
+ dav_resource_private *srcinfo = src->info;
+ dav_resource_private *dstinfo = dst->info;
+ dav_error *err;
+ int can_rename = 0;
+
+#if DAV_DEBUG
+ if (src->hooks != dst->hooks) {
+ /*
+ ** ### strictly speaking, this is a design error; we should not
+ ** ### have reached this point.
+ */
+ return dav_new_error(src->info->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "DESIGN ERROR: a mix of repositories "
+ "was passed to move_resource.");
+ }
+#endif
+
+ /* determine whether a simple rename will work.
+ * Assume source exists, else we wouldn't get called.
+ */
+ if (dstinfo->finfo.st_mode != 0) {
+ if (dstinfo->finfo.st_dev == srcinfo->finfo.st_dev) {
+ /* target exists and is on the same device. */
+ can_rename = 1;
+ }
+ }
+ else {
+ const char *dirpath;
+ struct stat finfo;
+
+ /* destination does not exist, but the parent directory should,
+ * so try it
+ */
+ dirpath = ap_make_dirstr_parent(dstinfo->pool, dstinfo->pathname);
+ if (stat(dirpath, &finfo) == 0
+ && finfo.st_dev == srcinfo->finfo.st_dev) {
+ can_rename = 1;
+ }
+ }
+
+ /* if we can't simply renamed, then do it the hard way... */
+ if (!can_rename) {
+ if ((err = dav_fs_copymove_resource(1, src, dst, DAV_INFINITY, response)) == NULL) {
+ /* update resource states */
+ dst->exists = 1;
+ dst->collection = src->collection;
+ src->exists = 0;
+ src->collection = 0;
+ }
+
+ return err;
+ }
+
+ /* a rename should work. do it, and move properties as well */
+
+ /* no multistatus response */
+ *response = NULL;
+
+ if (rename(srcinfo->pathname, dstinfo->pathname) != 0) {
+ /* ### should have a better error than this. */
+ return dav_new_error(srcinfo->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not rename resource.");
+ }
+
+ /* update resource states */
+ dst->exists = 1;
+ dst->collection = src->collection;
+ src->exists = 0;
+ src->collection = 0;
+
+ if ((err = dav_fs_copymoveset(1, src->info->pool,
+ src, dst, NULL)) == NULL) {
+ /* no error. we're done. go ahead and return now. */
+ return NULL;
+ }
+
+ /* error occurred during properties move; try to put resource back */
+ if (rename(dstinfo->pathname, srcinfo->pathname) != 0) {
+ /* couldn't put it back! */
+ return dav_push_error(srcinfo->pool,
+ HTTP_INTERNAL_SERVER_ERROR, 0,
+ "The resource was moved, but a failure "
+ "occurred during the move of its "
+ "properties. The resource could not be "
+ "restored to its original location. The "
+ "server is now in an inconsistent state.",
+ err);
+ }
+
+ /* update resource states again */
+ src->exists = 1;
+ src->collection = dst->collection;
+ dst->exists = 0;
+ dst->collection = 0;
+
+ /* resource moved back, but properties may be inconsistent */
+ return dav_push_error(srcinfo->pool,
+ HTTP_INTERNAL_SERVER_ERROR, 0,
+ "The resource was moved, but a failure "
+ "occurred during the move of its properties. "
+ "The resource was moved back to its original "
+ "location, but its properties may have been "
+ "partially moved. The server may be in an "
+ "inconsistent state.",
+ err);
+}
+
+static dav_error * dav_fs_delete_walker(dav_walker_ctx *ctx, int calltype)
+{
+ dav_resource_private *info = ctx->resource->info;
+
+ /* do not attempt to remove a null resource,
+ * or a collection with children
+ */
+ if (ctx->resource->exists &&
+ (!ctx->resource->collection || calltype == DAV_CALLTYPE_POSTFIX)) {
+ /* try to remove the resource */
+ int result;
+
+ result = ctx->resource->collection
+ ? rmdir(info->pathname)
+ : remove(info->pathname);
+
+ /*
+ ** If an error occurred, then add it to multistatus response.
+ ** Note that we add it for the root resource, too. It is quite
+ ** possible to delete the whole darn tree, yet fail on the root.
+ **
+ ** (also: remember we are deleting via a postfix traversal)
+ */
+ if (result != 0) {
+ /* ### assume there is a permissions problem */
+
+ /* ### use errno to generate DAV:responsedescription? */
+ dav_add_response(ctx, ctx->resource->uri, HTTP_FORBIDDEN, NULL);
+ }
+ }
+
+ return NULL;
+}
+
+static dav_error * dav_fs_remove_resource(dav_resource *resource,
+ dav_response **response)
+{
+ dav_resource_private *info = resource->info;
+
+ *response = NULL;
+
+ /* if a collection, recursively remove it and its children,
+ * including the state dirs
+ */
+ if (resource->collection) {
+ dav_walker_ctx ctx = { 0 };
+ dav_error *err = NULL;
+
+ ctx.walk_type = DAV_WALKTYPE_ALL | DAV_WALKTYPE_HIDDEN;
+ ctx.postfix = 1;
+ ctx.func = dav_fs_delete_walker;
+ ctx.pool = info->pool;
+ ctx.resource = resource;
+
+ dav_buffer_init(info->pool, &ctx.uri, resource->uri);
+
+ if ((err = dav_fs_walk(&ctx, DAV_INFINITY)) != NULL) {
+ /* on a "real" error, then just punt. nothing else to do. */
+ return err;
+ }
+
+ if ((*response = ctx.response) != NULL) {
+ /* some multistatus responses exist. wrap them in a 207 */
+ return dav_new_error(info->pool, HTTP_MULTI_STATUS, 0,
+ "Error(s) occurred on some resources during "
+ "the deletion process.");
+ }
+
+ /* no errors... update resource state */
+ resource->exists = 0;
+ resource->collection = 0;
+
+ return NULL;
+ }
+
+ /* not a collection; remove the file and its properties */
+ if (remove(info->pathname) != 0) {
+ /* ### put a description in here */
+ return dav_new_error(info->pool, HTTP_FORBIDDEN, 0, NULL);
+ }
+
+ /* update resource state */
+ resource->exists = 0;
+ resource->collection = 0;
+
+ /* remove properties and return its result */
+ return dav_fs_deleteset(info->pool, resource);
+}
+
+/* ### move this to dav_util? */
+/* Walk recursively down through directories, *
+ * including lock-null resources as we go. */
+dav_error * dav_fs_walker(dav_fs_walker_context *fsctx, int depth)
+{
+ dav_error *err = NULL;
+ dav_walker_ctx *wctx = fsctx->wctx;
+ int isdir = wctx->resource->collection;
+ DIR *dirp;
+ struct dirent *ep;
+
+ /* ensure the context is prepared properly, then call the func */
+ err = (*wctx->func)(wctx,
+ isdir
+ ? DAV_CALLTYPE_COLLECTION
+ : DAV_CALLTYPE_MEMBER);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (depth == 0 || !isdir) {
+ return NULL;
+ }
+
+ /* put a trailing slash onto the directory, in preparation for appending
+ * files to it as we discovery them within the directory */
+ dav_check_bufsize(wctx->pool, &fsctx->path1, DAV_BUFFER_PAD);
+ fsctx->path1.buf[fsctx->path1.cur_len++] = '/';
+ fsctx->path1.buf[fsctx->path1.cur_len] = '\0'; /* in pad area */
+
+ /* if a secondary path is present, then do that, too */
+ if (fsctx->path2.buf != NULL) {
+ dav_check_bufsize(wctx->pool, &fsctx->path2, DAV_BUFFER_PAD);
+ fsctx->path2.buf[fsctx->path2.cur_len++] = '/';
+ fsctx->path2.buf[fsctx->path2.cur_len] = '\0'; /* in pad area */
+ }
+
+ /* Note: the URI should ALREADY have a trailing "/" */
+
+ /* for this first pass of files, all resources exist */
+ fsctx->res1.exists = 1;
+
+ /* a file is the default; we'll adjust if we hit a directory */
+ fsctx->res1.collection = 0;
+ fsctx->res2.collection = 0;
+
+ /* open and scan the directory */
+ if ((dirp = opendir(fsctx->path1.buf)) == NULL) {
+ /* ### need a better error */
+ return dav_new_error(wctx->pool, HTTP_NOT_FOUND, 0, NULL);
+ }
+ while ((ep = readdir(dirp)) != NULL) {
+ size_t len = strlen(ep->d_name);
+
+ /* avoid recursing into our current, parent, or state directories */
+ if (ep->d_name[0] == '.'
+ && (len == 1 || (ep->d_name[1] == '.' && len == 2))) {
+ continue;
+ }
+
+ if (wctx->walk_type & DAV_WALKTYPE_AUTH) {
+ /* ### need to authorize each file */
+ /* ### example: .htaccess is normally configured to fail auth */
+
+ /* stuff in the state directory is never authorized! */
+ if (!strcmp(ep->d_name, DAV_FS_STATE_DIR)) {
+ continue;
+ }
+ }
+ /* skip the state dir unless a HIDDEN is performed */
+ if (!(wctx->walk_type & DAV_WALKTYPE_HIDDEN)
+ && !strcmp(ep->d_name, DAV_FS_STATE_DIR)) {
+ continue;
+ }
+
+ /* append this file onto the path buffer (copy null term) */
+ dav_buffer_place_mem(wctx->pool,
+ &fsctx->path1, ep->d_name, len + 1, 0);
+
+ if (lstat(fsctx->path1.buf, &fsctx->info1.finfo) != 0) {
+ /* woah! where'd it go? */
+ /* ### should have a better error here */
+ err = dav_new_error(wctx->pool, HTTP_NOT_FOUND, 0, NULL);
+ break;
+ }
+
+ /* copy the file to the URI, too. NOTE: we will pad an extra byte
+ for the trailing slash later. */
+ dav_buffer_place_mem(wctx->pool, &wctx->uri, ep->d_name, len + 1, 1);
+
+ /* if there is a secondary path, then do that, too */
+ if (fsctx->path2.buf != NULL) {
+ dav_buffer_place_mem(wctx->pool, &fsctx->path2,
+ ep->d_name, len + 1, 0);
+ }
+
+ /* set up the (internal) pathnames for the two resources */
+ fsctx->info1.pathname = fsctx->path1.buf;
+ fsctx->info2.pathname = fsctx->path2.buf;
+
+ /* set up the URI for the current resource */
+ fsctx->res1.uri = wctx->uri.buf;
+
+ /* ### for now, only process regular files (e.g. skip symlinks) */
+ if (S_ISREG(fsctx->info1.finfo.st_mode)) {
+ /* call the function for the specified dir + file */
+ if ((err = (*wctx->func)(wctx, DAV_CALLTYPE_MEMBER)) != NULL) {
+ /* ### maybe add a higher-level description? */
+ break;
+ }
+ }
+ else if (S_ISDIR(fsctx->info1.finfo.st_mode)) {
+ size_t save_path_len = fsctx->path1.cur_len;
+ size_t save_uri_len = wctx->uri.cur_len;
+ size_t save_path2_len = fsctx->path2.cur_len;
+
+ /* adjust length to incorporate the subdir name */
+ fsctx->path1.cur_len += len;
+ fsctx->path2.cur_len += len;
+
+ /* adjust URI length to incorporate subdir and a slash */
+ wctx->uri.cur_len += len + 1;
+ wctx->uri.buf[wctx->uri.cur_len - 1] = '/';
+ wctx->uri.buf[wctx->uri.cur_len] = '\0';
+
+ /* switch over to a collection */
+ fsctx->res1.collection = 1;
+ fsctx->res2.collection = 1;
+
+ /* recurse on the subdir */
+ /* ### don't always want to quit on error from single child */
+ if ((err = dav_fs_walker(fsctx, depth - 1)) != NULL) {
+ /* ### maybe add a higher-level description? */
+ break;
+ }
+
+ /* put the various information back */
+ fsctx->path1.cur_len = save_path_len;
+ fsctx->path2.cur_len = save_path2_len;
+ wctx->uri.cur_len = save_uri_len;
+
+ fsctx->res1.collection = 0;
+ fsctx->res2.collection = 0;
+
+ /* assert: res1.exists == 1 */
+ }
+ }
+
+ /* ### check the return value of this? */
+ closedir(dirp);
+
+ if (err != NULL)
+ return err;
+
+ if (wctx->walk_type & DAV_WALKTYPE_LOCKNULL) {
+ size_t offset = 0;
+
+ /* null terminate the directory name */
+ fsctx->path1.buf[fsctx->path1.cur_len - 1] = '\0';
+
+ /* Include any lock null resources found in this collection */
+ fsctx->res1.collection = 1;
+ if ((err = dav_fs_get_locknull_members(&fsctx->res1,
+ &fsctx->locknull_buf)) != NULL) {
+ /* ### maybe add a higher-level description? */
+ return err;
+ }
+
+ /* put a slash back on the end of the directory */
+ fsctx->path1.buf[fsctx->path1.cur_len - 1] = '/';
+
+ /* these are all non-existant (files) */
+ fsctx->res1.exists = 0;
+ fsctx->res1.collection = 0;
+ memset(&fsctx->info1.finfo, 0, sizeof(fsctx->info1.finfo));
+
+ while (offset < fsctx->locknull_buf.cur_len) {
+ size_t len = strlen(fsctx->locknull_buf.buf + offset);
+ dav_lock *locks = NULL;
+
+ /*
+ ** Append the locknull file to the paths and the URI. Note that
+ ** we don't have to pad the URI for a slash since a locknull
+ ** resource is not a collection.
+ */
+ dav_buffer_place_mem(wctx->pool, &fsctx->path1,
+ fsctx->locknull_buf.buf + offset, len + 1, 0);
+ dav_buffer_place_mem(wctx->pool, &wctx->uri,
+ fsctx->locknull_buf.buf + offset, len + 1, 0);
+ if (fsctx->path2.buf != NULL) {
+ dav_buffer_place_mem(wctx->pool, &fsctx->path2,
+ fsctx->locknull_buf.buf + offset,
+ len + 1, 0);
+ }
+
+ /* set up the (internal) pathnames for the two resources */
+ fsctx->info1.pathname = fsctx->path1.buf;
+ fsctx->info2.pathname = fsctx->path2.buf;
+
+ /* set up the URI for the current resource */
+ fsctx->res1.uri = wctx->uri.buf;
+
+ /*
+ ** To prevent a PROPFIND showing an expired locknull
+ ** resource, query the lock database to force removal
+ ** of both the lock entry and .locknull, if necessary..
+ ** Sure, the query in PROPFIND would do this.. after
+ ** the locknull resource was already included in the
+ ** return.
+ **
+ ** NOTE: we assume the caller has opened the lock database
+ ** if they have provided DAV_WALKTYPE_LOCKNULL.
+ */
+ /* ### we should also look into opening it read-only and
+ ### eliding timed-out items from the walk, yet leaving
+ ### them in the locknull database until somebody opens
+ ### the thing writable.
+ */
+ /* ### probably ought to use has_locks. note the problem
+ ### mentioned above, though... we would traverse this as
+ ### a locknull, but then a PROPFIND would load the lock
+ ### info, causing a timeout and the locks would not be
+ ### reported. Therefore, a null resource would be returned
+ ### in the PROPFIND.
+ ###
+ ### alternative: just load unresolved locks. any direct
+ ### locks will be timed out (correct). any indirect will
+ ### not (correct; consider if a parent timed out -- the
+ ### timeout routines do not walk and remove indirects;
+ ### even the resolve func would probably fail when it
+ ### tried to find a timed-out direct lock).
+ */
+ if ((err = dav_lock_query(wctx->lockdb, wctx->resource, &locks)) != NULL) {
+ /* ### maybe add a higher-level description? */
+ return err;
+ }
+
+ /* call the function for the specified dir + file */
+ if (locks != NULL &&
+ (err = (*wctx->func)(wctx, DAV_CALLTYPE_LOCKNULL)) != NULL) {
+ /* ### maybe add a higher-level description? */
+ return err;
+ }
+
+ offset += len + 1;
+ }
+
+ /* reset the exists flag */
+ fsctx->res1.exists = 1;
+ }
+
+ if (wctx->postfix) {
+ /* replace the dirs' trailing slashes with null terms */
+ fsctx->path1.buf[--fsctx->path1.cur_len] = '\0';
+ wctx->uri.buf[--wctx->uri.cur_len] = '\0';
+ if (fsctx->path2.buf != NULL) {
+ fsctx->path2.buf[--fsctx->path2.cur_len] = '\0';
+ }
+
+ /* this is a collection which exists */
+ fsctx->res1.collection = 1;
+
+ return (*wctx->func)(wctx, DAV_CALLTYPE_POSTFIX);
+ }
+
+ return NULL;
+}
+
+static dav_error * dav_fs_walk(dav_walker_ctx *wctx, int depth)
+{
+ dav_fs_walker_context fsctx = { 0 };
+
+#if DAV_DEBUG
+ if ((wctx->walk_type & DAV_WALKTYPE_LOCKNULL) != 0
+ && wctx->lockdb == NULL) {
+ return dav_new_error(wctx->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "DESIGN ERROR: walker called to walk locknull "
+ "resources, but a lockdb was not provided.");
+ }
+
+ /* ### an assertion that we have space for a trailing slash */
+ if (wctx->uri.cur_len + 1 > wctx->uri.alloc_len) {
+ return dav_new_error(wctx->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "DESIGN ERROR: walker should have been called "
+ "with padding in the URI buffer.");
+ }
+#endif
+
+ fsctx.wctx = wctx;
+
+ wctx->root = wctx->resource;
+
+ /* ### zero out versioned, working, baselined? */
+
+ fsctx.res1 = *wctx->resource;
+
+ fsctx.res1.info = &fsctx.info1;
+ fsctx.info1 = *wctx->resource->info;
+
+ dav_buffer_init(wctx->pool, &fsctx.path1, fsctx.info1.pathname);
+ fsctx.info1.pathname = fsctx.path1.buf;
+
+ if (wctx->res2 != NULL) {
+ fsctx.res2 = *wctx->res2;
+ fsctx.res2.exists = 0;
+ fsctx.res2.collection = 0;
+
+ fsctx.res2.info = &fsctx.info2;
+ fsctx.info2 = *wctx->res2->info;
+
+ /* res2 does not exist -- clear its finfo structure */
+ memset(&fsctx.info2.finfo, 0, sizeof(fsctx.info2.finfo));
+
+ dav_buffer_init(wctx->pool, &fsctx.path2, fsctx.info2.pathname);
+ fsctx.info2.pathname = fsctx.path2.buf;
+ }
+
+ /* if we have a directory, then ensure the URI has a trailing "/" */
+ if (fsctx.res1.collection
+ && wctx->uri.buf[wctx->uri.cur_len - 1] != '/') {
+
+ /* this will fall into the pad area */
+ wctx->uri.buf[wctx->uri.cur_len++] = '/';
+ wctx->uri.buf[wctx->uri.cur_len] = '\0';
+ }
+
+ /*
+ ** URI is tracked in the walker context. Ensure that people do not try
+ ** to fetch it from res2. We will ensure that res1 and uri will remain
+ ** synchronized.
+ */
+ fsctx.res1.uri = wctx->uri.buf;
+ fsctx.res2.uri = NULL;
+
+ /* use our resource structures */
+ wctx->resource = &fsctx.res1;
+ wctx->res2 = &fsctx.res2;
+
+ return dav_fs_walker(&fsctx, depth);
+}
+
+/* dav_fs_etag: Stolen from ap_make_etag. Creates a strong etag
+ * for file path.
+ * ### do we need to return weak tags sometimes?
+ */
+static const char *dav_fs_getetag(const dav_resource *resource)
+{
+ dav_resource_private *ctx = resource->info;
+
+ if (!resource->exists)
+ return ap_pstrdup(ctx->pool, "");
+
+ if (ctx->finfo.st_mode != 0) {
+ return ap_psprintf(ctx->pool, "\"%lx-%lx-%lx\"",
+ (unsigned long) ctx->finfo.st_ino,
+ (unsigned long) ctx->finfo.st_size,
+ (unsigned long) ctx->finfo.st_mtime);
+ }
+
+ return ap_psprintf(ctx->pool, "\"%lx\"", (unsigned long) ctx->finfo.st_mtime);
+}
+
+static const dav_hooks_repository dav_hooks_repository_fs =
+{
+ DEBUG_GET_HANDLER, /* normally: special GET handling not required */
+ dav_fs_get_resource,
+ dav_fs_get_parent_resource,
+ dav_fs_is_same_resource,
+ dav_fs_is_parent_resource,
+ dav_fs_open_stream,
+ dav_fs_close_stream,
+ dav_fs_read_stream,
+ dav_fs_write_stream,
+ dav_fs_seek_stream,
+ dav_fs_set_headers,
+#if DEBUG_PATHNAME_STYLE
+ dav_fs_get_pathname,
+#else
+ 0,
+#endif
+ dav_fs_free_file,
+ dav_fs_create_collection,
+ dav_fs_copy_resource,
+ dav_fs_move_resource,
+ dav_fs_remove_resource,
+ dav_fs_walk,
+ dav_fs_getetag,
+};
+
+static int dav_fs_find_prop(const char *ns_uri, const char *name)
+{
+ const dav_fs_liveprop_name *scan;
+ int ns;
+
+ if (*ns_uri == 'h'
+ && strcmp(ns_uri, dav_fs_namespace_uris[DAV_FS_URI_MYPROPS]) == 0) {
+ ns = DAV_FS_URI_MYPROPS;
+ }
+ else if (*ns_uri == 'D' && strcmp(ns_uri, "DAV:") == 0) {
+ ns = DAV_FS_URI_DAV;
+ }
+ else {
+ /* we don't define this property */
+ return 0;
+ }
+
+ for (scan = dav_fs_props; scan->name != NULL; ++scan)
+ if (ns == scan->ns && strcmp(name, scan->name) == 0)
+ return scan->propid;
+
+ return 0;
+}
+
+static dav_prop_insert dav_fs_insert_prop(const dav_resource *resource,
+ int propid, int insvalue,
+ const int *ns_map,
+ dav_text_header *phdr)
+{
+ const char *value;
+ const char *s;
+ dav_prop_insert which;
+ pool *p = resource->info->pool;
+ const dav_fs_liveprop_name *scan;
+ int ns;
+
+ /* an HTTP-date can be 29 chars plus a null term */
+ /* a 64-bit size can be 20 chars plus a null term */
+ char buf[DAV_TIMEBUF_SIZE];
+
+ if (!DAV_PROPID_FS_OURS(propid))
+ return DAV_PROP_INSERT_NOTME;
+
+ /*
+ ** None of FS provider properties are defined if the resource does not
+ ** exist. Just bail for this case.
+ **
+ ** Note that DAV:displayname and DAV:source will be stored as dead
+ ** properties; the NOTDEF return code indicates that dav_props.c should
+ ** look there for the value.
+ **
+ ** Even though we state that the FS properties are not defined, the
+ ** client cannot store dead values -- we deny that thru the is_writable
+ ** hook function.
+ */
+ if (!resource->exists)
+ return DAV_PROP_INSERT_NOTDEF;
+
+ switch (propid) {
+ case DAV_PROPID_FS_creationdate:
+ /*
+ ** Closest thing to a creation date. since we don't actually
+ ** perform the operations that would modify ctime (after we
+ ** create the file), then we should be pretty safe here.
+ */
+ dav_format_time(DAV_STYLE_ISO8601,
+ resource->info->finfo.st_ctime,
+ buf);
+ value = buf;
+ break;
+
+ case DAV_PROPID_FS_getcontentlength:
+ /* our property, but not defined on collection resources */
+ if (resource->collection)
+ return DAV_PROP_INSERT_NOTDEF;
+
+ (void) sprintf(buf, "%ld", resource->info->finfo.st_size);
+ value = buf;
+ break;
+
+ case DAV_PROPID_FS_getetag:
+ value = dav_fs_getetag(resource);
+ break;
+
+ case DAV_PROPID_FS_getlastmodified:
+ dav_format_time(DAV_STYLE_RFC822,
+ resource->info->finfo.st_mtime,
+ buf);
+ value = buf;
+ break;
+
+ case DAV_PROPID_FS_executable:
+#ifdef WIN32
+ /* our property, but not defined on the Win32 platform */
+ return DAV_PROP_INSERT_NOTDEF;
+#else
+ /* our property, but not defined on collection resources */
+ if (resource->collection)
+ return DAV_PROP_INSERT_NOTDEF;
+
+ /* the files are "ours" so we only need to check owner exec privs */
+ if (resource->info->finfo.st_mode & DAV_FS_MODE_XUSR)
+ value = "T";
+ else
+ value = "F";
+ break;
+#endif /* WIN32 */
+
+ case DAV_PROPID_FS_displayname:
+ case DAV_PROPID_FS_source:
+ default:
+ /*
+ ** This property is not defined. However, it may be a dead
+ ** property.
+ */
+ return DAV_PROP_INSERT_NOTDEF;
+ }
+
+ /* assert: value != NULL */
+
+ for (scan = dav_fs_props; scan->name != NULL; ++scan)
+ if (scan->propid == propid)
+ break;
+ /* assert: scan->name != NULL */
+
+ /* map our NS index into a global NS index */
+ ns = ns_map[scan->ns];
+
+ /* DBG3("FS: inserting lp%d:%s (local %d)", ns, scan->name, scan->ns); */
+
+ if (insvalue) {
+ /* use D: prefix to refer to the DAV: namespace URI */
+ s = ap_psprintf(p, "<lp%d:%s>%s</lp%d:%s>" DEBUG_CR,
+ ns, scan->name, value, ns, scan->name);
+ which = DAV_PROP_INSERT_VALUE;
+ }
+ else {
+ /* use D: prefix to refer to the DAV: namespace URI */
+ s = ap_psprintf(p, "<lp%d:%s/>" DEBUG_CR, ns, scan->name);
+ which = DAV_PROP_INSERT_NAME;
+ }
+ dav_text_append(p, phdr, s);
+
+ /* we inserted a name or value (this prop is done) */
+ return which;
+}
+
+static void dav_fs_insert_all(const dav_resource *resource, int insvalue,
+ const int *ns_map, dav_text_header *phdr)
+{
+ if (!resource->exists) {
+ /* a lock-null resource */
+ /*
+ ** ### technically, we should insert empty properties. dunno offhand
+ ** ### what part of the spec said this, but it was essentially thus:
+ ** ### "the properties should be defined, but may have no value".
+ */
+ return;
+ }
+
+ (void) dav_fs_insert_prop(resource, DAV_PROPID_FS_creationdate,
+ insvalue, ns_map, phdr);
+ (void) dav_fs_insert_prop(resource, DAV_PROPID_FS_getcontentlength,
+ insvalue, ns_map, phdr);
+ (void) dav_fs_insert_prop(resource, DAV_PROPID_FS_getlastmodified,
+ insvalue, ns_map, phdr);
+ (void) dav_fs_insert_prop(resource, DAV_PROPID_FS_getetag,
+ insvalue, ns_map, phdr);
+
+#ifndef WIN32
+ /*
+ ** Note: this property is not defined on the Win32 platform.
+ ** dav_fs_insert_prop() won't insert it, but we may as
+ ** well not even call it.
+ */
+ (void) dav_fs_insert_prop(resource, DAV_PROPID_FS_executable,
+ insvalue, ns_map, phdr);
+#endif
+
+ /* ### we know the others aren't defined as liveprops */
+}
+
+static dav_prop_rw dav_fs_is_writeable(const dav_resource *resource,
+ int propid)
+{
+ if (!DAV_PROPID_FS_OURS(propid))
+ return DAV_PROP_RW_NOTME;
+
+ if (propid == DAV_PROPID_FS_displayname
+ || propid == DAV_PROPID_FS_source
+#ifndef WIN32
+ /* this property is not usable (writeable) on the Win32 platform */
+ || (propid == DAV_PROPID_FS_executable && !resource->collection)
+#endif
+ )
+ return DAV_PROP_RW_YES;
+
+ return DAV_PROP_RW_NO;
+}
+
+static dav_error *dav_fs_patch_validate(const dav_resource *resource,
+ const dav_xml_elem *elem,
+ int operation,
+ void **context,
+ int *defer_to_dead)
+{
+ const dav_text *cdata;
+ const dav_text *f_cdata;
+ char value;
+
+ if (elem->propid != DAV_PROPID_FS_executable) {
+ *defer_to_dead = 1;
+ return NULL;
+ }
+
+ if (operation == DAV_PROP_OP_DELETE) {
+ return dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
+ "The 'executable' property cannot be removed.");
+ }
+
+ cdata = elem->first_cdata.first;
+ f_cdata = elem->last_child == NULL
+ ? NULL
+ : elem->last_child->following_cdata.first;
+
+ /* DBG3("name=%s cdata=%s f_cdata=%s",elem->name,cdata ? cdata->text : "[null]",f_cdata ? f_cdata->text : "[null]"); */
+
+ if (cdata == NULL) {
+ if (f_cdata == NULL) {
+ return dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
+ "The 'executable' property expects a single "
+ "character, valued 'T' or 'F'. There was no "
+ "value submitted.");
+ }
+ cdata = f_cdata;
+ }
+ else if (f_cdata != NULL)
+ goto too_long;
+
+ if (cdata->next != NULL || strlen(cdata->text) != 1)
+ goto too_long;
+
+ value = cdata->text[0];
+ if (value != 'T' && value != 'F') {
+ return dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
+ "The 'executable' property expects a single "
+ "character, valued 'T' or 'F'. The value "
+ "submitted is invalid.");
+ }
+
+ *context = (void *)(value == 'T');
+
+ return NULL;
+
+ too_long:
+ return dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
+ "The 'executable' property expects a single "
+ "character, valued 'T' or 'F'. The value submitted"
+ "has too many characters.");
+
+}
+
+static dav_error *dav_fs_patch_exec(dav_resource *resource,
+ const dav_xml_elem *elem,
+ int operation,
+ void *context,
+ dav_liveprop_rollback **rollback_ctx)
+{
+ int value = context != NULL;
+ mode_t mode = resource->info->finfo.st_mode;
+ int old_value = (resource->info->finfo.st_mode & DAV_FS_MODE_XUSR) != 0;
+
+ /* assert: prop == executable. operation == SET. */
+
+ /* don't do anything if there is no change. no rollback info either. */
+ if (value == old_value)
+ return NULL;
+
+ mode &= ~DAV_FS_MODE_XUSR;
+ if (value)
+ mode |= DAV_FS_MODE_XUSR;
+
+ if (chmod(resource->info->pathname, mode) == -1) {
+ return dav_new_error(resource->info->pool,
+ HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not set the executable flag of the "
+ "target resource.");
+ }
+
+ /* update the resource and set up the rollback context */
+ resource->info->finfo.st_mode = mode;
+ *rollback_ctx = (dav_liveprop_rollback *)old_value;
+
+ return NULL;
+}
+
+static void dav_fs_patch_commit(dav_resource *resource,
+ int operation,
+ void *context,
+ dav_liveprop_rollback *rollback_ctx)
+{
+ /* nothing to do */
+}
+
+static dav_error *dav_fs_patch_rollback(dav_resource *resource,
+ int operation,
+ void *context,
+ dav_liveprop_rollback *rollback_ctx)
+{
+ mode_t mode = resource->info->finfo.st_mode & ~DAV_FS_MODE_XUSR;
+ int value = rollback_ctx != NULL;
+
+ /* assert: prop == executable. operation == SET. */
+
+ /* restore the executable bit */
+ if (value)
+ mode |= DAV_FS_MODE_XUSR;
+
+ if (chmod(resource->info->pathname, mode) == -1) {
+ return dav_new_error(resource->info->pool,
+ HTTP_INTERNAL_SERVER_ERROR, 0,
+ "After a failure occurred, the resource's "
+ "executable flag could not be restored.");
+ }
+
+ /* restore the resource's state */
+ resource->info->finfo.st_mode = mode;
+
+ return NULL;
+}
+
+
+static const dav_hooks_liveprop dav_hooks_liveprop_fs =
+{
+#ifdef WIN32
+ NULL,
+#else
+ "http://apache.org/dav/propset/fs/1", /* filesystem, set 1 */
+#endif
+ dav_fs_find_prop,
+ dav_fs_insert_prop,
+ dav_fs_insert_all,
+ dav_fs_is_writeable,
+ dav_fs_namespace_uris,
+ dav_fs_patch_validate,
+ dav_fs_patch_exec,
+ dav_fs_patch_commit,
+ dav_fs_patch_rollback,
+};
+
+/*
+** Note: we do not provide an is_active function at this point. In the
+** future, mod_dav may use that to determine if a particular provider is
+** active/enabled, but it doesn't now.
+*/
+static const dav_dyn_provider dav_dyn_providers_fs[] =
+{
+ /* repository provider */
+ {
+ DAV_FS_PROVIDER_ID,
+ DAV_DYN_TYPE_REPOSITORY,
+ &dav_hooks_repository_fs,
+ NULL
+ },
+ /* liveprop provider */
+ {
+ DAV_FS_PROVIDER_ID,
+ DAV_DYN_TYPE_LIVEPROP,
+ &dav_hooks_liveprop_fs,
+ NULL
+ },
+ /* propdb provider */
+ {
+ DAV_FS_PROVIDER_ID,
+ DAV_DYN_TYPE_PROPDB,
+ &dav_hooks_db_dbm,
+ NULL
+ },
+ /* locks provider */
+ {
+ DAV_FS_PROVIDER_ID,
+ DAV_DYN_TYPE_LOCKS,
+ &dav_hooks_locks_fs,
+ NULL
+ },
+ /* must always be last */
+ DAV_DYN_END_MARKER
+};
+
+const dav_dyn_module dav_dyn_module_default =
+{
+ DAV_DYN_MAGIC,
+ DAV_DYN_VERSION,
+ "filesystem",
+
+ NULL, /* module_open */
+ NULL, /* module_close */
+ NULL, /* dir_open */
+ NULL, /* dir_param */
+ NULL, /* dir_merge */
+ NULL, /* dir_close */
+
+ dav_dyn_providers_fs
+};
diff --git a/modules/dav/fs/repos.h b/modules/dav/fs/repos.h
new file mode 100644
index 0000000000..2330c1ee02
--- /dev/null
+++ b/modules/dav/fs/repos.h
@@ -0,0 +1,75 @@
+/*
+** Copyright (C) 1998-2000 Greg Stein. All Rights Reserved.
+**
+** By using this file, you agree to the terms and conditions set forth in
+** the LICENSE.html file which can be found at the top level of the mod_dav
+** distribution or at http://www.webdav.org/mod_dav/license-1.html.
+**
+** Contact information:
+** Greg Stein, PO Box 760, Palo Alto, CA, 94302
+** gstein@lyra.org, http://www.webdav.org/mod_dav/
+*/
+
+/*
+** Declarations for the filesystem repository implementation
+**
+** Written by John Vasta, vasta@rational.com, by separating from mod_dav.h
+*/
+
+#ifndef _DAV_FS_REPOS_H_
+#define _DAV_FS_REPOS_H_
+
+/* the subdirectory to hold all DAV-related information for a directory */
+#define DAV_FS_STATE_DIR ".DAV"
+#define DAV_FS_STATE_FILE_FOR_DIR ".state_for_dir"
+#define DAV_FS_LOCK_NULL_FILE ".locknull"
+
+#ifndef WIN32
+
+#define DAV_FS_MODE_DIR (S_IRWXU | S_IRWXG)
+#define DAV_FS_MODE_FILE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
+#define DAV_FS_MODE_XUSR (S_IXUSR)
+
+#else /* WIN32 */
+
+#define DAV_FS_MODE_DIR (_S_IREAD | _S_IWRITE)
+#define DAV_FS_MODE_FILE (_S_IREAD | _S_IWRITE)
+#define DAV_FS_MODE_XUSR (_S_IEXEC)
+
+#include <limits.h>
+
+typedef int ssize_t;
+
+#define mkdir(p,m) _mkdir(p)
+
+#endif /* WIN32 */
+
+/* ensure that our state subdirectory is present */
+void dav_fs_ensure_state_dir(pool *p, const char *dirname);
+
+/* return the storage pool associated with a resource */
+pool *dav_fs_pool(const dav_resource *resource);
+
+/* return the full pathname for a resource */
+const char *dav_fs_pathname(const dav_resource *resource);
+
+/* return the directory and filename for a resource */
+void dav_fs_dir_file_name(const dav_resource *resource,
+ const char **dirpath,
+ const char **fname);
+
+/* return the list of locknull members in this resource's directory */
+dav_error * dav_fs_get_locknull_members(const dav_resource *resource,
+ dav_buffer *pbuf);
+
+
+/* DBM functions used by the repository and locking providers */
+extern const dav_hooks_db dav_hooks_db_dbm;
+
+dav_error * dav_dbm_open_direct(pool *p, const char *pathname, int ro,
+ dav_db **pdb);
+void dav_dbm_get_statefiles(pool *p, const char *fname,
+ const char **state1, const char **state2);
+
+
+#endif /* _DAV_FS_REPOS_H_ */
diff --git a/modules/dav/main/.cvsignore b/modules/dav/main/.cvsignore
new file mode 100644
index 0000000000..f2f7a70d2c
--- /dev/null
+++ b/modules/dav/main/.cvsignore
@@ -0,0 +1,10 @@
+.deps
+.libs
+*.la
+modules.mk
+Makefile
+*.lo
+*.slo
+*.so
+*.dll
+*.def
diff --git a/modules/dav/main/Makefile.in b/modules/dav/main/Makefile.in
new file mode 100644
index 0000000000..3fc5925b91
--- /dev/null
+++ b/modules/dav/main/Makefile.in
@@ -0,0 +1,6 @@
+
+LTLIBRARY_NAME = libapachemod_dav.la
+LTLIBRARY_SOURCES = mod_dav.c props.c util.c util_lock.c \
+ opaquelock.c dav_dyn.c
+
+include $(top_srcdir)/build/ltlib.mk
diff --git a/modules/dav/main/config5.m4 b/modules/dav/main/config5.m4
new file mode 100644
index 0000000000..bf435023ff
--- /dev/null
+++ b/modules/dav/main/config5.m4
@@ -0,0 +1,22 @@
+dnl modules enabled in this directory by default
+
+APACHE_MODPATH_INIT(dav/main)
+
+dav_objects="mod_dav.lo props.lo util.lo util_lock.lo liveprop.lo providers.lo std_liveprop.lo"
+
+if test "$enable_http" = "no"; then
+ dav_enable=no
+else
+ dav_enable=most
+fi
+
+APACHE_MODULE(dav, WebDAV protocol handling, $dav_objects, , $dav_enable)
+
+if test "$enable_dav" != "no"; then
+ apache_need_expat=yes
+
+ INCLUDES="$INCLUDES -I\$(top_srcdir)/$modpath_current"
+fi
+
+
+APACHE_MODPATH_FINISH
diff --git a/modules/dav/main/liveprop.c b/modules/dav/main/liveprop.c
new file mode 100644
index 0000000000..cd720feb22
--- /dev/null
+++ b/modules/dav/main/liveprop.c
@@ -0,0 +1,122 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+#include "apr_pools.h"
+#include "apr_hash.h"
+#include "apr_errno.h"
+
+#include "ap_hooks.h" /* ### for ap_global_hook_pool */
+#include "util_xml.h" /* for ap_text_header */
+
+#include "mod_dav.h"
+
+
+static ap_hash_t *dav_liveprop_uris = NULL;
+static int dav_liveprop_count = 0;
+
+
+static ap_status_t dav_cleanup_liveprops(void *ctx)
+{
+ dav_liveprop_uris = NULL;
+ dav_liveprop_count = 0;
+ return APR_SUCCESS;
+}
+
+void dav_register_liveprop_namespace(ap_pool_t *p, const char *uri)
+{
+ int value;
+
+ /* ### ignore the pool; it is NULL right now */
+ p = ap_global_hook_pool;
+
+ if (dav_liveprop_uris == NULL) {
+ dav_liveprop_uris = ap_make_hash(p);
+ ap_register_cleanup(p, NULL, dav_cleanup_liveprops, ap_null_cleanup);
+ }
+
+ value = (int)ap_hash_get(dav_liveprop_uris, uri, 0);
+ if (value != 0) {
+ /* already registered */
+ return;
+ }
+
+ /* start at 1, and count up */
+ ap_hash_set(dav_liveprop_uris, uri, 0, (void *)++dav_liveprop_count);
+}
+
+int dav_get_liveprop_ns_index(const char *uri)
+{
+ return (int)ap_hash_get(dav_liveprop_uris, uri, 0);
+}
+
+int dav_get_liveprop_ns_count(void)
+{
+ return dav_liveprop_count;
+}
+
+void dav_add_all_liveprop_xmlns(ap_pool_t *p, ap_text_header *phdr)
+{
+ ap_hash_index_t *idx = ap_hash_first(dav_liveprop_uris);
+
+ for ( ; idx != NULL; idx = ap_hash_next(idx) ) {
+ const void *key;
+ void *val;
+ const char *s;
+
+ ap_hash_this(idx, &key, NULL, &val);
+
+ s = ap_psprintf(p, " xmlns:lp%d=\"%s\"", (int)val, key);
+ ap_text_append(p, phdr, s);
+ }
+}
diff --git a/modules/dav/main/mod_dav.c b/modules/dav/main/mod_dav.c
new file mode 100644
index 0000000000..749994a5e8
--- /dev/null
+++ b/modules/dav/main/mod_dav.c
@@ -0,0 +1,3313 @@
+/*
+** Copyright (C) 1998-2000 Greg Stein. All Rights Reserved.
+**
+** By using this file, you agree to the terms and conditions set forth in
+** the LICENSE.html file which can be found at the top level of the mod_dav
+** distribution or at http://www.webdav.org/mod_dav/license-1.html.
+**
+** Contact information:
+** Greg Stein, PO Box 760, Palo Alto, CA, 94302
+** gstein@lyra.org, http://www.webdav.org/mod_dav/
+*/
+
+/*
+** DAV extension module for Apache 1.3.*
+** This module is repository-independent. It depends on hooks provided by a
+** repository implementation.
+**
+** Written by Greg Stein, gstein@lyra.org, http://www.lyra.org/
+**
+** APACHE ISSUES:
+** - within a DAV hierarchy, if an unknown method is used and we default
+** to Apache's implementation, it sends back an OPTIONS with the wrong
+** set of methods -- there is NO HOOK for us.
+** therefore: we need to manually handle the HTTP_METHOD_NOT_ALLOWED
+** and HTTP_NOT_IMPLEMENTED responses (not ap_send_error_response).
+** - process_mkcol_body() had to dup code from ap_setup_client_block().
+** - it would be nice to get status lines from Apache for arbitrary
+** status codes
+** - it would be nice to be able to extend Apache's set of response
+** codes so that it doesn't return 500 when an unknown code is placed
+** into r->status.
+** - http_vhost functions should apply "const" to their params
+**
+** DESIGN NOTES:
+** - For PROPFIND, we batch up the entire response in memory before
+** sending it. We may want to reorganize around sending the information
+** as we suck it in from the propdb. Alternatively, we should at least
+** generate a total Content-Length if we're going to buffer in memory
+** so that we can keep the connection open.
+*/
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "util_script.h"
+
+#include "mod_dav.h"
+
+#include "dav_opaquelock.h"
+
+
+enum {
+ DAV_ENABLED_UNSET = 0,
+ DAV_ENABLED_OFF,
+ DAV_ENABLED_ON
+};
+
+/* per-dir configuration */
+typedef struct {
+ int enabled;
+ const char *dir;
+ int locktimeout;
+ int handle_get; /* cached from repository hook structure */
+ int allow_depthinfinity;
+ long limit_xml_body;
+
+ table *d_params; /* per-directory DAV config parameters */
+ struct dav_dyn_mod_ctx *dmc;
+
+ dav_dyn_hooks propdb;
+ dav_dyn_hooks locks;
+ dav_dyn_hooks *liveprop;
+ dav_dyn_hooks repository;
+ dav_dyn_hooks vsn;
+} dav_dir_conf;
+
+/* per-server configuration */
+typedef struct {
+ const char *lockdb_path; /* lock database path */
+
+ uuid_state st; /* UUID state for opaquelocktoken */
+
+} dav_server_conf;
+
+#define DAV_INHERIT_VALUE(parent, child, field) \
+ ((child)->field ? (child)->field : (parent)->field)
+
+/* LimitXMLRequestBody handling */
+#define DAV_LIMIT_UNSET ((long) -1)
+#define DAV_DEFAULT_LIMIT_XML_BODY ((size_t)1000000)
+
+
+/* forward-declare for use in configuration lookup */
+extern module MODULE_VAR_EXPORT dav_module;
+
+/* copy a module's providers into our per-directory configuration state */
+static void dav_copy_providers(pool *p, const char *name, dav_dir_conf *conf)
+{
+ const dav_dyn_module *mod;
+ const dav_dyn_provider *provider;
+ dav_dyn_hooks hooks;
+ void *ctx;
+
+ mod = dav_find_module(name);
+ /* ### if NULL? need to error out somehow... */
+
+ /* Set hooks for any providers in the module */
+ ctx = dav_prepare_scan(p, mod);
+ if (ctx == NULL) {
+ /* ### how to signal an error? */
+ return;
+ }
+
+ while (!dav_scan_providers(ctx, &provider, &hooks)) {
+
+ switch (provider->type) {
+
+ case DAV_DYN_TYPE_PROPDB:
+ conf->propdb = hooks;
+ break;
+
+ case DAV_DYN_TYPE_LOCKS:
+ conf->locks = hooks;
+ break;
+
+ case DAV_DYN_TYPE_QUERY_GRAMMAR:
+ /* ### not yet defined */
+ break;
+
+ case DAV_DYN_TYPE_ACL:
+ /* ### not yet defined */
+ break;
+
+ case DAV_DYN_TYPE_VSN:
+ conf->vsn = hooks;
+ break;
+
+ case DAV_DYN_TYPE_REPOSITORY:
+ conf->repository = hooks;
+ conf->handle_get = DAV_AS_HOOKS_REPOSITORY(&hooks)->handle_get;
+ break;
+
+ case DAV_DYN_TYPE_LIVEPROP:
+ {
+ dav_dyn_hooks *ddh = ap_palloc(p, sizeof(*ddh));
+
+ *ddh = hooks;
+ ddh->next = conf->liveprop;
+ conf->liveprop = ddh;
+ break;
+ }
+
+ default:
+ /* ### need to error out somehow... */
+ break;
+ }
+ }
+}
+
+static void dav_init_handler(server_rec *s, pool *p)
+{
+ /* DBG0("dav_init_handler"); */
+
+ ap_add_version_component("DAV/" DAV_VERSION);
+
+ dav_process_builtin_modules(p);
+}
+
+static void *dav_create_server_config(pool *p, server_rec *s)
+{
+ dav_server_conf *newconf;
+
+ newconf = (dav_server_conf *) ap_pcalloc(p, sizeof(*newconf));
+
+ newconf->lockdb_path = NULL;
+ dav_create_uuid_state(&newconf->st);
+
+ return newconf;
+}
+
+static void *dav_merge_server_config(pool *p, void *base, void *overrides)
+{
+ dav_server_conf *parent = base;
+ dav_server_conf *child = overrides;
+ dav_server_conf *newconf;
+
+ newconf = (dav_server_conf *) ap_pcalloc(p, sizeof(*newconf));
+
+ newconf->lockdb_path = DAV_INHERIT_VALUE(parent, child, lockdb_path);
+
+ memcpy(&newconf->st, &child->st, sizeof(newconf->st));
+
+ return newconf;
+}
+
+static void *dav_create_dir_config(pool *p, char *dir)
+{
+ /* NOTE: dir==NULL creates the default per-dir config */
+
+ dav_dir_conf *conf;
+
+ conf = (dav_dir_conf *) ap_pcalloc(p, sizeof(*conf));
+ conf->dir = ap_pstrdup(p, dir);
+ conf->d_params = ap_make_table(p, 1);
+ conf->limit_xml_body = DAV_LIMIT_UNSET;
+
+ /* DBG1("dav_create_dir_config: %08lx", (long)conf); */
+
+ /*
+ ** Locate the appropriate module (NULL == default) and copy the module's
+ ** providers' hooks into our configuration state.
+ */
+ dav_copy_providers(p, NULL, conf);
+
+ return conf;
+}
+
+static void *dav_merge_dir_config(pool *p, void *base, void *overrides)
+{
+ dav_dir_conf *parent = base;
+ dav_dir_conf *child = overrides;
+ dav_dir_conf *newconf = (dav_dir_conf *) ap_pcalloc(p, sizeof(*newconf));
+
+ /* DBG3("dav_merge_dir_config: new=%08lx base=%08lx overrides=%08lx",
+ (long)newconf, (long)base, (long)overrides); */
+
+ newconf->enabled = DAV_INHERIT_VALUE(parent, child, enabled);
+ newconf->locktimeout = DAV_INHERIT_VALUE(parent, child, locktimeout);
+ newconf->dir = DAV_INHERIT_VALUE(parent, child, dir);
+ newconf->allow_depthinfinity = DAV_INHERIT_VALUE(parent, child,
+ allow_depthinfinity);
+
+ if (child->limit_xml_body != DAV_LIMIT_UNSET)
+ newconf->limit_xml_body = child->limit_xml_body;
+ else
+ newconf->limit_xml_body = parent->limit_xml_body;
+
+ newconf->d_params = ap_copy_table(p, parent->d_params);
+ ap_overlap_tables(newconf->d_params, child->d_params,
+ AP_OVERLAP_TABLES_SET);
+
+ if (child->propdb.hooks != NULL)
+ newconf->propdb = child->propdb;
+ else
+ newconf->propdb = parent->propdb;
+
+ if (child->locks.hooks != NULL)
+ newconf->locks = child->locks;
+ else
+ newconf->locks = parent->locks;
+
+ if (child->vsn.hooks != NULL)
+ newconf->vsn = child->vsn;
+ else
+ newconf->vsn = parent->vsn;
+
+ if (child->repository.hooks != NULL)
+ newconf->repository = child->repository;
+ else
+ newconf->repository = parent->repository;
+ newconf->handle_get =
+ newconf->repository.hooks != NULL
+ && DAV_AS_HOOKS_REPOSITORY(&newconf->repository)->handle_get;
+
+ if (child->liveprop != NULL)
+ newconf->liveprop = child->liveprop;
+ else
+ newconf->liveprop = parent->liveprop;
+
+ return newconf;
+}
+
+uuid_state *dav_get_uuid_state(const request_rec *r)
+{
+ dav_server_conf *conf;
+
+ conf = ap_get_module_config(r->server->module_config, &dav_module);
+
+ return &conf->st;
+}
+
+const char *dav_get_lockdb_path(const request_rec *r)
+{
+ dav_server_conf *conf;
+
+ conf = ap_get_module_config(r->server->module_config, &dav_module);
+ return conf->lockdb_path;
+}
+
+table *dav_get_dir_params(const request_rec *r)
+{
+ dav_dir_conf *conf;
+
+ conf = ap_get_module_config(r->per_dir_config, &dav_module);
+ return conf->d_params;
+}
+
+size_t dav_get_limit_xml_body(const request_rec *r)
+{
+ dav_dir_conf *conf;
+
+ conf = ap_get_module_config(r->per_dir_config, &dav_module);
+ if (conf->limit_xml_body == DAV_LIMIT_UNSET)
+ return DAV_DEFAULT_LIMIT_XML_BODY;
+ return (size_t)conf->limit_xml_body;
+}
+
+const dav_dyn_hooks *dav_get_provider_hooks(request_rec *r, int provider_type)
+{
+ dav_dir_conf *conf;
+ const dav_dyn_hooks *hooks;
+ static const dav_dyn_hooks null_hooks = { { 0 } };
+
+ /* Call repository hook to resolve resource */
+ conf = (dav_dir_conf *) ap_get_module_config(r->per_dir_config,
+ &dav_module);
+ switch (provider_type) {
+
+ case DAV_DYN_TYPE_PROPDB:
+ hooks = &conf->propdb;
+ break;
+
+ case DAV_DYN_TYPE_LOCKS:
+ hooks = &conf->locks;
+ break;
+
+ case DAV_DYN_TYPE_QUERY_GRAMMAR:
+ /* ### not yet defined */
+ hooks = &null_hooks;
+ break;
+
+ case DAV_DYN_TYPE_ACL:
+ /* ### not yet defined */
+ hooks = &null_hooks;
+ break;
+
+ case DAV_DYN_TYPE_VSN:
+ hooks = &conf->vsn;
+ break;
+
+ case DAV_DYN_TYPE_REPOSITORY:
+ hooks = &conf->repository;
+ break;
+
+ case DAV_DYN_TYPE_LIVEPROP:
+ hooks = conf->liveprop;
+ break;
+
+ default:
+ /* unknown provider type */
+ hooks = &null_hooks;
+ break;
+ }
+
+ return hooks;
+}
+
+/*
+ * Command handler for the DAV directive, which is FLAG.
+ */
+static const char *dav_cmd_dav(cmd_parms *cmd, void *config, int arg)
+{
+ dav_dir_conf *conf = (dav_dir_conf *) config;
+
+ if (arg)
+ conf->enabled = DAV_ENABLED_ON;
+ else
+ conf->enabled = DAV_ENABLED_OFF;
+ return NULL;
+}
+
+/*
+ * Command handler for the DAVDepthInfinity directive, which is FLAG.
+ */
+static const char *dav_cmd_davdepthinfinity(cmd_parms *cmd, void *config,
+ int arg)
+{
+ dav_dir_conf *conf = (dav_dir_conf *) config;
+
+ if (arg)
+ conf->allow_depthinfinity = DAV_ENABLED_ON;
+ else
+ conf->allow_depthinfinity = DAV_ENABLED_OFF;
+ return NULL;
+}
+
+/*
+ * Command handler for the DAVLockDB directive, which is TAKE1
+ */
+static const char *dav_cmd_davlockdb(cmd_parms *cmd, void *config, char *arg1)
+{
+ dav_server_conf *conf;
+
+ conf = (dav_server_conf *) ap_get_module_config(cmd->server->module_config,
+ &dav_module);
+ arg1 = ap_os_canonical_filename(cmd->pool, arg1);
+ conf->lockdb_path = ap_server_root_relative(cmd->pool, arg1);
+
+ return NULL;
+}
+
+/*
+ * Command handler for DAVMinTimeout directive, which is TAKE1
+ */
+static const char *dav_cmd_davmintimeout(cmd_parms *cmd, void *config,
+ char *arg1)
+{
+ dav_dir_conf *conf = (dav_dir_conf *) config;
+
+ conf->locktimeout = atoi(arg1);
+ if (conf->locktimeout < 0)
+ return "DAVMinTimeout requires a non-negative integer.";
+
+ return NULL;
+}
+
+/*
+ * Command handler for DAVParam directive, which is TAKE2
+ */
+static const char *dav_cmd_davparam(cmd_parms *cmd, void *config,
+ char *arg1, char *arg2)
+{
+ dav_dir_conf *conf = (dav_dir_conf *) config;
+
+ ap_table_set(conf->d_params, arg1, arg2);
+
+ return NULL;
+}
+
+/*
+ * Command handler for LimitXMLRequestBody directive, which is TAKE1
+ */
+static const char *dav_cmd_limitxmlrequestbody(cmd_parms *cmd, void *config,
+ char *arg1)
+{
+ dav_dir_conf *conf = (dav_dir_conf *) config;
+
+ conf->limit_xml_body = atol(arg1);
+ if (conf->limit_xml_body < 0)
+ return "LimitXMLRequestBody requires a non-negative integer.";
+
+ return NULL;
+}
+
+/*
+** dav_error_response()
+**
+** Send a nice response back to the user. In most cases, Apache doesn't
+** allow us to provide details in the body about what happened. This
+** function allows us to completely specify the response body.
+*/
+static int dav_error_response(request_rec *r, int status, const char *body)
+{
+ r->status = status;
+ r->content_type = "text/html";
+
+ /* since we're returning DONE, ensure the request body is consumed. */
+ (void) ap_discard_request_body(r);
+
+ /* begin the response now... */
+ ap_send_http_header(r);
+
+ /* ### hard or soft? */
+ ap_soft_timeout("send error body", r);
+
+ ap_rvputs(r,
+ DAV_RESPONSE_BODY_1,
+ r->status_line,
+ DAV_RESPONSE_BODY_2,
+ &r->status_line[4],
+ DAV_RESPONSE_BODY_3,
+ NULL);
+
+ ap_rputs(body, r);
+
+ ap_rputs(ap_psignature("\n<P><HR>\n", r), r);
+ ap_rputs(DAV_RESPONSE_BODY_4, r);
+
+ ap_kill_timeout(r);
+
+ /* the response has been sent. */
+ /*
+ * ### Use of DONE obviates logging..!
+ */
+ return DONE;
+}
+
+/*
+** Apache's URI escaping does not replace '&' since that is a valid character
+** in a URI (to form a query section). We must explicitly handle it so that
+** we can embed the URI into an XML document.
+*/
+static const char *dav_xml_escape_uri(pool *p, const char *uri)
+{
+ const char *e_uri = ap_escape_uri(p, uri);
+
+ /* check the easy case... */
+ if (strchr(e_uri, '&') == NULL)
+ return e_uri;
+
+ /* more work needed... sigh. */
+
+ /*
+ ** Note: this is a teeny bit of overkill since we know there are no
+ ** '<' or '>' characters, but who cares.
+ */
+ return dav_quote_string(p, e_uri, 0);
+}
+
+static void dav_send_multistatus(request_rec *r, int status,
+ dav_response *first,
+ array_header *namespaces)
+{
+ /* Set the correct status and Content-Type */
+ r->status = status;
+ r->content_type = DAV_XML_CONTENT_TYPE;
+
+ /* Send all of the headers now */
+ ap_send_http_header(r);
+
+ /* Start a timeout for delivering the response. */
+ ap_soft_timeout("sending multistatus response", r);
+
+ /* Send the actual multistatus response now... */
+ ap_rputs(DAV_XML_HEADER DEBUG_CR
+ "<D:multistatus xmlns:D=\"DAV:\"", r);
+
+ if (namespaces != NULL) {
+ int i;
+
+ for (i = namespaces->nelts; i--; ) {
+ ap_rprintf(r, " xmlns:ns%d=\"%s\"", i,
+ DAV_GET_URI_ITEM(namespaces, i));
+ }
+ }
+
+ /* ap_rputc('>', r); */
+ ap_rputs(">" DEBUG_CR, r);
+
+ for (; first != NULL; first = first->next) {
+ dav_text *t;
+
+ if (first->propresult.xmlns == NULL) {
+ ap_rputs("<D:response>", r);
+ }
+ else {
+ ap_rputs("<D:response", r);
+ for (t = first->propresult.xmlns; t; t = t->next) {
+ ap_rputs(t->text, r);
+ }
+ ap_rputc('>', r);
+ }
+
+ ap_rputs(DEBUG_CR "<D:href>", r);
+ ap_rputs(dav_xml_escape_uri(r->pool, first->href), r);
+ ap_rputs("</D:href>" DEBUG_CR, r);
+
+ if (first->propresult.propstats == NULL) {
+ /* ### it would be nice to get a status line from Apache */
+ ap_rprintf(r,
+ "<D:status>HTTP/1.1 %d status text goes here</D:status>"
+ DEBUG_CR, first->status);
+ }
+ else {
+ /* assume this includes <propstat> and is quoted properly */
+ for (t = first->propresult.propstats; t; t = t->next) {
+ ap_rputs(t->text, r);
+ }
+ }
+
+ if (first->desc != NULL) {
+ /*
+ ** We supply the description, so we know it doesn't have to
+ ** have any escaping/encoding applied to it.
+ */
+ ap_rputs("<D:responsedescription>", r);
+ ap_rputs(first->desc, r);
+ ap_rputs("</D:responsedescription>" DEBUG_CR, r);
+ }
+
+ ap_rputs("</D:response>" DEBUG_CR, r);
+ }
+
+ ap_rputs("</D:multistatus>" DEBUG_CR, r);
+
+ /* Done with sending and the timeout. */
+ ap_kill_timeout(r);
+}
+
+/*
+** dav_log_err()
+**
+** Write error information to the log.
+*/
+static void dav_log_err(request_rec *r, dav_error *err, int level)
+{
+ dav_error *errscan;
+
+ /* Log the errors */
+ /* ### should have a directive to log the first or all */
+ for (errscan = err; errscan != NULL; errscan = errscan->prev) {
+ if (errscan->desc == NULL)
+ continue;
+ if (errscan->save_errno != 0) {
+ errno = errscan->save_errno;
+ ap_log_rerror(APLOG_MARK, level, r, "%s [%d, #%d]",
+ errscan->desc, errscan->status, errscan->error_id);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, level | APLOG_NOERRNO, r,
+ "%s [%d, #%d]",
+ errscan->desc, errscan->status, errscan->error_id);
+ }
+ }
+}
+
+/*
+** dav_handle_err()
+**
+** Handle the standard error processing. <err> must be non-NULL.
+**
+** <response> is set by the following:
+** - dav_validate_request()
+** - dav_add_lock()
+** - repos_hooks->remove_resource
+** - repos_hooks->move_resource
+** - repos_hooks->copy_resource
+*/
+static int dav_handle_err(request_rec *r, dav_error *err,
+ dav_response *response)
+{
+ /* log the errors */
+ dav_log_err(r, err, APLOG_ERR);
+
+ if (response == NULL) {
+ /* our error messages are safe; tell Apache this */
+ ap_table_setn(r->notes, "verbose-error-to", "*");
+ return err->status;
+ }
+
+ /* since we're returning DONE, ensure the request body is consumed. */
+ (void) ap_discard_request_body(r);
+
+ /* send the multistatus and tell Apache the request/response is DONE. */
+ dav_send_multistatus(r, err->status, response, NULL);
+ return DONE;
+}
+
+/* handy function for return values of methods that (may) create things */
+static int dav_created(request_rec *r, request_rec *rnew,
+ dav_resource *res, const char *what,
+ int replaced)
+{
+ const char *body;
+
+ if (rnew == NULL) {
+ rnew = r;
+ }
+
+ /* did the target resource already exist? */
+ if (replaced) {
+ /* Apache will supply a default message */
+ return HTTP_NO_CONTENT;
+ }
+
+ /* Per HTTP/1.1, S10.2.2: add a Location header to contain the
+ * URI that was created. */
+
+ /* ### rnew->uri does not contain an absoluteURI. S14.30 states that
+ * ### the Location header requires an absoluteURI. where to get it? */
+ /* ### disable until we get the right value */
+#if 0
+ ap_table_setn(r->headers_out, "Location", rnew->uri);
+#endif
+
+ /* ### insert an ETag header? see HTTP/1.1 S10.2.2 */
+
+ /* Apache doesn't allow us to set a variable body for HTTP_CREATED, so
+ * we must manufacture the entire response. */
+ body = ap_psprintf(r->pool, "%s %s has been created.",
+ what,
+ ap_escape_html(rnew->pool, rnew->uri));
+ return dav_error_response(r, HTTP_CREATED, body);
+}
+
+/* ### move to dav_util? */
+int dav_get_depth(request_rec *r, int def_depth)
+{
+ const char *depth = ap_table_get(r->headers_in, "Depth");
+
+ if (depth == NULL) {
+ return def_depth;
+ }
+ if (strcasecmp(depth, "infinity") == 0) {
+ return DAV_INFINITY;
+ }
+ else if (strcmp(depth, "0") == 0) {
+ return 0;
+ }
+ else if (strcmp(depth, "1") == 0) {
+ return 1;
+ }
+
+ /* The caller will return an HTTP_BAD_REQUEST. This will augment the
+ * default message that Apache provides. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "An invalid Depth header was specified.");
+ return -1;
+}
+
+static int dav_get_overwrite(request_rec *r)
+{
+ const char *overwrite = ap_table_get(r->headers_in, "Overwrite");
+
+ if (overwrite == NULL) {
+ return 1; /* default is "T" */
+ }
+
+ if ((*overwrite == 'F' || *overwrite == 'f') && overwrite[1] == '\0') {
+ return 0;
+ }
+ if ((*overwrite == 'T' || *overwrite == 't') && overwrite[1] == '\0') {
+ return 1;
+ }
+
+ /* The caller will return an HTTP_BAD_REQUEST. This will augment the
+ * default message that Apache provides. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "An invalid Overwrite header was specified.");
+ return -1;
+}
+
+/* resolve a request URI to a resource descriptor */
+static int dav_get_resource(request_rec *r, dav_resource **res_p)
+{
+ dav_dir_conf *conf;
+ const dav_hooks_repository *repos_hooks;
+
+ /* Call repository hook to resolve resource */
+ conf = (dav_dir_conf *) ap_get_module_config(r->per_dir_config,
+ &dav_module);
+
+ repos_hooks = DAV_AS_HOOKS_REPOSITORY(&conf->repository);
+ if (repos_hooks == NULL || repos_hooks->get_resource == NULL) {
+ /* ### this should happen at startup rather than per-request */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "No %s has been configured.",
+ repos_hooks == NULL
+ ? "repository module"
+ : "GET handler");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ *res_p = (*repos_hooks->get_resource)(r, conf->dir,
+ dav_get_target_selector(r));
+ if (*res_p == NULL) {
+ /* Apache will supply a default error for this. */
+ return HTTP_NOT_FOUND;
+ }
+
+ return OK;
+}
+
+static dav_error * dav_open_lockdb(request_rec *r, int ro, dav_lockdb **lockdb)
+{
+ const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
+
+ if (hooks == NULL) {
+ *lockdb = NULL;
+ return NULL;
+ }
+
+ /* open the thing lazily */
+ return (*hooks->open_lockdb)(r, ro, 0, lockdb);
+}
+
+static int dav_parse_range(request_rec *r,
+ off_t *range_start, off_t *range_end)
+{
+ const char *range;
+ char *dash;
+ char *slash;
+
+ range = ap_table_get(r->headers_in, "content-range");
+ if (range == NULL)
+ return 0;
+
+ range = ap_pstrdup(r->pool, range);
+ if (strncasecmp(range, "bytes ", 6) != 0
+ || (dash = strchr(range, '-')) == NULL
+ || (slash = strchr(range, '/')) == NULL) {
+ /* malformed header. ignore it (per S14.16 of RFC2616) */
+ return 0;
+ }
+
+ *dash = *slash = '\0';
+ *range_start = atol(range + 6);
+ *range_end = atol(dash + 1);
+ if (*range_end < *range_start
+ || (slash[1] != '*' && atol(slash + 1) <= *range_end)) {
+ /* invalid range. ignore it (per S14.16 of RFC2616) */
+ return 0;
+ }
+
+ /* we now have a valid range */
+ return 1;
+}
+
+/* handle the GET method */
+static int dav_method_get(request_rec *r)
+{
+ dav_resource *resource;
+ int result;
+
+ /* This method should only be called when the resource is not
+ * visible to Apache. We will fetch the resource from the repository,
+ * then create a subrequest for Apache to handle.
+ */
+ result = dav_get_resource(r, &resource);
+ if (result != OK)
+ return result;
+ if (!resource->exists) {
+ /* Apache will supply a default error for this. */
+ return HTTP_NOT_FOUND;
+ }
+
+ /* Check resource type */
+ if (resource->type != DAV_RESOURCE_TYPE_REGULAR &&
+ resource->type != DAV_RESOURCE_TYPE_REVISION) {
+ return dav_error_response(r, HTTP_CONFLICT,
+ "Cannot GET this type of resource.");
+ }
+
+ /* Cannot handle GET of a collection from a repository */
+ if (resource->collection) {
+ return dav_error_response(r, HTTP_CONFLICT,
+ "No default response to GET for a "
+ "collection.");
+ }
+
+ /*
+ ** We can use two different approaches for a GET.
+ **
+ ** 1) get_pathname will return a pathname to a file which should be
+ ** sent to the client. If the repository provides this, then we
+ ** use it.
+ **
+ ** This is the best alternative since it allows us to do a sub-
+ ** request on the file, which gives the Apache framework a chance
+ ** to deal with negotiation, MIME types, or whatever.
+ **
+ ** 2) open_stream and read_stream.
+ */
+ if (resource->hooks->get_pathname != NULL) {
+ const char *pathname;
+ void *fhandle;
+ request_rec *new_req;
+
+ /* Ask repository for copy of file */
+ pathname = (*resource->hooks->get_pathname)(resource, &fhandle);
+ if (pathname == NULL) {
+ return HTTP_NOT_FOUND;
+ }
+
+ /* Convert to canonical filename, so Apache detects component
+ * separators (on Windows, it only looks for '/', not '\')
+ */
+ pathname = ap_os_case_canonical_filename(r->pool, pathname);
+
+ /* Create a sub-request with the new filename */
+ new_req = ap_sub_req_lookup_file(pathname, r);
+ if (new_req == NULL) {
+ (*resource->hooks->free_file)(fhandle);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* This may be a HEAD request */
+ new_req->header_only = r->header_only;
+
+ /* ### this enables header generation */
+ new_req->assbackwards = 0;
+
+ /* Run the sub-request */
+ result = ap_run_sub_req(new_req);
+ ap_destroy_sub_req(new_req);
+
+ /* Free resources */
+ (*resource->hooks->free_file)(fhandle);
+
+ return result;
+ }
+ else {
+ dav_stream_mode mode;
+ dav_stream *stream;
+ dav_error *err;
+ void *buffer;
+ int has_range;
+ off_t range_start;
+ off_t range_end;
+
+ /* set up the HTTP headers for the response */
+ if ((err = (*resource->hooks->set_headers)(r, resource)) != NULL) {
+ err = dav_push_error(r->pool, err->status, 0,
+ "Unable to set up HTTP headers.",
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* use plain READ mode unless we see a Content-Range */
+ mode = DAV_MODE_READ;
+
+ /* process the Content-Range header (if present) */
+ has_range = dav_parse_range(r, &range_start, &range_end);
+ if (has_range) {
+ /* use a read mode which is seekable */
+ mode = DAV_MODE_READ_SEEKABLE;
+
+ /* prep the output */
+ r->status = HTTP_PARTIAL_CONTENT;
+ ap_table_setn(r->headers_out,
+ "Content-Range",
+ ap_psprintf(r->pool, "bytes %ld-%ld/*",
+ range_start, range_end));
+ ap_set_content_length(r, range_end - range_start + 1);
+ }
+
+ if (r->header_only) {
+ ap_send_http_header(r);
+ return DONE;
+ }
+
+ if ((err = (*resource->hooks->open_stream)(resource, mode,
+ &stream)) != NULL) {
+ /* ### assuming FORBIDDEN is probably not quite right... */
+ err = dav_push_error(r->pool, HTTP_FORBIDDEN, 0,
+ ap_psprintf(r->pool,
+ "Unable to GET contents for %s.",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+
+ if (has_range
+ && (err = (*resource->hooks->seek_stream)(stream,
+ range_start)) != NULL) {
+ err = dav_push_error(r->pool, err->status, 0,
+ "Could not seek to beginning of the "
+ "specified Content-Range.", err);
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* all set. send the headers now. */
+ ap_send_http_header(r);
+
+ /* start a timeout for delivering the response. */
+ ap_soft_timeout("sending GET response", r);
+
+ buffer = ap_palloc(r->pool, DAV_READ_BLOCKSIZE);
+ while (1) {
+ size_t amt;
+
+ if (!has_range)
+ amt = DAV_READ_BLOCKSIZE;
+ else if ((range_end - range_start + 1) > DAV_READ_BLOCKSIZE)
+ amt = DAV_READ_BLOCKSIZE;
+ else {
+ /* note: range_end - range_start is an ssize_t */
+ amt = (size_t)(range_end - range_start + 1);
+ }
+
+ if ((err = (*resource->hooks->read_stream)(stream, buffer,
+ &amt)) != NULL) {
+ break;
+ }
+ if (amt == 0) {
+ /* no more content */
+ break;
+ }
+ if (ap_rwrite(buffer, amt, r) < 0) {
+ /* ### what to do with this error? */
+ break;
+ }
+
+ if (has_range) {
+ range_start += amt;
+ if (range_start > range_end)
+ break;
+ }
+
+ /* reset the timeout after a successful write */
+ ap_reset_timeout(r);
+ }
+
+ /* Done with the request; clear its timeout */
+ ap_kill_timeout(r);
+
+ if (err != NULL)
+ return dav_handle_err(r, err, NULL);
+
+ /*
+ ** ### range_start should equal range_end+1. if it doesn't, then
+ ** ### we did not send enough data to the client. the client will
+ ** ### hang (and timeout) waiting for the data.
+ **
+ ** ### what to do? abort the connection?
+ */
+ return DONE;
+ }
+
+ /* NOTREACHED */
+}
+
+/* validate resource on POST, then pass it off to the default handler */
+static int dav_method_post(request_rec *r)
+{
+ dav_resource *resource;
+ dav_error *err;
+ int result;
+
+ /* Ask repository module to resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK) {
+ return result;
+ }
+
+ /* Note: depth == 0. Implies no need for a multistatus response. */
+ if ((err = dav_validate_request(r, resource, 0, NULL, NULL,
+ DAV_VALIDATE_RESOURCE, NULL)) != NULL) {
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, NULL);
+ }
+
+ return DECLINED;
+}
+
+/* handle the PUT method */
+static int dav_method_put(request_rec *r)
+{
+ dav_resource *resource;
+ int resource_state;
+ dav_resource *resource_parent;
+ const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
+ const char *body;
+ dav_error *err;
+ dav_error *err2;
+ int result;
+ int resource_existed = 0;
+ int resource_was_writable = 0;
+ int parent_was_writable = 0;
+ dav_stream_mode mode;
+ dav_stream *stream;
+ dav_response *multi_response;
+ int has_range;
+ off_t range_start;
+ off_t range_end;
+
+ if ((result = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) != OK) {
+ return result;
+ }
+
+ /* Ask repository module to resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK) {
+ return result;
+ }
+
+ /* If not a file or collection resource, PUT not allowed */
+ if (resource->type != DAV_RESOURCE_TYPE_REGULAR) {
+ body = ap_psprintf(r->pool,
+ "Cannot create resource %s with PUT.",
+ ap_escape_html(r->pool, r->uri));
+ return dav_error_response(r, HTTP_CONFLICT, body);
+ }
+
+ /* Cannot PUT a collection */
+ if (resource->collection) {
+ return dav_error_response(r, HTTP_CONFLICT,
+ "Cannot PUT to a collection.");
+
+ }
+
+ resource_state = dav_get_resource_state(r, resource);
+
+ /*
+ ** Note: depth == 0 normally requires no multistatus response. However,
+ ** if we pass DAV_VALIDATE_PARENT, then we could get an error on a URI
+ ** other than the Request-URI, thereby requiring a multistatus.
+ **
+ ** If the resource does not exist (DAV_RESOURCE_NULL), then we must
+ ** check the resource *and* its parent. If the resource exists or is
+ ** a locknull resource, then we check only the resource.
+ */
+ if ((err = dav_validate_request(r, resource, 0, NULL, &multi_response,
+ resource_state == DAV_RESOURCE_NULL ?
+ DAV_VALIDATE_PARENT :
+ DAV_VALIDATE_RESOURCE, NULL)) != NULL) {
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, multi_response);
+ }
+
+ /* make sure the resource can be modified (if versioning repository) */
+ if ((err = dav_ensure_resource_writable(r, resource,
+ 0 /* not parent_only */,
+ &resource_parent,
+ &resource_existed,
+ &resource_was_writable,
+ &parent_was_writable)) != NULL) {
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* truncate and rewrite the file unless we see a Content-Range */
+ mode = DAV_MODE_WRITE_TRUNC;
+
+ has_range = dav_parse_range(r, &range_start, &range_end);
+ if (has_range) {
+ mode = DAV_MODE_WRITE_SEEKABLE;
+ }
+
+ /* Create the new file in the repository */
+ if ((err = (*resource->hooks->open_stream)(resource, mode,
+ &stream)) != NULL) {
+ /* ### assuming FORBIDDEN is probably not quite right... */
+ err = dav_push_error(r->pool, HTTP_FORBIDDEN, 0,
+ ap_psprintf(r->pool,
+ "Unable to PUT new contents for %s.",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ }
+
+ if (err == NULL && has_range) {
+ /* a range was provided. seek to the start */
+ err = (*resource->hooks->seek_stream)(stream, range_start);
+ }
+
+ if (err == NULL) {
+ if (ap_should_client_block(r)) {
+ char *buffer = ap_palloc(r->pool, DAV_READ_BLOCKSIZE);
+ long len;
+
+ /*
+ ** Once we start reading the request, then we must read the
+ ** whole darn thing. ap_discard_request_body() won't do anything
+ ** for a partially-read request.
+ */
+
+ while ((len = ap_get_client_block(r, buffer,
+ DAV_READ_BLOCKSIZE)) > 0) {
+ if (err == NULL) {
+ /* write whatever we read, until we see an error */
+ err = (*resource->hooks->write_stream)(stream,
+ buffer, len);
+ }
+ }
+
+ /*
+ ** ### what happens if we read more/less than the amount
+ ** ### specified in the Content-Range? eek...
+ */
+
+ if (len == -1) {
+ /*
+ ** Error reading request body. This has precedence over
+ ** prior errors.
+ */
+ err = dav_new_error(r->pool, HTTP_BAD_REQUEST, 0,
+ "An error occurred while reading the "
+ "request body.");
+ }
+ }
+
+ err2 = (*resource->hooks->close_stream)(stream,
+ err == NULL /* commit */);
+ if (err2 != NULL && err == NULL) {
+ /* no error during the write, but we hit one at close. use it. */
+ err = err2;
+ }
+ }
+
+ /*
+ ** Ensure that we think the resource exists now.
+ ** ### eek. if an error occurred during the write and we did not commit,
+ ** ### then the resource might NOT exist (e.g. dav_fs_repos.c)
+ */
+ if (err == NULL) {
+ resource->exists = 1;
+ }
+
+ /* restore modifiability of resources back to what they were */
+ err2 = dav_revert_resource_writability(r, resource, resource_parent,
+ err != NULL /* undo if error */,
+ resource_existed,
+ resource_was_writable,
+ parent_was_writable);
+
+ /* check for errors now */
+ if (err != NULL) {
+ return dav_handle_err(r, err, NULL);
+ }
+ if (err2 != NULL) {
+ /* just log a warning */
+ err2 = dav_push_error(r->pool, err->status, 0,
+ "The PUT was successful, but there "
+ "was a problem reverting the writability of "
+ "the resource or its parent collection.",
+ err2);
+ dav_log_err(r, err2, APLOG_WARNING);
+ }
+
+ /* ### place the Content-Type and Content-Language into the propdb */
+
+ if (locks_hooks != NULL) {
+ dav_lockdb *lockdb;
+
+ if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) {
+ /* The file creation was successful, but the locking failed. */
+ err = dav_push_error(r->pool, err->status, 0,
+ "The file was PUT successfully, but there "
+ "was a problem opening the lock database "
+ "which prevents inheriting locks from the "
+ "parent resources.",
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* notify lock system that we have created/replaced a resource */
+ err = dav_notify_created(r, lockdb, resource, resource_state, 0);
+
+ (*locks_hooks->close_lockdb)(lockdb);
+
+ if (err != NULL) {
+ /* The file creation was successful, but the locking failed. */
+ err = dav_push_error(r->pool, err->status, 0,
+ "The file was PUT successfully, but there "
+ "was a problem updating its lock "
+ "information.",
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+ }
+
+ /* NOTE: WebDAV spec, S8.7.1 states properties should be unaffected */
+
+ /* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */
+ return dav_created(r, NULL, resource, "Resource", resource_existed);
+}
+
+/* ### move this to dav_util? */
+void dav_add_response(dav_walker_ctx *ctx, const char *href, int status,
+ dav_get_props_result *propstats)
+{
+ dav_response *resp;
+
+ /* just drop some data into an dav_response */
+ resp = ap_pcalloc(ctx->pool, sizeof(*resp));
+ resp->href = ap_pstrdup(ctx->pool, href);
+ resp->status = status;
+ if (propstats) {
+ resp->propresult = *propstats;
+ }
+ resp->next = ctx->response;
+
+ ctx->response = resp;
+}
+
+/* handle the DELETE method */
+static int dav_method_delete(request_rec *r)
+{
+ dav_resource *resource;
+ dav_resource *resource_parent = NULL;
+ dav_error *err;
+ dav_error *err2;
+ dav_response *multi_response;
+ const char *body;
+ int result;
+ int depth;
+ int parent_was_writable = 0;
+
+ /* We don't use the request body right now, so torch it. */
+ if ((result = ap_discard_request_body(r)) != OK) {
+ return result;
+ }
+
+ /* Ask repository module to resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK)
+ return result;
+ if (!resource->exists) {
+ /* Apache will supply a default error for this. */
+ return HTTP_NOT_FOUND;
+ }
+
+ /* 2518 says that depth must be infinity only for collections.
+ * For non-collections, depth is ignored, unless it is an illegal value (1).
+ */
+ depth = dav_get_depth(r, DAV_INFINITY);
+
+ if (resource->collection && depth != DAV_INFINITY) {
+ /* This supplies additional information for the default message. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "Depth must be \"infinity\" for DELETE of a collection.");
+ return HTTP_BAD_REQUEST;
+ }
+ if (!resource->collection && depth == 1) {
+ /* This supplies additional information for the default message. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "Depth of \"1\" is not allowed for DELETE.");
+ return HTTP_BAD_REQUEST;
+ }
+
+ /* Check for valid resource type */
+ /* ### allow DAV_RESOURCE_TYPE_REVISION with All-Bindings header */
+ if (resource->type != DAV_RESOURCE_TYPE_REGULAR &&
+ resource->type != DAV_RESOURCE_TYPE_WORKSPACE) {
+ body = ap_psprintf(r->pool,
+ "Cannot delete resource %s.",
+ ap_escape_html(r->pool, r->uri));
+ return dav_error_response(r, HTTP_CONFLICT, body);
+ }
+
+ /*
+ ** If any resources fail the lock/If: conditions, then we must fail
+ ** the delete. Each of the failing resources will be listed within
+ ** a DAV:multistatus body, wrapped into a 424 response.
+ **
+ ** Note that a failure on the resource itself does not generate a
+ ** multistatus response -- only internal members/collections.
+ */
+ if ((err = dav_validate_request(r, resource, depth, NULL,
+ &multi_response,
+ DAV_VALIDATE_PARENT
+ | DAV_VALIDATE_USE_424, NULL)) != NULL) {
+ err = dav_push_error(r->pool, err->status, 0,
+ ap_psprintf(r->pool,
+ "Could not DELETE %s due to a failed "
+ "precondition (e.g. locks).",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ return dav_handle_err(r, err, multi_response);
+ }
+
+ /* ### RFC 2518 s. 8.10.5 says to remove _all_ locks, not just those
+ * locked by the token(s) in the if_header.
+ */
+ if ((result = dav_unlock(r, resource, NULL)) != OK) {
+ return result;
+ }
+
+ /* if versioned resource, make sure parent is checked out */
+ if ((err = dav_ensure_resource_writable(r, resource, 1 /* parent_only */,
+ &resource_parent,
+ NULL, NULL,
+ &parent_was_writable)) != NULL) {
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* try to remove the resource */
+ err = (*resource->hooks->remove_resource)(resource, &multi_response);
+
+ /* restore writability of parent back to what it was */
+ err2 = dav_revert_resource_writability(r, NULL, resource_parent,
+ err != NULL /* undo if error */,
+ 0, 0, parent_was_writable);
+
+ /* check for errors now */
+ if (err != NULL) {
+ err = dav_push_error(r->pool, err->status, 0,
+ ap_psprintf(r->pool,
+ "Could not DELETE %s.",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ return dav_handle_err(r, err, multi_response);
+ }
+ if (err2 != NULL) {
+ /* just log a warning */
+ err = dav_push_error(r->pool, err2->status, 0,
+ "The DELETE was successful, but there "
+ "was a problem reverting the writability of "
+ "its parent collection.",
+ err2);
+ dav_log_err(r, err, APLOG_WARNING);
+ }
+
+ /* ### HTTP_NO_CONTENT if no body, HTTP_OK if there is a body (some day) */
+
+ /* Apache will supply a default error for this. */
+ return HTTP_NO_CONTENT;
+}
+
+/* handle the OPTIONS method */
+static int dav_method_options(request_rec *r)
+{
+ const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
+ const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
+ dav_resource *resource;
+ const char *options;
+ const char *dav_level;
+ const char *vsn_level;
+ int result;
+ const dav_dir_conf *conf;
+ const dav_dyn_hooks *lp;
+
+ /* per HTTP/1.1 S9.2, we can discard this body */
+ if ((result = ap_discard_request_body(r)) != OK) {
+ return result;
+ }
+
+ /* no body */
+ ap_set_content_length(r, 0);
+
+ /* resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK)
+ return result;
+
+ /* determine which providers are available */
+ dav_level = "1";
+ vsn_level = NULL;
+
+ if (locks_hooks != NULL) {
+ dav_level = "1,2";
+ }
+ if (vsn_hooks != NULL) {
+ vsn_level = (*vsn_hooks->get_vsn_header)();
+ }
+
+ /*
+ ** Iterate through the live property providers; add their URIs to
+ ** the dav_level string.
+ */
+ conf = (dav_dir_conf *) ap_get_module_config(r->per_dir_config,
+ &dav_module);
+ for (lp = conf->liveprop; lp != NULL; lp = lp->next) {
+ const char *uri = DAV_AS_HOOKS_LIVEPROP(lp)->propset_uri;
+
+ if (uri != NULL)
+ dav_level = ap_pstrcat(r->pool, dav_level, ",<", uri, ">", NULL);
+ }
+
+ /* this tells MSFT products to skip looking for FrontPage extensions */
+ ap_table_setn(r->headers_out, "MS-Author-Via", "DAV");
+
+ /*
+ ** Three cases: resource is null (3), is lock-null (7.4), or exists.
+ **
+ ** All cases support OPTIONS and LOCK.
+ ** (Lock-) null resources also support MKCOL and PUT.
+ ** Lock-null support PROPFIND and UNLOCK.
+ ** Existing resources support lots of stuff.
+ */
+
+ /* ### take into account resource type */
+ switch (dav_get_resource_state(r, resource))
+ {
+ case DAV_RESOURCE_EXISTS:
+ /* resource exists */
+ if (resource->collection) {
+ options = ap_pstrcat(r->pool,
+ "OPTIONS, "
+ "GET, HEAD, POST, DELETE, TRACE, "
+ "PROPFIND, PROPPATCH, COPY, MOVE",
+ locks_hooks != NULL ? ", LOCK, UNLOCK" : "",
+ NULL);
+ }
+ else {
+ /* files also support PUT */
+ options = ap_pstrcat(r->pool,
+ "OPTIONS, "
+ "GET, HEAD, POST, DELETE, TRACE, "
+ "PROPFIND, PROPPATCH, COPY, MOVE, PUT",
+ locks_hooks != NULL ? ", LOCK, UNLOCK" : "",
+ NULL);
+ }
+ break;
+
+ case DAV_RESOURCE_LOCK_NULL:
+ /* resource is lock-null. */
+ options = ap_pstrcat(r->pool, "OPTIONS, MKCOL, PUT, PROPFIND",
+ locks_hooks != NULL ? ", LOCK, UNLOCK" : "",
+ NULL);
+ break;
+
+ case DAV_RESOURCE_NULL:
+ /* resource is null. */
+ options = ap_pstrcat(r->pool, "OPTIONS, MKCOL, PUT",
+ locks_hooks != NULL ? ", LOCK" : "",
+ NULL);
+ break;
+
+ default:
+ /* ### internal error! */
+ options = "OPTIONS";
+ break;
+ }
+
+ /* If there is a versioning provider, add versioning options */
+ if (vsn_hooks != NULL) {
+ const char *vsn_options = NULL;
+
+ /* ### take into account resource type */
+ if (!resource->exists) {
+ if ((*vsn_hooks->versionable)(resource))
+ vsn_options = ", MKRESOURCE";
+ }
+ else if (!resource->versioned) {
+ if ((*vsn_hooks->versionable)(resource))
+ vsn_options = ", CHECKIN";
+ }
+ else if (resource->working)
+ vsn_options = ", CHECKIN, UNCHECKOUT";
+ else
+ vsn_options = ", CHECKOUT";
+
+ if (vsn_options != NULL)
+ options = ap_pstrcat(r->pool, options, vsn_options, NULL);
+ }
+
+ ap_table_setn(r->headers_out, "Allow", options);
+ ap_table_setn(r->headers_out, "DAV", dav_level);
+
+ if (vsn_level != NULL)
+ ap_table_setn(r->headers_out, "Versioning", vsn_level);
+
+ /* ### this will send a Content-Type. the default OPTIONS does not. */
+ ap_send_http_header(r);
+
+ /* ### the default (ap_send_http_options) returns OK, but I believe
+ * ### that is because it is the default handler and nothing else
+ * ### will run after the thing. */
+
+ /* we've sent everything necessary to the client. */
+ return DONE;
+}
+
+static void dav_cache_badprops(dav_walker_ctx *ctx)
+{
+ const dav_xml_elem *elem;
+ dav_text_header hdr = { 0 };
+
+ /* just return if we built the thing already */
+ if (ctx->propstat_404 != NULL) {
+ return;
+ }
+
+ dav_text_append(ctx->pool, &hdr,
+ "<D:propstat>" DEBUG_CR
+ "<D:prop>" DEBUG_CR);
+
+ elem = dav_find_child(ctx->doc->root, "prop");
+ for (elem = elem->first_child; elem; elem = elem->next) {
+ dav_text_append(ctx->pool, &hdr, dav_empty_elem(ctx->pool, elem));
+ }
+
+ dav_text_append(ctx->pool, &hdr,
+ "</D:prop>" DEBUG_CR
+ "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR
+ "</D:propstat>" DEBUG_CR);
+
+ ctx->propstat_404 = hdr.first;
+}
+
+static dav_error * dav_propfind_walker(dav_walker_ctx *ctx, int calltype)
+{
+ dav_error *err;
+ dav_propdb *propdb;
+ dav_get_props_result propstats = { 0 };
+
+ /*
+ ** Note: ctx->doc can only be NULL for DAV_PROPFIND_IS_ALLPROP. Since
+ ** dav_get_allprops() does not need to do namespace translation,
+ ** we're okay.
+ **
+ ** Note: we cast to lose the "const". The propdb won't try to change
+ ** the resource, however, since we are opening readonly.
+ */
+ err = dav_open_propdb(ctx->r, ctx->lockdb,
+ (dav_resource *)ctx->resource, 1,
+ ctx->doc ? ctx->doc->namespaces : NULL, &propdb);
+ if (err != NULL) {
+ /* ### do something with err! */
+
+ if (ctx->propfind_type == DAV_PROPFIND_IS_PROP) {
+ dav_get_props_result badprops = { 0 };
+
+ /* some props were expected on this collection/resource */
+ dav_cache_badprops(ctx);
+ badprops.propstats = ctx->propstat_404;
+ dav_add_response(ctx, ctx->uri.buf, 0, &badprops);
+ }
+ else {
+ /* no props on this collection/resource */
+ dav_add_response(ctx, ctx->uri.buf, HTTP_OK, NULL);
+ }
+ return NULL;
+ }
+ /* ### what to do about closing the propdb on server failure? */
+
+ if (ctx->propfind_type == DAV_PROPFIND_IS_PROP) {
+ propstats = dav_get_props(propdb, ctx->doc);
+ }
+ else {
+ propstats = dav_get_allprops(propdb,
+ ctx->propfind_type == DAV_PROPFIND_IS_ALLPROP);
+ }
+ dav_close_propdb(propdb);
+
+ dav_add_response(ctx, ctx->uri.buf, 0, &propstats);
+
+ return NULL;
+}
+
+/* handle the PROPFIND method */
+static int dav_method_propfind(request_rec *r)
+{
+ dav_resource *resource;
+ int depth;
+ dav_error *err;
+ int result;
+ dav_xml_doc *doc;
+ const dav_xml_elem *child;
+ dav_walker_ctx ctx = { 0 };
+
+ /* Ask repository module to resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK)
+ return result;
+
+ if (dav_get_resource_state(r, resource) == DAV_RESOURCE_NULL) {
+ /* Apache will supply a default error for this. */
+ return HTTP_NOT_FOUND;
+ }
+
+ if ((depth = dav_get_depth(r, DAV_INFINITY)) < 0) {
+ /* dav_get_depth() supplies additional information for the
+ * default message. */
+ return HTTP_BAD_REQUEST;
+ }
+
+ if (depth == DAV_INFINITY) {
+ dav_dir_conf *conf;
+ conf = (dav_dir_conf *) ap_get_module_config(r->per_dir_config,
+ &dav_module);
+ /* default is to DISALLOW these requests */
+ if (conf->allow_depthinfinity != DAV_ENABLED_ON) {
+ return dav_error_response(r, HTTP_FORBIDDEN,
+ ap_psprintf(r->pool,
+ "PROPFIND requests with a "
+ "Depth of \"infinity\" are "
+ "not allowed for %s.",
+ ap_escape_html(r->pool,
+ r->uri)));
+ }
+ }
+
+ if ((result = dav_parse_input(r, &doc)) != OK) {
+ return result;
+ }
+ /* note: doc == NULL if no request body */
+
+ if (doc && !dav_validate_root(doc, "propfind")) {
+ /* This supplies additional information for the default message. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "The \"propfind\" element was not found.");
+ return HTTP_BAD_REQUEST;
+ }
+
+ /* ### validate that only one of these three elements is present */
+
+ if (doc == NULL
+ || (child = dav_find_child(doc->root, "allprop")) != NULL) {
+ /* note: no request body implies allprop */
+ ctx.propfind_type = DAV_PROPFIND_IS_ALLPROP;
+ }
+ else if ((child = dav_find_child(doc->root, "propname")) != NULL) {
+ ctx.propfind_type = DAV_PROPFIND_IS_PROPNAME;
+ }
+ else if ((child = dav_find_child(doc->root, "prop")) != NULL) {
+ ctx.propfind_type = DAV_PROPFIND_IS_PROP;
+ }
+ else {
+ /* "propfind" element must have one of the above three children */
+
+ /* This supplies additional information for the default message. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "The \"propfind\" element does not contain one of "
+ "the required child elements (the specific command).");
+ return HTTP_BAD_REQUEST;
+ }
+
+ ctx.walk_type = DAV_WALKTYPE_ALL | DAV_WALKTYPE_AUTH;
+ ctx.func = dav_propfind_walker;
+ ctx.pool = r->pool;
+ ctx.doc = doc;
+ ctx.r = r;
+ ctx.resource = resource;
+
+ dav_buffer_init(r->pool, &ctx.uri, r->uri);
+
+ /* ### should open read-only */
+ if ((err = dav_open_lockdb(r, 0, &ctx.lockdb)) != NULL) {
+ err = dav_push_error(r->pool, err->status, 0,
+ "The lock database could not be opened, "
+ "preventing access to the various lock "
+ "properties for the PROPFIND.",
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+ if (ctx.lockdb != NULL) {
+ /* if we have a lock database, then we can walk locknull resources */
+ ctx.walk_type |= DAV_WALKTYPE_LOCKNULL;
+ }
+
+ err = (*resource->hooks->walk)(&ctx, depth);
+
+ if (ctx.lockdb != NULL) {
+ (*ctx.lockdb->hooks->close_lockdb)(ctx.lockdb);
+ }
+
+ if (err != NULL) {
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* return a 207 (Multi-Status) response now. */
+
+ /* if a 404 was generated for an HREF, then we need to spit out the
+ * doc's namespaces for use by the 404. Note that <response> elements
+ * will override these ns0, ns1, etc, but NOT within the <response>
+ * scope for the badprops. */
+ /* NOTE: propstat_404 != NULL implies doc != NULL */
+ if (ctx.propstat_404 != NULL) {
+ dav_send_multistatus(r, HTTP_MULTI_STATUS, ctx.response,
+ doc->namespaces);
+ }
+ else {
+ dav_send_multistatus(r, HTTP_MULTI_STATUS, ctx.response, NULL);
+ }
+
+ /* the response has been sent. */
+ return DONE;
+}
+
+static dav_text * dav_failed_proppatch(pool *p, array_header *prop_ctx)
+{
+ dav_text_header hdr = { 0 };
+ int i = prop_ctx->nelts;
+ dav_prop_ctx *ctx = (dav_prop_ctx *)prop_ctx->elts;
+ dav_error *err424_set = NULL;
+ dav_error *err424_delete = NULL;
+ const char *s;
+
+ /* ### might be nice to sort by status code and description */
+
+ for ( ; i-- > 0; ++ctx ) {
+ dav_text_append(p, &hdr,
+ "<D:propstat>" DEBUG_CR
+ "<D:prop>");
+ dav_text_append(p, &hdr, dav_empty_elem(p, ctx->prop));
+ dav_text_append(p, &hdr, "</D:prop>" DEBUG_CR);
+
+ if (ctx->err == NULL) {
+ /* nothing was assigned here yet, so make it a 424 */
+
+ if (ctx->operation == DAV_PROP_OP_SET) {
+ if (err424_set == NULL)
+ err424_set = dav_new_error(p, HTTP_FAILED_DEPENDENCY, 0,
+ "Attempted DAV:set operation "
+ "could not be completed due "
+ "to other errors.");
+ ctx->err = err424_set;
+ }
+ else if (ctx->operation == DAV_PROP_OP_DELETE) {
+ if (err424_delete == NULL)
+ err424_delete = dav_new_error(p, HTTP_FAILED_DEPENDENCY, 0,
+ "Attempted DAV:remove "
+ "operation could not be "
+ "completed due to other "
+ "errors.");
+ ctx->err = err424_delete;
+ }
+ }
+
+ s = ap_psprintf(p,
+ "<D:status>"
+ "HTTP/1.1 %d (status)"
+ "</D:status>" DEBUG_CR,
+ ctx->err->status);
+ dav_text_append(p, &hdr, s);
+
+ /* ### we should use compute_desc if necessary... */
+ if (ctx->err->desc != NULL) {
+ dav_text_append(p, &hdr, "<D:responsedescription>" DEBUG_CR);
+ dav_text_append(p, &hdr, ctx->err->desc);
+ dav_text_append(p, &hdr, "</D:responsedescription>" DEBUG_CR);
+ }
+
+ dav_text_append(p, &hdr, "</D:propstat>" DEBUG_CR);
+ }
+
+ return hdr.first;
+}
+
+static dav_text * dav_success_proppatch(pool *p, array_header *prop_ctx)
+{
+ dav_text_header hdr = { 0 };
+ int i = prop_ctx->nelts;
+ dav_prop_ctx *ctx = (dav_prop_ctx *)prop_ctx->elts;
+
+ /*
+ ** ### we probably need to revise the way we assemble the response...
+ ** ### this code assumes everything will return status==200.
+ */
+
+ dav_text_append(p, &hdr,
+ "<D:propstat>" DEBUG_CR
+ "<D:prop>" DEBUG_CR);
+
+ for ( ; i-- > 0; ++ctx ) {
+ dav_text_append(p, &hdr, dav_empty_elem(p, ctx->prop));
+ }
+
+ dav_text_append(p, &hdr,
+ "</D:prop>" DEBUG_CR
+ "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
+ "</D:propstat>" DEBUG_CR);
+
+ return hdr.first;
+}
+
+static void dav_prop_log_errors(dav_prop_ctx *ctx)
+{
+ dav_log_err(ctx->r, ctx->err, APLOG_ERR);
+}
+
+/*
+** Call <func> for each context. This can stop when an error occurs, or
+** simply iterate through the whole list.
+**
+** Returns 1 if an error occurs (and the iteration is aborted). Returns 0
+** if all elements are processed.
+**
+** If <reverse> is true (non-zero), then the list is traversed in
+** reverse order.
+*/
+static int dav_process_ctx_list(void (*func)(dav_prop_ctx *ctx),
+ array_header *ctx_list, int stop_on_error,
+ int reverse)
+{
+ int i = ctx_list->nelts;
+ dav_prop_ctx *ctx = (dav_prop_ctx *)ctx_list->elts;
+
+ if (reverse)
+ ctx += i;
+
+ while (i--) {
+ if (reverse)
+ --ctx;
+
+ (*func)(ctx);
+ if (stop_on_error && DAV_PROP_CTX_HAS_ERR(*ctx)) {
+ return 1;
+ }
+
+ if (!reverse)
+ ++ctx;
+ }
+
+ return 0;
+}
+
+/* handle the PROPPATCH method */
+static int dav_method_proppatch(request_rec *r)
+{
+ dav_error *err;
+ dav_resource *resource;
+ int result;
+ dav_xml_doc *doc;
+ dav_xml_elem *child;
+ dav_propdb *propdb;
+ int failure = 0;
+ dav_response resp = { 0 };
+ dav_text *propstat_text;
+ array_header *ctx_list;
+ dav_prop_ctx *ctx;
+
+ /* Ask repository module to resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK)
+ return result;
+ if (!resource->exists) {
+ /* Apache will supply a default error for this. */
+ return HTTP_NOT_FOUND;
+ }
+
+ if ((result = dav_parse_input(r, &doc)) != OK) {
+ return result;
+ }
+ /* note: doc == NULL if no request body */
+
+ if (doc == NULL || !dav_validate_root(doc, "propertyupdate")) {
+ /* This supplies additional information for the default message. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "The request body does not contain "
+ "a \"propertyupdate\" element.");
+ return HTTP_BAD_REQUEST;
+ }
+
+ /* Check If-Headers and existing locks */
+ /* Note: depth == 0. Implies no need for a multistatus response. */
+ if ((err = dav_validate_request(r, resource, 0, NULL, NULL,
+ DAV_VALIDATE_RESOURCE, NULL)) != NULL) {
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, NULL);
+ }
+
+ if ((err = dav_open_propdb(r, NULL, resource, 0, doc->namespaces,
+ &propdb)) != NULL) {
+ err = dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ ap_psprintf(r->pool,
+ "Could not open the property "
+ "database for %s.",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+ /* ### what to do about closing the propdb on server failure? */
+
+ /* ### validate "live" properties */
+
+ /* set up an array to hold property operation contexts */
+ ctx_list = ap_make_array(r->pool, 10, sizeof(dav_prop_ctx));
+
+ /* do a first pass to ensure that all "remove" properties exist */
+ for (child = doc->root->first_child; child; child = child->next) {
+ int is_remove;
+ dav_xml_elem *prop_group;
+ dav_xml_elem *one_prop;
+
+ /* Ignore children that are not set/remove */
+ if (child->ns != DAV_NS_DAV_ID
+ || (!(is_remove = strcmp(child->name, "remove") == 0)
+ && strcmp(child->name, "set") != 0)) {
+ continue;
+ }
+
+ /* make sure that a "prop" child exists for set/remove */
+ if ((prop_group = dav_find_child(child, "prop")) == NULL) {
+ dav_close_propdb(propdb);
+
+ /* This supplies additional information for the default message. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "A \"prop\" element is missing inside "
+ "the propertyupdate command.");
+ return HTTP_BAD_REQUEST;
+ }
+
+ for (one_prop = prop_group->first_child; one_prop;
+ one_prop = one_prop->next) {
+
+ ctx = (dav_prop_ctx *)ap_push_array(ctx_list);
+ ctx->propdb = propdb;
+ ctx->operation = is_remove ? DAV_PROP_OP_DELETE : DAV_PROP_OP_SET;
+ ctx->prop = one_prop;
+
+ ctx->r = r; /* for later use by dav_prop_log_errors() */
+
+ dav_prop_validate(ctx);
+
+ if ( DAV_PROP_CTX_HAS_ERR(*ctx) ) {
+ failure = 1;
+ }
+ }
+ }
+
+ /* ### should test that we found at least one set/remove */
+
+ /* execute all of the operations */
+ if (!failure && dav_process_ctx_list(dav_prop_exec, ctx_list, 1, 0)) {
+ failure = 1;
+ }
+
+ /* generate a failure/success response */
+ if (failure) {
+ (void)dav_process_ctx_list(dav_prop_rollback, ctx_list, 0, 1);
+ propstat_text = dav_failed_proppatch(r->pool, ctx_list);
+ }
+ else {
+ (void)dav_process_ctx_list(dav_prop_commit, ctx_list, 0, 0);
+ propstat_text = dav_success_proppatch(r->pool, ctx_list);
+ }
+
+ /* make sure this gets closed! */
+ dav_close_propdb(propdb);
+
+ /* log any errors that occurred */
+ (void)dav_process_ctx_list(dav_prop_log_errors, ctx_list, 0, 0);
+
+ resp.href = resource->uri;
+
+ /* ### should probably use something new to pass along this text... */
+ resp.propresult.propstats = propstat_text;
+
+ dav_send_multistatus(r, HTTP_MULTI_STATUS, &resp, doc->namespaces);
+
+ /* the response has been sent. */
+ return DONE;
+}
+
+static int process_mkcol_body(request_rec *r)
+{
+ /* This is snarfed from ap_setup_client_block(). We could get pretty
+ * close to this behavior by passing REQUEST_NO_BODY, but we need to
+ * return HTTP_UNSUPPORTED_MEDIA_TYPE (while ap_setup_client_block
+ * returns HTTP_REQUEST_ENTITY_TOO_LARGE). */
+
+ const char *tenc = ap_table_get(r->headers_in, "Transfer-Encoding");
+ const char *lenp = ap_table_get(r->headers_in, "Content-Length");
+
+ /* make sure to set the Apache request fields properly. */
+ r->read_body = REQUEST_NO_BODY;
+ r->read_chunked = 0;
+ r->remaining = 0;
+
+ if (tenc) {
+ if (strcasecmp(tenc, "chunked")) {
+ /* Use this instead of Apache's default error string */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "Unknown Transfer-Encoding %s", tenc);
+ return HTTP_NOT_IMPLEMENTED;
+ }
+
+ r->read_chunked = 1;
+ }
+ else if (lenp) {
+ const char *pos = lenp;
+
+ while (ap_isdigit(*pos) || ap_isspace(*pos)) {
+ ++pos;
+ }
+ if (*pos != '\0') {
+ /* This supplies additional information for the default message. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "Invalid Content-Length %s", lenp);
+ return HTTP_BAD_REQUEST;
+ }
+
+ r->remaining = atol(lenp);
+ }
+
+ if (r->read_chunked || r->remaining > 0) {
+ /* ### log something? */
+
+ /* Apache will supply a default error for this. */
+ return HTTP_UNSUPPORTED_MEDIA_TYPE;
+ }
+
+ /*
+ ** Get rid of the body. this will call ap_setup_client_block(), but
+ ** our copy above has already verified its work.
+ */
+ return ap_discard_request_body(r);
+}
+
+/* handle the MKCOL method */
+static int dav_method_mkcol(request_rec *r)
+{
+ dav_resource *resource;
+ int resource_state;
+ dav_resource *resource_parent;
+ const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
+ dav_error *err;
+ dav_error *err2;
+ int result;
+ dav_dir_conf *conf;
+ int parent_was_writable = 0;
+ dav_response *multi_status;
+
+ /* handle the request body */
+ /* ### this may move lower once we start processing bodies */
+ if ((result = process_mkcol_body(r)) != OK) {
+ return result;
+ }
+
+ conf = (dav_dir_conf *) ap_get_module_config(r->per_dir_config,
+ &dav_module);
+
+ /* Ask repository module to resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK)
+ return result;
+
+ if (resource->exists) {
+ /* oops. something was already there! */
+
+ /* Apache will supply a default error for this. */
+ /* ### we should provide a specific error message! */
+ return HTTP_METHOD_NOT_ALLOWED;
+ }
+
+ resource_state = dav_get_resource_state(r, resource);
+
+ /*
+ ** Check If-Headers and existing locks.
+ **
+ ** Note: depth == 0 normally requires no multistatus response. However,
+ ** if we pass DAV_VALIDATE_PARENT, then we could get an error on a URI
+ ** other than the Request-URI, thereby requiring a multistatus.
+ **
+ ** If the resource does not exist (DAV_RESOURCE_NULL), then we must
+ ** check the resource *and* its parent. If the resource exists or is
+ ** a locknull resource, then we check only the resource.
+ */
+ if ((err = dav_validate_request(r, resource, 0, NULL, &multi_status,
+ resource_state == DAV_RESOURCE_NULL ?
+ DAV_VALIDATE_PARENT :
+ DAV_VALIDATE_RESOURCE, NULL)) != NULL) {
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, multi_status);
+ }
+
+ /* if versioned resource, make sure parent is checked out */
+ if ((err = dav_ensure_resource_writable(r, resource, 1 /* parent_only */,
+ &resource_parent,
+ NULL, NULL,
+ &parent_was_writable)) != NULL) {
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* try to create the collection */
+ resource->collection = 1;
+ err = (*resource->hooks->create_collection)(r->pool, resource);
+
+ /* restore modifiability of parent back to what it was */
+ err2 = dav_revert_resource_writability(r, NULL, resource_parent,
+ err != NULL /* undo if error */,
+ 0, 0, parent_was_writable);
+
+ /* check for errors now */
+ if (err != NULL) {
+ return dav_handle_err(r, err, NULL);
+ }
+ if (err2 != NULL) {
+ /* just log a warning */
+ err = dav_push_error(r->pool, err->status, 0,
+ "The MKCOL was successful, but there "
+ "was a problem reverting the writability of "
+ "its parent collection.",
+ err2);
+ dav_log_err(r, err, APLOG_WARNING);
+ }
+
+ if (locks_hooks != NULL) {
+ dav_lockdb *lockdb;
+
+ if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) {
+ /* The directory creation was successful, but the locking failed. */
+ err = dav_push_error(r->pool, err->status, 0,
+ "The MKCOL was successful, but there "
+ "was a problem opening the lock database "
+ "which prevents inheriting locks from the "
+ "parent resources.",
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* notify lock system that we have created/replaced a resource */
+ err = dav_notify_created(r, lockdb, resource, resource_state, 0);
+
+ (*locks_hooks->close_lockdb)(lockdb);
+
+ if (err != NULL) {
+ /* The dir creation was successful, but the locking failed. */
+ err = dav_push_error(r->pool, err->status, 0,
+ "The MKCOL was successful, but there "
+ "was a problem updating its lock "
+ "information.",
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+ }
+
+ /* return an appropriate response (HTTP_CREATED) */
+ return dav_created(r, NULL, resource, "Collection", 0);
+}
+
+/* handle the COPY and MOVE methods */
+static int dav_method_copymove(request_rec *r, int is_move)
+{
+ dav_resource *resource;
+ dav_resource *resource_parent = NULL;
+ dav_resource *resnew;
+ dav_resource *resnew_parent = NULL;
+ const char *body;
+ const char *dest;
+ dav_error *err;
+ dav_error *err2;
+ dav_error *err3;
+ dav_response *multi_response;
+ dav_lookup_result lookup;
+ int is_dir;
+ int overwrite;
+ int depth;
+ int result;
+ dav_lockdb *lockdb;
+ int replaced;
+ int src_parent_was_writable = 0;
+ int dst_parent_was_writable = 0;
+
+ /* Ask repository module to resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK)
+ return result;
+ if (!resource->exists) {
+ /* Apache will supply a default error for this. */
+ return HTTP_NOT_FOUND;
+ }
+
+ /* If not a file or collection resource, COPY/MOVE not allowed */
+ if (resource->type != DAV_RESOURCE_TYPE_REGULAR) {
+ body = ap_psprintf(r->pool,
+ "Cannot COPY/MOVE resource %s.",
+ ap_escape_html(r->pool, r->uri));
+ return dav_error_response(r, HTTP_METHOD_NOT_ALLOWED, body);
+ }
+
+ /* get the destination URI */
+ dest = ap_table_get(r->headers_in, "Destination");
+ if (dest == NULL) {
+ /* Look in headers provided by Netscape's Roaming Profiles */
+ const char *nscp_host = ap_table_get(r->headers_in, "Host");
+ const char *nscp_path = ap_table_get(r->headers_in, "New-uri");
+
+ if (nscp_host != NULL && nscp_path != NULL)
+ dest = ap_psprintf(r->pool, "http://%s%s", nscp_host, nscp_path);
+ }
+ if (dest == NULL) {
+ /* This supplies additional information for the default message. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "The request is missing a Destination header.");
+ return HTTP_BAD_REQUEST;
+ }
+
+ lookup = dav_lookup_uri(dest, r);
+ if (lookup.rnew == NULL) {
+ if (lookup.err.status == HTTP_BAD_REQUEST) {
+ /* This supplies additional information for the default message. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ lookup.err.desc);
+ return HTTP_BAD_REQUEST;
+ }
+
+ /* ### this assumes that dav_lookup_uri() only generates a status
+ * ### that Apache can provide a status line for!! */
+
+ return dav_error_response(r, lookup.err.status, lookup.err.desc);
+ }
+ if (lookup.rnew->status != HTTP_OK) {
+ /* ### how best to report this... */
+ return dav_error_response(r, lookup.rnew->status,
+ "Destination URI had an error.");
+ }
+
+ /* Resolve destination resource */
+ result = dav_get_resource(lookup.rnew, &resnew);
+ if (result != OK)
+ return result;
+
+ /* are the two resources handled by the same repository? */
+ if (resource->hooks != resnew->hooks) {
+ /* ### this message exposes some backend config, but screw it... */
+ return dav_error_response(r, HTTP_BAD_GATEWAY,
+ "Destination URI is handled by a "
+ "different repository than the source URI. "
+ "MOVE or COPY between repositories is "
+ "not possible.");
+ }
+
+ /* get and parse the overwrite header value */
+ if ((overwrite = dav_get_overwrite(r)) < 0) {
+ /* dav_get_overwrite() supplies additional information for the
+ * default message. */
+ return HTTP_BAD_REQUEST;
+ }
+
+ /* quick failure test: if dest exists and overwrite is false. */
+ if (resnew->exists && !overwrite) {
+ /* Supply some text for the error response body. */
+ return dav_error_response(r, HTTP_PRECONDITION_FAILED,
+ "Destination is not empty and "
+ "Overwrite is not \"T\"");
+ }
+
+ /* are the source and destination the same? */
+ if ((*resource->hooks->is_same_resource)(resource, resnew)) {
+ /* Supply some text for the error response body. */
+ return dav_error_response(r, HTTP_FORBIDDEN,
+ "Source and Destination URIs are the same.");
+
+ }
+
+ is_dir = resource->collection;
+
+ /* get and parse the Depth header value. "0" and "infinity" are legal. */
+ if ((depth = dav_get_depth(r, DAV_INFINITY)) < 0) {
+ /* dav_get_depth() supplies additional information for the
+ * default message. */
+ return HTTP_BAD_REQUEST;
+ }
+ if (depth == 1) {
+ /* This supplies additional information for the default message. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "Depth must be \"0\" or \"infinity\" for COPY or MOVE.");
+ return HTTP_BAD_REQUEST;
+ }
+ if (is_move && is_dir && depth != DAV_INFINITY) {
+ /* This supplies additional information for the default message. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "Depth must be \"infinity\" when moving a collection.");
+ return HTTP_BAD_REQUEST;
+ }
+
+ /*
+ ** Check If-Headers and existing locks for each resource in the source
+ ** if we are performing a MOVE. We will return a 424 response with a
+ ** DAV:multistatus body. The multistatus responses will contain the
+ ** information about any resource that fails the validation.
+ **
+ ** We check the parent resource, too, since this is a MOVE. Moving the
+ ** resource effectively removes it from the parent collection, so we
+ ** must ensure that we have met the appropriate conditions.
+ **
+ ** If a problem occurs with the Request-URI itself, then a plain error
+ ** (rather than a multistatus) will be returned.
+ */
+ if (is_move
+ && (err = dav_validate_request(r, resource, depth, NULL,
+ &multi_response,
+ DAV_VALIDATE_PARENT
+ | DAV_VALIDATE_USE_424,
+ NULL)) != NULL) {
+ err = dav_push_error(r->pool, err->status, 0,
+ ap_psprintf(r->pool,
+ "Could not MOVE %s due to a failed "
+ "precondition on the source "
+ "(e.g. locks).",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ return dav_handle_err(r, err, multi_response);
+ }
+
+ /*
+ ** Check If-Headers and existing locks for destination. Note that we
+ ** use depth==infinity since the target (hierarchy) will be deleted
+ ** before the move/copy is completed.
+ **
+ ** Note that we are overwriting the target, which implies a DELETE, so
+ ** we are subject to the error/response rules as a DELETE. Namely, we
+ ** will return a 424 error if any of the validations fail.
+ ** (see dav_method_delete() for more information)
+ */
+ if ((err = dav_validate_request(lookup.rnew, resnew, DAV_INFINITY, NULL,
+ &multi_response,
+ DAV_VALIDATE_PARENT
+ | DAV_VALIDATE_USE_424, NULL)) != NULL) {
+ err = dav_push_error(r->pool, err->status, 0,
+ ap_psprintf(r->pool,
+ "Could not MOVE/COPY %s due to a "
+ "failed precondition on the "
+ "destination (e.g. locks).",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ return dav_handle_err(r, err, multi_response);
+ }
+
+ if (is_dir
+ && depth == DAV_INFINITY
+ && (*resource->hooks->is_parent_resource)(resource, resnew)) {
+ /* Supply some text for the error response body. */
+ return dav_error_response(r, HTTP_FORBIDDEN,
+ "Source collection contains the "
+ "Destination.");
+
+ }
+ if (is_dir
+ && (*resnew->hooks->is_parent_resource)(resnew, resource)) {
+ /* The destination must exist (since it contains the source), and
+ * a condition above implies Overwrite==T. Obviously, we cannot
+ * delete the Destination before the MOVE/COPY, as that would
+ * delete the Source.
+ */
+
+ /* Supply some text for the error response body. */
+ return dav_error_response(r, HTTP_FORBIDDEN,
+ "Destination collection contains the Source "
+ "and Overwrite has been specified.");
+ }
+
+ /* ### for now, we don't need anything in the body */
+ if ((result = ap_discard_request_body(r)) != OK) {
+ return result;
+ }
+
+ if ((err = dav_open_lockdb(r, 0, &lockdb)) != NULL) {
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* remove any locks from the old resources */
+ /*
+ ** ### this is Yet Another Traversal. if we do a rename(), then we
+ ** ### really don't have to do this in some cases since the inode
+ ** ### values will remain constant across the move. but we can't
+ ** ### know that fact from outside the provider :-(
+ **
+ ** ### note that we now have a problem atomicity in the move/copy
+ ** ### since a failure after this would have removed locks (technically,
+ ** ### this is okay to do, but really...)
+ */
+ if (is_move && lockdb != NULL) {
+ /* ### this is wrong! it blasts direct locks on parent resources */
+ /* ### pass lockdb! */
+ (void)dav_unlock(r, resource, NULL);
+ }
+
+ /* remember whether target resource existed */
+ replaced = resnew->exists;
+
+ /* if this is a move, then the source parent collection will be modified */
+ if (is_move) {
+ if ((err = dav_ensure_resource_writable(r, resource,
+ 1 /* parent_only */,
+ &resource_parent,
+ NULL, NULL,
+ &src_parent_was_writable)) != NULL) {
+ if (lockdb != NULL)
+ (*lockdb->hooks->close_lockdb)(lockdb);
+
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, NULL);
+ }
+ }
+
+ /* prepare the destination collection for modification */
+ if ((err = dav_ensure_resource_writable(r, resnew, 1 /* parent_only */,
+ &resnew_parent,
+ NULL, NULL,
+ &dst_parent_was_writable)) != NULL) {
+ /* could not make destination writable:
+ * if move, restore state of source parent
+ */
+ if (is_move) {
+ (void) dav_revert_resource_writability(r, NULL, resource_parent,
+ 1 /* undo */,
+ 0, 0,
+ src_parent_was_writable);
+ }
+
+ if (lockdb != NULL)
+ (*lockdb->hooks->close_lockdb)(lockdb);
+
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* If source and destination parents are the same, then
+ * use the same object, so status updates to one are reflected
+ * in the other.
+ */
+ if (resource_parent != NULL
+ && (*resource_parent->hooks->is_same_resource)(resource_parent,
+ resnew_parent))
+ resnew_parent = resource_parent;
+
+ /* New resource will be same kind as source */
+ resnew->collection = resource->collection;
+
+ /* If target exists, remove it first (we know Ovewrite must be TRUE).
+ * Then try to copy/move the resource.
+ */
+ if (resnew->exists)
+ err = (*resnew->hooks->remove_resource)(resnew, &multi_response);
+
+ if (err == NULL) {
+ if (is_move)
+ err = (*resource->hooks->move_resource)(resource, resnew,
+ &multi_response);
+ else
+ err = (*resource->hooks->copy_resource)(resource, resnew, depth,
+ &multi_response);
+ }
+
+ /* restore parent collection states */
+ err2 = dav_revert_resource_writability(r, NULL, resnew_parent,
+ err != NULL /* undo if error */,
+ 0, 0, dst_parent_was_writable);
+
+ if (is_move) {
+ err3 = dav_revert_resource_writability(r, NULL, resource_parent,
+ err != NULL /* undo if error */,
+ 0, 0, src_parent_was_writable);
+ }
+ else
+ err3 = NULL;
+
+ /* check for error from remove/copy/move operations */
+ if (err != NULL) {
+ if (lockdb != NULL)
+ (*lockdb->hooks->close_lockdb)(lockdb);
+
+ err = dav_push_error(r->pool, err->status, 0,
+ ap_psprintf(r->pool,
+ "Could not MOVE/COPY %s.",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ return dav_handle_err(r, err, multi_response);
+ }
+
+ /* check for errors from reverting writability */
+ if (err2 != NULL) {
+ /* just log a warning */
+ err = dav_push_error(r->pool, err2->status, 0,
+ "The MOVE/COPY was successful, but there was a "
+ "problem reverting the writability of the "
+ "source parent collection.",
+ err2);
+ dav_log_err(r, err, APLOG_WARNING);
+ }
+ if (err3 != NULL) {
+ /* just log a warning */
+ err = dav_push_error(r->pool, err3->status, 0,
+ "The MOVE/COPY was successful, but there was a "
+ "problem reverting the writability of the "
+ "destination parent collection.",
+ err3);
+ dav_log_err(r, err, APLOG_WARNING);
+ }
+
+ /* propagate any indirect locks at the target */
+ if (lockdb != NULL) {
+ int resource_state = dav_get_resource_state(lookup.rnew, resnew);
+
+ /* notify lock system that we have created/replaced a resource */
+ err = dav_notify_created(r, lockdb, resnew, resource_state, depth);
+
+ (*lockdb->hooks->close_lockdb)(lockdb);
+
+ if (err != NULL) {
+ /* The move/copy was successful, but the locking failed. */
+ err = dav_push_error(r->pool, err->status, 0,
+ "The MOVE/COPY was successful, but there "
+ "was a problem updating the lock "
+ "information.",
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+ }
+
+ /* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */
+ return dav_created(r, lookup.rnew, resnew, "Destination", replaced);
+}
+
+/* dav_method_lock: Handler to implement the DAV LOCK method
+** Returns appropriate HTTP_* response.
+*/
+static int dav_method_lock(request_rec *r)
+{
+ dav_error *err;
+ dav_resource *resource;
+ const dav_hooks_locks *locks_hooks;
+ int result;
+ int depth;
+ int new_lock_request = 0;
+ dav_xml_doc *doc = NULL;
+ dav_lock *lock;
+ dav_response *multi_response = NULL;
+ dav_lockdb *lockdb;
+ int resource_state;
+
+ /* If no locks provider, decline the request */
+ locks_hooks = DAV_GET_HOOKS_LOCKS(r);
+ if (locks_hooks == NULL)
+ return DECLINED;
+
+ if ((result = dav_parse_input(r, &doc)) != OK)
+ return result;
+
+ depth = dav_get_depth(r, DAV_INFINITY);
+ if (depth != 0 && depth != DAV_INFINITY) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "Depth must be 0 or \"infinity\" for LOCK.");
+ return HTTP_BAD_REQUEST;
+ }
+
+ /* Ask repository module to resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK)
+ return result;
+
+ /*
+ ** Open writable. Unless an error occurs, we'll be
+ ** writing into the database.
+ */
+ if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) {
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, NULL);
+ }
+
+ if (doc != NULL) {
+ if ((err = dav_lock_parse_lockinfo(r, resource, lockdb, doc,
+ &lock)) != NULL) {
+ /* ### add a higher-level description to err? */
+ goto error;
+ }
+ new_lock_request = 1;
+
+ lock->auth_user = ap_pstrdup(r->pool, r->connection->user);
+ }
+
+ resource_state = dav_get_resource_state(r, resource);
+
+ /*
+ ** Check If-Headers and existing locks.
+ **
+ ** If this will create a locknull resource, then the LOCK will affect
+ ** the parent collection (much like a PUT/MKCOL). For that case, we must
+ ** validate the parent resource's conditions.
+ */
+ if ((err = dav_validate_request(r, resource, depth, NULL, &multi_response,
+ (resource_state == DAV_RESOURCE_NULL
+ ? DAV_VALIDATE_PARENT
+ : DAV_VALIDATE_RESOURCE)
+ | (new_lock_request ? lock->scope : 0)
+ | DAV_VALIDATE_ADD_LD,
+ lockdb)) != OK) {
+ err = dav_push_error(r->pool, err->status, 0,
+ ap_psprintf(r->pool,
+ "Could not LOCK %s due to a failed "
+ "precondition (e.g. other locks).",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ goto error;
+ }
+
+ if (new_lock_request == 0) {
+ dav_locktoken_list *ltl;
+
+ /*
+ ** Refresh request
+ ** ### Assumption: We can renew multiple locks on the same resource
+ ** ### at once. First harvest all the positive lock-tokens given in
+ ** ### the If header. Then modify the lock entries for this resource
+ ** ### with the new Timeout val.
+ */
+
+ if ((err = dav_get_locktoken_list(r, &ltl)) != NULL) {
+ err = dav_push_error(r->pool, err->status, 0,
+ ap_psprintf(r->pool,
+ "The lock refresh for %s failed "
+ "because no lock tokens were "
+ "specified in an \"If:\" "
+ "header.",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ goto error;
+ }
+
+ if ((err = (*locks_hooks->refresh_locks)(lockdb, resource, ltl,
+ dav_get_timeout(r),
+ &lock)) != NULL) {
+ /* ### add a higher-level description to err? */
+ goto error;
+ }
+ } else {
+ /* New lock request */
+ char *locktoken_txt;
+ dav_dir_conf *conf;
+
+ conf = (dav_dir_conf *) ap_get_module_config(r->per_dir_config,
+ &dav_module);
+
+ /* apply lower bound (if any) from DAVMinTimeout directive */
+ if (lock->timeout != DAV_TIMEOUT_INFINITE
+ && lock->timeout < time(NULL) + conf->locktimeout)
+ lock->timeout = time(NULL) + conf->locktimeout;
+
+ err = dav_add_lock(r, resource, lockdb, lock, &multi_response);
+ if (err != NULL) {
+ /* ### add a higher-level description to err? */
+ goto error;
+ }
+
+ locktoken_txt = ap_pstrcat(r->pool, "<",
+ (*locks_hooks->format_locktoken)(r->pool, lock->locktoken),
+ ">", NULL);
+
+ ap_table_set(r->headers_out, "Lock-Token", locktoken_txt);
+ }
+
+ (*locks_hooks->close_lockdb)(lockdb);
+
+ r->status = HTTP_OK;
+ r->content_type = DAV_XML_CONTENT_TYPE;
+
+ ap_send_http_header(r);
+ ap_soft_timeout("send LOCK response", r);
+
+ ap_rputs(DAV_XML_HEADER DEBUG_CR "<D:prop xmlns:D=\"DAV:\">" DEBUG_CR, r);
+ if (lock == NULL)
+ ap_rputs("<D:lockdiscovery/>" DEBUG_CR, r);
+ else {
+ ap_rprintf(r,
+ "<D:lockdiscovery>" DEBUG_CR
+ "%s" DEBUG_CR
+ "</D:lockdiscovery>" DEBUG_CR,
+ dav_lock_get_activelock(r, lock, NULL));
+ }
+ ap_rputs("</D:prop>", r);
+
+ ap_kill_timeout(r);
+
+ /* the response has been sent. */
+ return DONE;
+
+ error:
+ (*locks_hooks->close_lockdb)(lockdb);
+ return dav_handle_err(r, err, multi_response);
+}
+
+/* dav_method_unlock: Handler to implement the DAV UNLOCK method
+ * Returns appropriate HTTP_* response.
+ */
+static int dav_method_unlock(request_rec *r)
+{
+ dav_error *err;
+ dav_resource *resource;
+ const dav_hooks_locks *locks_hooks;
+ int result;
+ const char *const_locktoken_txt;
+ char *locktoken_txt;
+ dav_locktoken *locktoken = NULL;
+ int resource_state;
+ dav_response *multi_response;
+
+ /* If no locks provider, decline the request */
+ locks_hooks = DAV_GET_HOOKS_LOCKS(r);
+ if (locks_hooks == NULL)
+ return DECLINED;
+
+ if ((const_locktoken_txt = ap_table_get(r->headers_in, "Lock-Token")) == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "Unlock failed (%s): No Lock-Token specified in header", r->filename);
+ return HTTP_BAD_REQUEST;
+ }
+
+ locktoken_txt = ap_pstrdup(r->pool, const_locktoken_txt);
+ if (locktoken_txt[0] != '<') {
+ /* ### should provide more specifics... */
+ return HTTP_BAD_REQUEST;
+ }
+ locktoken_txt++;
+
+ if (locktoken_txt[strlen(locktoken_txt) - 1] != '>') {
+ /* ### should provide more specifics... */
+ return HTTP_BAD_REQUEST;
+ }
+ locktoken_txt[strlen(locktoken_txt) - 1] = '\0';
+
+ if ((err = (*locks_hooks->parse_locktoken)(r->pool, locktoken_txt,
+ &locktoken)) != NULL) {
+ err = dav_push_error(r->pool, HTTP_BAD_REQUEST, 0,
+ ap_psprintf(r->pool,
+ "The UNLOCK on %s failed -- an "
+ "invalid lock token was specified "
+ "in the \"If:\" header.",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* Ask repository module to resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK)
+ return result;
+
+ resource_state = dav_get_resource_state(r, resource);
+
+ /*
+ ** Check If-Headers and existing locks.
+ **
+ ** Note: depth == 0 normally requires no multistatus response. However,
+ ** if we pass DAV_VALIDATE_PARENT, then we could get an error on a URI
+ ** other than the Request-URI, thereby requiring a multistatus.
+ **
+ ** If the resource is a locknull resource, then the UNLOCK will affect
+ ** the parent collection (much like a delete). For that case, we must
+ ** validate the parent resource's conditions.
+ */
+ if ((err = dav_validate_request(r, resource, 0, locktoken,
+ &multi_response,
+ resource_state == DAV_RESOURCE_LOCK_NULL
+ ? DAV_VALIDATE_PARENT
+ : DAV_VALIDATE_RESOURCE, NULL)) != NULL) {
+ /* ### add a higher-level description? */
+ return dav_handle_err(r, err, multi_response);
+ }
+
+ /* ### RFC 2518 s. 8.11: If this resource is locked by locktoken,
+ * _all_ resources locked by locktoken are released. It does not say
+ * resource has to be the root of an infinte lock. Thus, an UNLOCK
+ * on any part of an infinte lock will remove the lock on all resources.
+ *
+ * For us, if r->filename represents an indirect lock (part of an infinity lock),
+ * we must actually perform an UNLOCK on the direct lock for this resource.
+ */
+ if ((result = dav_unlock(r, resource, locktoken)) != OK) {
+ return result;
+ }
+
+ return HTTP_NO_CONTENT;
+}
+
+/* handle the SEARCH method from DASL */
+static int dav_method_search(request_rec *r)
+{
+ /* ### we know this method, but we won't allow it yet */
+ /* Apache will supply a default error for this. */
+ return HTTP_METHOD_NOT_ALLOWED;
+
+ /* Do some error checking, like if the querygrammar is
+ * supported by the content type, and then pass the
+ * request on to the appropriate query module.
+ */
+}
+
+/* handle the CHECKOUT method */
+static int dav_method_checkout(request_rec *r)
+{
+ dav_resource *resource;
+ const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
+ dav_error *err;
+ int result;
+
+ /* If no versioning provider, decline the request */
+ if (vsn_hooks == NULL)
+ return DECLINED;
+
+ /* ### eventually check body for DAV:checkin-policy */
+ if ((result = ap_discard_request_body(r)) != OK) {
+ return result;
+ }
+
+ /* Ask repository module to resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK)
+ return result;
+ if (!resource->exists) {
+ /* Apache will supply a default error for this. */
+ return HTTP_NOT_FOUND;
+ }
+
+ /* Check the state of the resource: must be a file or collection,
+ * must be versioned, and must not already be checked out.
+ */
+ if (resource->type != DAV_RESOURCE_TYPE_REGULAR) {
+ return dav_error_response(r, HTTP_CONFLICT,
+ "Cannot checkout this type of resource.");
+ }
+
+ if (!resource->versioned) {
+ return dav_error_response(r, HTTP_CONFLICT,
+ "Cannot checkout unversioned resource.");
+ }
+
+ if (resource->working) {
+ return dav_error_response(r, HTTP_CONFLICT,
+ "The resource is already checked out to the workspace.");
+ }
+
+ /* ### do lock checks, once behavior is defined */
+
+ /* Do the checkout */
+ if ((err = (*vsn_hooks->checkout)(resource)) != NULL) {
+ err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
+ ap_psprintf(r->pool,
+ "Could not CHECKOUT resource %s.",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* no body */
+ ap_set_content_length(r, 0);
+ ap_send_http_header(r);
+
+ return DONE;
+}
+
+/* handle the UNCHECKOUT method */
+static int dav_method_uncheckout(request_rec *r)
+{
+ dav_resource *resource;
+ const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
+ dav_error *err;
+ int result;
+
+ /* If no versioning provider, decline the request */
+ if (vsn_hooks == NULL)
+ return DECLINED;
+
+ if ((result = ap_discard_request_body(r)) != OK) {
+ return result;
+ }
+
+ /* Ask repository module to resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK)
+ return result;
+ if (!resource->exists) {
+ /* Apache will supply a default error for this. */
+ return HTTP_NOT_FOUND;
+ }
+
+ /* Check the state of the resource: must be a file or collection,
+ * must be versioned, and must be checked out.
+ */
+ if (resource->type != DAV_RESOURCE_TYPE_REGULAR) {
+ return dav_error_response(r, HTTP_CONFLICT,
+ "Cannot uncheckout this type of resource.");
+ }
+
+ if (!resource->versioned) {
+ return dav_error_response(r, HTTP_CONFLICT,
+ "Cannot uncheckout unversioned resource.");
+ }
+
+ if (!resource->working) {
+ return dav_error_response(r, HTTP_CONFLICT,
+ "The resource is not checked out to the workspace.");
+ }
+
+ /* ### do lock checks, once behavior is defined */
+
+ /* Do the uncheckout */
+ if ((err = (*vsn_hooks->uncheckout)(resource)) != NULL) {
+ err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
+ ap_psprintf(r->pool,
+ "Could not UNCHECKOUT resource %s.",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* no body */
+ ap_set_content_length(r, 0);
+ ap_send_http_header(r);
+
+ return DONE;
+}
+
+/* handle the CHECKIN method */
+static int dav_method_checkin(request_rec *r)
+{
+ dav_resource *resource;
+ const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
+ dav_error *err;
+ int result;
+
+ /* If no versioning provider, decline the request */
+ if (vsn_hooks == NULL)
+ return DECLINED;
+
+ if ((result = ap_discard_request_body(r)) != OK) {
+ return result;
+ }
+
+ /* Ask repository module to resolve the resource */
+ result = dav_get_resource(r, &resource);
+ if (result != OK)
+ return result;
+ if (!resource->exists) {
+ /* Apache will supply a default error for this. */
+ return HTTP_NOT_FOUND;
+ }
+
+ /* Check the state of the resource: must be a file or collection,
+ * must be versioned, and must be checked out.
+ */
+ if (resource->type != DAV_RESOURCE_TYPE_REGULAR) {
+ return dav_error_response(r, HTTP_CONFLICT,
+ "Cannot checkin this type of resource.");
+ }
+
+ if (!resource->versioned) {
+ return dav_error_response(r, HTTP_CONFLICT,
+ "Cannot checkin unversioned resource.");
+ }
+
+ if (!resource->working) {
+ return dav_error_response(r, HTTP_CONFLICT,
+ "The resource is not checked out to the workspace.");
+ }
+
+ /* ### do lock checks, once behavior is defined */
+
+ /* Do the checkin */
+ if ((err = (*vsn_hooks->checkin)(resource)) != NULL) {
+ err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
+ ap_psprintf(r->pool,
+ "Could not CHECKIN resource %s.",
+ ap_escape_html(r->pool, r->uri)),
+ err);
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* no body */
+ ap_set_content_length(r, 0);
+ ap_send_http_header(r);
+
+ return DONE;
+}
+
+
+/*
+ * Response handler for DAV resources
+ */
+static int dav_handler(request_rec *r)
+{
+ dav_dir_conf *conf;
+
+ /* quickly ignore any HTTP/0.9 requests */
+ if (r->assbackwards) {
+ return DECLINED;
+ }
+
+ /* ### do we need to do anything with r->proxyreq ?? */
+
+ conf = (dav_dir_conf *) ap_get_module_config(r->per_dir_config,
+ &dav_module);
+
+ /*
+ * Set up the methods mask, since that's one of the reasons this handler
+ * gets called, and lower-level things may need the info.
+ *
+ * First, set the mask to the methods we handle directly. Since by
+ * definition we own our managed space, we unconditionally set
+ * the r->allowed field rather than ORing our values with anything
+ * any other module may have put in there.
+ *
+ * These are the HTTP-defined methods that we handle directly.
+ */
+ r->allowed = 0
+ | (1 << M_GET)
+ | (1 << M_PUT)
+ | (1 << M_DELETE)
+ | (1 << M_OPTIONS)
+ | (1 << M_INVALID);
+ /*
+ * These are the DAV methods we handle.
+ */
+ r->allowed |= 0
+ | (1 << M_COPY)
+ | (1 << M_LOCK)
+ | (1 << M_UNLOCK)
+ | (1 << M_MKCOL)
+ | (1 << M_MOVE)
+ | (1 << M_PROPFIND)
+ | (1 << M_PROPPATCH);
+ /*
+ * These are methods that we don't handle directly, but let the
+ * server's default handler do for us as our agent.
+ */
+ r->allowed |= 0
+ | (1 << M_POST);
+
+ /* ### hrm. if we return HTTP_METHOD_NOT_ALLOWED, then an Allow header
+ * ### is sent; it will need the other allowed states; since the default
+ * ### handler is not called on error, then it doesn't add the other
+ * ### allowed states, so we must */
+ /* ### we might need to refine this for just where we return the error.
+ * ### also, there is the issue with other methods (see ISSUES) */
+ /* ### more work necessary, now that we have M_foo for DAV methods */
+
+ /* dispatch the appropriate method handler */
+ if (r->method_number == M_GET) {
+ return dav_method_get(r);
+ }
+
+ if (r->method_number == M_PUT) {
+ return dav_method_put(r);
+ }
+
+ if (r->method_number == M_POST) {
+ return dav_method_post(r);
+ }
+
+ if (r->method_number == M_DELETE) {
+ return dav_method_delete(r);
+ }
+
+ if (r->method_number == M_OPTIONS) {
+ return dav_method_options(r);
+ }
+
+ if (r->method_number == M_PROPFIND) {
+ return dav_method_propfind(r);
+ }
+
+ if (r->method_number == M_PROPPATCH) {
+ return dav_method_proppatch(r);
+ }
+
+ if (r->method_number == M_MKCOL) {
+ return dav_method_mkcol(r);
+ }
+
+ if (r->method_number == M_COPY) {
+ return dav_method_copymove(r, DAV_DO_COPY);
+ }
+
+ if (r->method_number == M_MOVE) {
+ return dav_method_copymove(r, DAV_DO_MOVE);
+ }
+
+ if (r->method_number == M_LOCK) {
+ return dav_method_lock(r);
+ }
+
+ if (r->method_number == M_UNLOCK) {
+ return dav_method_unlock(r);
+ }
+
+ /*
+ * NOTE: When Apache moves creates defines for the add'l DAV methods,
+ * then it will no longer use M_INVALID. This code must be
+ * updated each time Apache adds method defines.
+ */
+ if (r->method_number != M_INVALID) {
+ return DECLINED;
+ }
+
+ if (!strcmp(r->method, "SEARCH")) {
+ return dav_method_search(r);
+ }
+
+ if (!strcmp(r->method, "CHECKOUT")) {
+ return dav_method_checkout(r);
+ }
+
+ if (!strcmp(r->method, "UNCHECKOUT")) {
+ return dav_method_uncheckout(r);
+ }
+
+ if (!strcmp(r->method, "CHECKIN")) {
+ return dav_method_checkin(r);
+ }
+
+#if 0
+ if (!strcmp(r->method, "MKRESOURCE")) {
+ return dav_method_mkresource(r);
+ }
+
+ if (!strcmp(r->method, "REPORT")) {
+ return dav_method_report(r);
+ }
+#endif
+
+ /* ### add'l methods for Advanced Collections, ACLs, DASL */
+
+ return DECLINED;
+}
+
+static int dav_type_checker(request_rec *r)
+{
+ dav_dir_conf *conf;
+
+ conf = (dav_dir_conf *) ap_get_module_config(r->per_dir_config,
+ &dav_module);
+
+ /* if DAV is not enabled, then we've got nothing to do */
+ if (conf->enabled != DAV_ENABLED_ON) {
+ return DECLINED;
+ }
+
+ if (r->method_number == M_GET) {
+ /*
+ ** ### need some work to pull Content-Type and Content-Language
+ ** ### from the property database.
+ */
+
+ /*
+ ** If the repository hasn't indicated that it will handle the
+ ** GET method, then just punt.
+ **
+ ** ### this isn't quite right... taking over the response can break
+ ** ### things like mod_negotiation. need to look into this some more.
+ */
+ if (!conf->handle_get)
+ return DECLINED;
+ }
+
+ /* ### we should (instead) trap the ones that we DO understand */
+ /* ### the handler DOES handle POST, so we need to fix one of these */
+ if (r->method_number != M_POST) {
+
+ /*
+ ** ### anything else to do here? could another module and/or
+ ** ### config option "take over" the handler here? i.e. how do
+ ** ### we lock down this hierarchy so that we are the ultimate
+ ** ### arbiter? (or do we simply depend on the administrator
+ ** ### to avoid conflicting configurations?)
+ **
+ ** ### I think the OK stops running type-checkers. need to look.
+ */
+ r->handler = "dav-handler";
+ return OK;
+ }
+
+ return DECLINED;
+}
+
+
+/*---------------------------------------------------------------------------
+**
+** Configuration info for the module
+*/
+
+static const command_rec dav_cmds[] =
+{
+ {
+ "DAV",
+ dav_cmd_dav,
+ NULL,
+ ACCESS_CONF, /* per directory/location */
+ FLAG,
+ "turn DAV on/off for a directory or location"
+ },
+ {
+ "DAVLockDB",
+ dav_cmd_davlockdb,
+ NULL,
+ RSRC_CONF, /* per server */
+ TAKE1,
+ "specify a lock database"
+ },
+ {
+ "DAVMinTimeout",
+ dav_cmd_davmintimeout,
+ NULL,
+ ACCESS_CONF|RSRC_CONF, /* per directory/location, or per server */
+ TAKE1,
+ "specify minimum allowed timeout"
+ },
+ {
+ "DAVDepthInfinity",
+ dav_cmd_davdepthinfinity,
+ NULL,
+ ACCESS_CONF|RSRC_CONF, /* per directory/location, or per server */
+ FLAG,
+ "allow Depth infinity PROPFIND requests"
+ },
+ {
+ "DAVParam",
+ dav_cmd_davparam,
+ NULL,
+ ACCESS_CONF|RSRC_CONF, /* per directory/location, or per server */
+ TAKE2,
+ "DAVParam <parameter name> <parameter value>"
+ },
+ {
+ "LimitXMLRequestBody",
+ dav_cmd_limitxmlrequestbody,
+ NULL,
+ ACCESS_CONF|RSRC_CONF, /* per directory/location, or per server */
+ TAKE1,
+ "Limit (in bytes) on maximum size of an XML-based request body"
+ },
+ { NULL }
+};
+
+static const handler_rec dav_handlers[] =
+{
+ {"dav-handler", dav_handler},
+ { NULL }
+};
+
+module MODULE_VAR_EXPORT dav_module =
+{
+ STANDARD_MODULE_STUFF,
+ dav_init_handler, /* initializer */
+ dav_create_dir_config, /* dir config creater */
+ dav_merge_dir_config, /* dir merger --- default is to override */
+ dav_create_server_config, /* server config */
+ dav_merge_server_config, /* merge server config */
+ dav_cmds, /* command table */
+ dav_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ dav_type_checker, /* type_checker */
+ NULL, /* fixups */
+ NULL, /* logger */
+ NULL, /* header parser */
+ NULL, /* child_init */
+ NULL, /* child_exit */
+ NULL /* post read-request */
+};
diff --git a/modules/dav/main/mod_dav.dsp b/modules/dav/main/mod_dav.dsp
new file mode 100644
index 0000000000..e103a8d5dd
--- /dev/null
+++ b/modules/dav/main/mod_dav.dsp
@@ -0,0 +1,128 @@
+# Microsoft Developer Studio Project File - Name="mod_dav" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_dav - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "mod_dav.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "mod_dav.mak" CFG="mod_dav - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_dav - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_dav - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "mod_dav - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\ApacheMo"
+# PROP BASE Intermediate_Dir ".\ApacheMo"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\mod_davR"
+# PROP Intermediate_Dir ".\mod_davR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\modules\dav\main" /I "..\..\lib\sdbm" /I "..\..\lib\expat-lite" /I "..\..\lib\apr\include" /I "..\..\include" /I "..\..\os\win32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /D "DAV_EXPORT_SYMBOLS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 ApacheCore.lib aprlib.lib kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /map /machine:I386 /libpath:"..\..\CoreR" /libpath:"..\..\lib\apr\Release" /base:@BaseAddr.ref,mod_dav
+# ADD LINK32 ApacheCore.lib aprlib.lib kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /map /machine:I386 /libpath:"..\..\CoreR" /libpath:"..\..\lib\apr\Release" /base:@BaseAddr.ref,mod_dav
+
+!ELSEIF "$(CFG)" == "mod_dav - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\ApacheM0"
+# PROP BASE Intermediate_Dir ".\ApacheM0"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\mod_davD"
+# PROP Intermediate_Dir ".\mod_davD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\modules\dav\main" /I "..\..\lib\sdbm" /I "..\..\lib\expat-lite" /I "..\..\lib\apr\include" /I "..\..\include" /I "..\..\os\win32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /D "DAV_EXPORT_SYMBOLS" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 ApacheCore.lib aprlib.lib kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /map /debug /machine:I386 /libpath:"..\..\CoreD" /libpath:"..\..\lib\apr\Debug" /base:@BaseAddr.ref,mod_dav
+# ADD LINK32 ApacheCore.lib aprlib.lib kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /debug /machine:I386 /libpath:"..\..\CoreD" /libpath:"..\..\lib\apr\Debug" /base:@BaseAddr.ref,mod_dav
+# SUBTRACT LINK32 /incremental:no /map
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_dav - Win32 Release"
+# Name "mod_dav - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\dav\main\liveprop.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\modules\dav\main\mod_dav.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\modules\dav\main\props.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\modules\dav\main\providers.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\modules\dav\main\util.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\modules\dav\main\util_lock.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=..\..\modules\dav\main\mod_dav.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/modules/dav/main/mod_dav.h b/modules/dav/main/mod_dav.h
new file mode 100644
index 0000000000..df38d4fbbb
--- /dev/null
+++ b/modules/dav/main/mod_dav.h
@@ -0,0 +1,1809 @@
+/*
+** Copyright (C) 1998-2000 Greg Stein. All Rights Reserved.
+**
+** By using this file, you agree to the terms and conditions set forth in
+** the LICENSE.html file which can be found at the top level of the mod_dav
+** distribution or at http://www.webdav.org/mod_dav/license-1.html.
+**
+** Contact information:
+** Greg Stein, PO Box 760, Palo Alto, CA, 94302
+** gstein@lyra.org, http://www.webdav.org/mod_dav/
+*/
+
+/*
+** DAV extension module for Apache 1.3.*
+**
+** Written by Greg Stein, gstein@lyra.org, http://www.lyra.org/
+*/
+
+#ifndef _MOD_DAV_H_
+#define _MOD_DAV_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "httpd.h"
+
+
+#define DAV_VERSION "1.0.1"
+
+#define DAV_XML_HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+#define DAV_XML_CONTENT_TYPE "text/xml; charset=\"utf-8\""
+
+#define DAV_READ_BLOCKSIZE 2048 /* used for reading input blocks */
+
+#ifdef WIN32
+#include <limits.h>
+typedef int ssize_t;
+#endif /* WIN32 */
+
+#define DAV_RESPONSE_BODY_1 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<HTML><HEAD>\n<TITLE>"
+#define DAV_RESPONSE_BODY_2 "</TITLE>\n</HEAD><BODY>\n<H1>"
+#define DAV_RESPONSE_BODY_3 "</H1>\n"
+#define DAV_RESPONSE_BODY_4 "</BODY></HTML>\n"
+
+#define DAV_DO_COPY 0
+#define DAV_DO_MOVE 1
+
+
+#if 1
+#define DAV_DEBUG 1
+#define DEBUG_CR "\n"
+#define DBG0(f) ap_log_error(APLOG_MARK, \
+ APLOG_ERR|APLOG_NOERRNO, NULL, (f))
+#define DBG1(f,a1) ap_log_error(APLOG_MARK, \
+ APLOG_ERR|APLOG_NOERRNO, NULL, f, a1)
+#define DBG2(f,a1,a2) ap_log_error(APLOG_MARK, \
+ APLOG_ERR|APLOG_NOERRNO, NULL, f, a1, a2)
+#define DBG3(f,a1,a2,a3) ap_log_error(APLOG_MARK, \
+ APLOG_ERR|APLOG_NOERRNO, NULL, f, a1, a2, a3)
+#else
+#undef DAV_DEBUG
+#define DEBUG_CR ""
+#endif
+
+#define DAV_INFINITY INT_MAX /* for the Depth: header */
+
+
+/* --------------------------------------------------------------------
+**
+** ERROR MANAGEMENT
+*/
+
+/*
+** dav_error structure.
+**
+** In most cases, mod_dav uses a pointer to a dav_error structure. If the
+** pointer is NULL, then no error has occurred.
+**
+** In certain cases, a dav_error structure is directly used. In these cases,
+** a status value of 0 means that an error has not occurred.
+**
+** Note: this implies that status != 0 whenever an error occurs.
+**
+** The desc field is optional (it may be NULL). When NULL, it typically
+** implies that Apache has a proper description for the specified status.
+*/
+typedef struct dav_error {
+ int status; /* suggested HTTP status (0 for no error) */
+ int error_id; /* DAV-specific error ID */
+ const char *desc; /* DAV:responsedescription and error log */
+
+ int save_errno; /* copy of errno causing the error */
+
+ struct dav_error *prev; /* previous error (in stack) */
+
+ /* deferred computation of the description */
+ void (*compute_desc)(struct dav_error *err, pool *p);
+ int ctx_i;
+ const char *ctx_s;
+ void *ctx_p;
+
+} dav_error;
+
+/*
+** Create a new error structure. save_errno will be filled with the current
+** errno value.
+*/
+dav_error *dav_new_error(pool *p, int status, int error_id, const char *desc);
+
+/*
+** Push a new error description onto the stack of errors.
+**
+** This function is used to provide an additional description to an existing
+** error.
+**
+** <status> should contain the caller's view of what the current status is,
+** given the underlying error. If it doesn't have a better idea, then the
+** caller should pass prev->status.
+**
+** <error_id> can specify a new error_id since the topmost description has
+** changed.
+*/
+dav_error *dav_push_error(pool *p, int status, int error_id, const char *desc,
+ dav_error *prev);
+
+
+/* error ID values... */
+
+/* IF: header errors */
+#define DAV_ERR_IF_PARSE 100 /* general parsing error */
+#define DAV_ERR_IF_MULTIPLE_NOT 101 /* multiple "Not" found */
+#define DAV_ERR_IF_UNK_CHAR 102 /* unknown char in header */
+#define DAV_ERR_IF_ABSENT 103 /* no locktokens given */
+#define DAV_ERR_IF_TAGGED 104 /* in parsing tagged-list */
+#define DAV_ERR_IF_UNCLOSED_PAREN 105 /* in no-tagged-list */
+
+/* Prop DB errors */
+#define DAV_ERR_PROP_BAD_MAJOR 200 /* major version was wrong */
+#define DAV_ERR_PROP_READONLY 201 /* prop is read-only */
+#define DAV_ERR_PROP_NO_DATABASE 202 /* writeable db not avail */
+#define DAV_ERR_PROP_NOT_FOUND 203 /* prop not found */
+#define DAV_ERR_PROP_BAD_LOCKDB 204 /* could not open lockdb */
+#define DAV_ERR_PROP_OPENING 205 /* problem opening propdb */
+#define DAV_ERR_PROP_EXEC 206 /* problem exec'ing patch */
+
+/* Predefined DB errors */
+/* ### any to define?? */
+
+/* Predefined locking system errors */
+#define DAV_ERR_LOCK_OPENDB 400 /* could not open lockdb */
+#define DAV_ERR_LOCK_NO_DB 401 /* no database defined */
+#define DAV_ERR_LOCK_CORRUPT_DB 402 /* DB is corrupt */
+#define DAV_ERR_LOCK_UNK_STATE_TOKEN 403 /* unknown State-token */
+#define DAV_ERR_LOCK_PARSE_TOKEN 404 /* bad opaquelocktoken */
+#define DAV_ERR_LOCK_SAVE_LOCK 405 /* err saving locks */
+
+/*
+** Some comments on Error ID values:
+**
+** The numbers do not necessarily need to be unique. Uniqueness simply means
+** that two errors that have not been predefined above can be distinguished
+** from each other. At the moment, mod_dav does not use this distinguishing
+** feature, but it could be used in the future to collapse <response> elements
+** into groups based on the error ID (and associated responsedescription).
+**
+** If a compute_desc is provided, then the error ID should be unique within
+** the context of the compute_desc function (so the function can figure out
+** what to filled into the desc).
+**
+** Basically, subsystems can ignore defining new error ID values if they want
+** to. The subsystems *do* need to return the predefined errors when
+** appropriate, so that mod_dav can figure out what to do. Subsystems can
+** simply leave the error ID field unfilled (zero) if there isn't an error
+** that must be placed there.
+*/
+
+
+/* --------------------------------------------------------------------
+**
+** HOOK STRUCTURES
+**
+** These are here for forward-declaration purposes. For more info, see
+** the section title "HOOK HANDLING" for more information, plus each
+** structure definition.
+*/
+
+/* forward-declare this structure */
+typedef struct dav_hooks_db dav_hooks_db;
+typedef struct dav_hooks_locks dav_hooks_locks;
+typedef struct dav_hooks_vsn dav_hooks_vsn;
+typedef struct dav_hooks_repository dav_hooks_repository;
+typedef struct dav_hooks_liveprop dav_hooks_liveprop;
+
+
+/* --------------------------------------------------------------------
+**
+** RESOURCE HANDLING
+*/
+
+/*
+** Resource Types:
+** The base protocol defines only file and collection resources.
+** The versioning protocol defines several additional resource types
+** to represent artifacts of a version control system.
+*/
+typedef enum {
+ DAV_RESOURCE_TYPE_REGULAR, /* file or collection, working resource
+ or revision */
+ DAV_RESOURCE_TYPE_REVISION, /* explicit revision-id */
+ DAV_RESOURCE_TYPE_HISTORY, /* explicit history-id */
+ DAV_RESOURCE_TYPE_WORKSPACE, /* workspace */
+ DAV_RESOURCE_TYPE_ACTIVITY, /* activity */
+ DAV_RESOURCE_TYPE_CONFIGURATION /* configuration */
+} dav_resource_type;
+
+/*
+** Opaque, repository-specific information for a resource.
+*/
+typedef struct dav_resource_private dav_resource_private;
+
+/* Resource descriptor, generated by a repository provider.
+ * Note: the lock-null state is not explicitly represented here,
+ * since it may be expensive to compute. Use dav_get_resource_state()
+ * to determine whether a non-existent resource is a lock-null resource.
+ */
+typedef struct dav_resource {
+ dav_resource_type type;
+
+ int exists; /* 0 => null resource */
+ int collection; /* 0 => file (if type == DAV_RESOURCE_TYPE_REGULAR) */
+ int versioned; /* 0 => unversioned */
+ int working; /* 0 => revision (if versioned) */
+ int baselined; /* 0 => not baselined */
+
+ const char *uri; /* the URI for this resource */
+
+ dav_resource_private *info;
+
+ const dav_hooks_repository *hooks; /* hooks used for this resource */
+
+} dav_resource;
+
+/*
+** Lock token type. Lock providers define the details of a lock token.
+** However, all providers are expected to at least be able to parse
+** the "opaquelocktoken" scheme, which is represented by a uuid_t.
+*/
+typedef struct dav_locktoken dav_locktoken;
+
+
+/* --------------------------------------------------------------------
+**
+** BUFFER HANDLING
+**
+** These buffers are used as a lightweight buffer reuse mechanism. Apache
+** provides sub-pool creation and destruction to much the same effect, but
+** the sub-pools are a bit more general and heavyweight than these buffers.
+*/
+
+/* buffer for reuse; can grow to accomodate needed size */
+typedef struct
+{
+ size_t alloc_len; /* how much has been allocated */
+ size_t cur_len; /* how much is currently being used */
+ char *buf; /* buffer contents */
+} dav_buffer;
+#define DAV_BUFFER_MINSIZE 256 /* minimum size for buffer */
+#define DAV_BUFFER_PAD 64 /* amount of pad when growing */
+
+/* set the cur_len to the given size and ensure space is available */
+void dav_set_bufsize(pool *p, dav_buffer *pbuf, size_t size);
+
+/* initialize a buffer and copy the specified (null-term'd) string into it */
+void dav_buffer_init(pool *p, dav_buffer *pbuf, const char *str);
+
+/* check that the buffer can accomodate <extra_needed> more bytes */
+void dav_check_bufsize(pool *p, dav_buffer *pbuf, size_t extra_needed);
+
+/* append a string to the end of the buffer, adjust length */
+void dav_buffer_append(pool *p, dav_buffer *pbuf, const char *str);
+
+/* place a string on the end of the buffer, do NOT adjust length */
+void dav_buffer_place(pool *p, dav_buffer *pbuf, const char *str);
+
+/* place some memory on the end of a buffer; do NOT adjust length */
+void dav_buffer_place_mem(pool *p, dav_buffer *pbuf, const void *mem,
+ size_t amt, size_t pad);
+
+
+/* --------------------------------------------------------------------
+**
+** HANDY UTILITIES
+*/
+
+/* simple strutures to keep a linked list of pieces of text */
+typedef struct dav_text
+{
+ const char *text;
+ struct dav_text *next;
+} dav_text;
+
+typedef struct
+{
+ dav_text *first;
+ dav_text *last;
+} dav_text_header;
+
+/* contains results from one of the getprop functions */
+typedef struct
+{
+ dav_text * propstats; /* <propstat> element text */
+ dav_text * xmlns; /* namespace decls for <response> elem */
+} dav_get_props_result;
+
+/* holds the contents of a <response> element */
+typedef struct dav_response
+{
+ const char *href; /* always */
+ const char *desc; /* optional description at <response> level */
+
+ /* use status if propresult.propstats is NULL. */
+ dav_get_props_result propresult;
+
+ int status;
+
+ struct dav_response *next;
+} dav_response;
+
+typedef struct
+{
+ request_rec *rnew; /* new subrequest */
+ dav_error err; /* potential error response */
+} dav_lookup_result;
+
+
+void dav_text_append(pool *p, dav_text_header *hdr, const char *text);
+
+dav_lookup_result dav_lookup_uri(const char *uri, request_rec *r);
+
+/* format a time string (buf must be at least DAV_TIMEBUF_SIZE chars) */
+#define DAV_STYLE_ISO8601 1
+#define DAV_STYLE_RFC822 2
+#define DAV_TIMEBUF_SIZE 30
+
+int dav_get_depth(request_rec *r, int def_depth);
+
+
+/* --------------------------------------------------------------------
+**
+** DYNAMIC EXTENSIONS
+*/
+
+/* ### docco goes here... */
+
+
+/*
+** This structure is used to define the runtime, per-directory/location
+** operating context for a single provider.
+*/
+typedef struct
+{
+ int id; /* provider ID */
+
+ void *m_context; /* module-level context (i.e. managed globals) */
+
+ void *d_context; /* per-directory context */
+ table *d_params; /* per-directory DAV config parameters */
+
+ int *ns_map; /* for LIVEPROP, map provider URI to global URI */
+
+} dav_dyn_context;
+
+/*
+** This structure is used to specify a set of hooks and its associated
+** context, on a per-directory/location basis.
+**
+** Note: the context is assembled from various sources. dav_dyn_hooks
+** structures will typically have the same pointer values within the
+** context (e.g. ctx.m_context is shared across all providers in a module).
+*/
+typedef struct dav_dyn_hooks
+{
+ dav_dyn_context ctx; /* context for this set of hooks */
+ const void *hooks; /* the type-specific hooks */
+
+ struct dav_dyn_hooks *next; /* next set of hooks, if applicable */
+
+} dav_dyn_hooks;
+
+/*
+** These enumerated values define the different types of functionality that
+** a provider can implement.
+*/
+enum
+{
+ DAV_DYN_TYPE_SENTINEL,
+
+ DAV_DYN_TYPE_PROPDB, /* property database (1 per dir) */
+ DAV_DYN_TYPE_LOCKS, /* lock handling (1 per dir) */
+ DAV_DYN_TYPE_QUERY_GRAMMAR, /* DASL search grammar (N per dir) */
+ DAV_DYN_TYPE_ACL, /* ACL handling (1 per dir) */
+ DAV_DYN_TYPE_VSN, /* versioning (1 per dir) */
+ DAV_DYN_TYPE_REPOSITORY, /* resource repository (1 per dir) */
+ DAV_DYN_TYPE_LIVEPROP, /* live property handler (N per dir) */
+
+ DAV_DYN_TYPE_MAX
+};
+
+/*
+** This structure defines a provider for a particular type of functionality.
+**
+** The ID is private to a provider and can be used to differentiate between
+** different subclasses of functionality which are implemented using the
+** same set of hooks. For example, a hook function could perform two entirely
+** different operations based on the ID which is passed.
+**
+** is_active() is used by the system to determine whether a particular
+** provider is "active" for the given context. It is possible that a provider
+** is configured for a directory, but has not been enabled -- the is_active()
+** function is used to determine that information.
+**
+** ### is_active is not used right now
+**
+** Note: dav_dyn_provider structures are always treated as "const" by mod_dav.
+*/
+typedef struct dav_dyn_provider
+{
+ int id; /* provider ID */
+
+ int type; /* provider's functionality type */
+ const void *hooks; /* pointer to type-specific hooks */
+
+ int (*is_active)(dav_dyn_context *ctx, int id);
+
+} dav_dyn_provider;
+
+#define DAV_DYN_END_MARKER { 0, DAV_DYN_TYPE_SENTINEL, NULL, NULL }
+
+/*
+** This structure defines a module (a set of providers).
+**
+** The friendly name should be a single word. It is used with the "DAV"
+** directive to specify the module to use for a particular directory/location.
+**
+** The module_open/close functions are used to initialize per-module "global"
+** data. The functions are expected to update ctx->m_context.
+**
+** ### module_open/close are not used at the moment
+** ### dir_* are not well-defined, nor are they used
+**
+** Note: The DAV_DYN_VERSION specifies the version of the dav_dyn_module
+** structure itself. It will be updated if changes in the structure
+** are made. There are no provisions for forward or backward
+** compatible changes.
+**
+** Note: dav_dyn_module structures are always treated as "const" by mod_dav.
+*/
+typedef struct
+{
+ int magic;
+#define DAV_DYN_MAGIC 0x44415621 /* "DAV!" */
+
+ int version;
+#define DAV_DYN_VERSION 1 /* must match exactly */
+
+ const char *name; /* friendly name */
+
+ int (*module_open)(dav_dyn_context *ctx);
+ int (*module_close)(dav_dyn_context *ctx);
+
+ int (*dir_open)(dav_dyn_context *ctx);
+ int (*dir_param)(dav_dyn_context *ctx, const char *param_name,
+ const char *param_value);
+ int (*dir_merge)(dav_dyn_context *base, dav_dyn_context *overrides,
+ dav_dyn_context *result);
+ int (*dir_close)(dav_dyn_context *ctx);
+
+ const dav_dyn_provider *providers; /* providers in this module */
+
+} dav_dyn_module;
+
+int dav_load_module(const char *name, const char *module_sym,
+ const char *filename);
+const dav_dyn_module *dav_find_module(const char *name);
+
+/*
+** Various management functions.
+**
+** NOTE: the pool should be the "configuration pool"
+*/
+void dav_process_builtin_modules(pool *p);
+void dav_process_module(pool *p, const dav_dyn_module *mod);
+
+int * dav_collect_liveprop_uris(pool *p, const dav_hooks_liveprop *hooks);
+extern array_header *dav_liveprop_uris;
+
+void *dav_prepare_scan(pool *p, const dav_dyn_module *mod);
+int dav_scan_providers(void *ctx,
+ const dav_dyn_provider **provider,
+ dav_dyn_hooks *output);
+
+/* handy macros to assist with dav_dyn_hooks.hooks usage */
+#define DAV_AS_HOOKS_PROPDB(ph) ((const dav_hooks_db *)((ph)->hooks))
+#define DAV_AS_HOOKS_LOCKS(ph) ((const dav_hooks_locks *)((ph)->hooks))
+#define DAV_AS_HOOKS_QUERY_GRAMMAR(ph) ((void *)((ph)->hooks))
+#define DAV_AS_HOOKS_ACL(ph) ((void *)((ph)->hooks))
+#define DAV_AS_HOOKS_VSN(ph) ((const dav_hooks_vsn *)((ph)->hooks))
+#define DAV_AS_HOOKS_REPOSITORY(ph) ((const dav_hooks_repository *)((ph)->hooks))
+#define DAV_AS_HOOKS_LIVEPROP(ph) ((const dav_hooks_liveprop *)((ph)->hooks))
+
+/* get provider hooks, given a request record */
+const dav_dyn_hooks *dav_get_provider_hooks(request_rec *r, int provider_type);
+
+#define DAV_GET_HOOKS_PROPDB(r) DAV_AS_HOOKS_PROPDB(dav_get_provider_hooks(r, DAV_DYN_TYPE_PROPDB))
+#define DAV_GET_HOOKS_LOCKS(r) DAV_AS_HOOKS_LOCKS(dav_get_provider_hooks(r, DAV_DYN_TYPE_LOCKS))
+#define DAV_GET_HOOKS_QUERY_GRAMMAR(r) DAV_AS_HOOKS_QUERY_GRAMMAR(dav_get_provider_hooks(r, DAV_DYN_TYPE_QUERY_GRAMMAR))
+#define DAV_GET_HOOKS_ACL(r) DAV_AS_HOOKS_ACL(dav_get_provider_hooks(r, DAV_DYN_TYPE_ACL))
+#define DAV_GET_HOOKS_VSN(r) DAV_AS_HOOKS_VSN(dav_get_provider_hooks(r, DAV_DYN_TYPE_VSN))
+#define DAV_GET_HOOKS_REPOSITORY(r) DAV_AS_HOOKS_REPOSITORY(dav_get_provider_hooks(r, DAV_DYN_TYPE_REPOSITORY))
+#define DAV_GET_HOOKS_LIVEPROP(r) DAV_AS_HOOKS_LIVEPROP(dav_get_provider_hooks(r, DAV_DYN_TYPE_LIVEPROP))
+
+
+/* --------------------------------------------------------------------
+**
+** IF HEADER PROCESSING
+**
+** Here is the definition of the If: header from RFC 2518, S9.4:
+**
+** If = "If" ":" (1*No-tag-list | 1*Tagged-list)
+** No-tag-list = List
+** Tagged-list = Resource 1*List
+** Resource = Coded-URL
+** List = "(" 1*(["Not"](State-token | "[" entity-tag "]")) ")"
+** State-token = Coded-URL
+** Coded-URL = "<" absoluteURI ">" ; absoluteURI from RFC 2616
+**
+** List corresponds to dav_if_state_list. No-tag-list corresponds to
+** dav_if_header with uri==NULL. Tagged-list corresponds to a sequence of
+** dav_if_header structures with (duplicate) uri==Resource -- one
+** dav_if_header per state_list. A second Tagged-list will start a new
+** sequence of dav_if_header structures with the new URI.
+**
+** A summary of the semantics, mapped into our structures:
+** - Chained dav_if_headers: OR
+** - Chained dav_if_state_lists: AND
+** - NULL uri matches all resources
+*/
+
+typedef enum
+{
+ dav_if_etag,
+ dav_if_opaquelock
+} dav_if_state_type;
+
+typedef struct dav_if_state_list
+{
+ dav_if_state_type type;
+
+ int condition;
+#define DAV_IF_COND_NORMAL 0
+#define DAV_IF_COND_NOT 1 /* "Not" was applied */
+
+ const char *etag; /* etag */
+ dav_locktoken *locktoken; /* locktoken */
+
+ struct dav_if_state_list *next;
+} dav_if_state_list;
+
+typedef struct dav_if_header
+{
+ const char *uri;
+ size_t uri_len;
+ struct dav_if_state_list *state;
+ struct dav_if_header *next;
+
+ int dummy_header; /* used internally by the lock/etag validation */
+} dav_if_header;
+
+typedef struct dav_locktoken_list
+{
+ dav_locktoken *locktoken;
+ struct dav_locktoken_list *next;
+} dav_locktoken_list;
+
+dav_error * dav_get_locktoken_list(request_rec *r, dav_locktoken_list **ltl);
+
+
+/* --------------------------------------------------------------------
+**
+** XML PARSING
+*/
+
+/*
+** Qualified namespace values
+**
+** DAV_NS_DAV_ID
+** We always insert the "DAV:" namespace URI at the head of the
+** namespace array. This means that it will always be at ID==0,
+** making it much easier to test for.
+**
+** DAV_NS_NONE
+** This special ID is used for two situations:
+**
+** 1) The namespace prefix begins with "xml" (and we do not know
+** what it means). Namespace prefixes with "xml" (any case) as
+** their first three characters are reserved by the XML Namespaces
+** specification for future use. mod_dav will pass these through
+** unchanged. When this identifier is used, the prefix is LEFT in
+** the element/attribute name. Downstream processing should not
+** prepend another prefix.
+**
+** 2) The element/attribute does not have a namespace.
+**
+** a) No prefix was used, and a default namespace has not been
+** defined.
+** b) No prefix was used, and the default namespace was specified
+** to mean "no namespace". This is done with a namespace
+** declaration of: xmlns=""
+** (this declaration is typically used to override a previous
+** specification for the default namespace)
+**
+** In these cases, we need to record that the elem/attr has no
+** namespace so that we will not attempt to prepend a prefix.
+** All namespaces that are used will have a prefix assigned to
+** them -- mod_dav will never set or use the default namespace
+** when generating XML. This means that "no prefix" will always
+** mean "no namespace".
+**
+** In both cases, the XML generation will avoid prepending a prefix.
+** For the first case, this means the original prefix/name will be
+** inserted into the output stream. For the latter case, it means
+** the name will have no prefix, and since we never define a default
+** namespace, this means it will have no namespace.
+**
+** Note: currently, mod_dav understands the "xmlns" prefix and the
+** "xml:lang" attribute. These are handled specially (they aren't
+** left within the XML tree), so the DAV_NS_NONE value won't ever
+** really apply to these values.
+*/
+#define DAV_NS_DAV_ID 0 /* namespace ID for "DAV:" */
+#define DAV_NS_NONE -10 /* no namespace for this elem/attr */
+
+#define DAV_NS_ERROR_BASE -100 /* used only during processing */
+#define DAV_NS_IS_ERROR(e) ((e) <= DAV_NS_ERROR_BASE)
+
+
+/*
+** dav_xml_doc: holds a parsed XML document
+** dav_xml_elem: holds a parsed XML element
+** dav_xml_attr: holds a parsed XML attribute
+**
+** dav_xml_ns_scope: internal struct used during processing to scope
+** namespace declarations
+*/
+
+typedef struct dav_xml_attr
+{
+ const char *name; /* attribute name */
+ int ns; /* index into namespace array */
+
+ const char *value; /* attribute value */
+
+ struct dav_xml_attr *next; /* next attribute */
+} dav_xml_attr;
+
+typedef struct dav_xml_elem
+{
+ const char *name; /* element name */
+ int ns; /* index into namespace array */
+ const char *lang; /* xml:lang for attrs/contents */
+
+ dav_text_header first_cdata; /* cdata right after start tag */
+ dav_text_header following_cdata; /* cdata after MY end tag */
+
+ struct dav_xml_elem *parent; /* parent element */
+ struct dav_xml_elem *next; /* next (sibling) element */
+ struct dav_xml_elem *first_child; /* first child element */
+ struct dav_xml_attr *attr; /* first attribute */
+
+ /* used only during parsing */
+ struct dav_xml_elem *last_child; /* last child element */
+ struct dav_xml_ns_scope *ns_scope; /* namespaces scoped by this elem */
+
+ /* used during request processing */
+ int propid; /* live property ID */
+ const dav_hooks_liveprop *provider; /* the provider defining this prop */
+ const int *ns_map; /* ns map for this provider */
+
+} dav_xml_elem;
+
+#define DAV_ELEM_IS_EMPTY(e) ((e)->first_child == NULL && \
+ (e)->first_cdata.first == NULL)
+
+typedef struct dav_xml_doc
+{
+ dav_xml_elem *root; /* root element */
+ array_header *namespaces; /* array of namespaces used */
+
+} dav_xml_doc;
+
+
+int dav_parse_input(request_rec *r, dav_xml_doc **pdoc);
+
+int dav_validate_root(const dav_xml_doc *doc, const char *tagname);
+
+dav_xml_elem *dav_find_child(
+ const dav_xml_elem *elem,
+ const char *tagname);
+
+void dav_xml2text(
+ pool *p,
+ const dav_xml_elem *elem,
+ int style,
+ array_header *namespaces,
+ int *ns_map,
+ const char **pbuf,
+ size_t *psize
+ );
+#define DAV_X2T_FULL 0 /* start tag, contents, end tag */
+#define DAV_X2T_INNER 1 /* contents only */
+#define DAV_X2T_LANG_INNER 2 /* xml:lang + inner contents */
+#define DAV_X2T_FULL_NS_LANG 3 /* FULL + ns defns + xml:lang */
+
+const char *dav_empty_elem(pool *p, const dav_xml_elem *elem);
+void dav_quote_xml_elem(pool *p, dav_xml_elem *elem);
+const char * dav_quote_string(pool *p, const char *s, int quotes);
+
+
+/* --------------------------------------------------------------------
+**
+** LIVE PROPERTY HANDLING
+*/
+
+typedef enum {
+ DAV_PROP_INSERT_NOTME, /* prop not defined by this provider */
+ DAV_PROP_INSERT_NOTDEF, /* property is defined by this provider,
+ but nothing was inserted because the
+ (live) property is not defined for this
+ resource (it may be present as a dead
+ property). */
+ DAV_PROP_INSERT_NAME, /* a property name (empty elem) was
+ inserted into the text block */
+ DAV_PROP_INSERT_VALUE /* a property name/value pair was inserted
+ into the text block */
+} dav_prop_insert;
+
+typedef enum {
+ DAV_PROP_RW_NOTME, /* not my property */
+ DAV_PROP_RW_NO, /* property is NOT writeable */
+ DAV_PROP_RW_YES /* property IS writeable */
+} dav_prop_rw;
+
+/* opaque type for PROPPATCH rollback information */
+typedef struct dav_liveprop_rollback dav_liveprop_rollback;
+
+struct dav_hooks_liveprop
+{
+ /*
+ ** This URI is returned in the DAV: header to let clients know what
+ ** sets of live properties are supported by the installation. mod_dav
+ ** will place open/close angle brackets around this value (much like
+ ** a Coded-URL); quotes and brackets should not be in the value.
+ **
+ ** Example: http://apache.org/dav/props/
+ **
+ ** (of course, use your own domain to ensure a unique value)
+ */
+ const char * propset_uri;
+
+ /*
+ ** Find a property, returning a non-zero, unique, opaque identifier.
+ **
+ ** NOTE: Providers must ensure this identifier is universally unique.
+ ** See the registration table below.
+ ** ### it would be nice to avoid this uniqueness constraint. however,
+ ** ### that would mean our xml_elem annotation concept would need to
+ ** ### change (w.r.t. the fact that it acts as a cache for find_prop).
+ **
+ ** Returns 0 if the property is not defined by this provider.
+ */
+ int (*find_prop)(const char *ns_uri, const char *name);
+
+ /*
+ ** Insert a property name/value into a text block. The property to
+ ** insert is identified by the propid value. Providers should return
+ ** DAV_PROP_INSERT_NOTME if they do not define the specified propid.
+ ** If insvalue is true, then the property's value should be inserted;
+ ** otherwise, an empty element (ie. just the prop's name) should be
+ ** inserted.
+ **
+ ** Returns one of DAV_PROP_INSERT_* based on what happened.
+ **
+ ** ### we may need more context... ie. the lock database
+ */
+ dav_prop_insert (*insert_prop)(const dav_resource *resource,
+ int propid, int insvalue,
+ const int *ns_map, dav_text_header *phdr);
+
+ /*
+ ** Insert all known/defined property names (and values). This is
+ ** similar to insert_prop, but *all* properties will be inserted
+ ** rather than specific, individual properties.
+ */
+ void (*insert_all)(const dav_resource *resource, int insvalue,
+ const int *ns_map, dav_text_header *phdr);
+
+ /*
+ ** Determine whether a given property is writeable.
+ **
+ ** ### we may want a different semantic. i.e. maybe it should be
+ ** ### "can we write <value> into this property?"
+ **
+ ** Returns appropriate read/write status.
+ */
+ dav_prop_rw (*is_writeable)(const dav_resource *resource, int propid);
+
+ /*
+ ** This member defines the set of namespace URIs that the provider
+ ** uses for its properties. When insert_all is called, it will be
+ ** passed a list of integers that map from indices into this list
+ ** to namespace IDs for output generation.
+ **
+ ** The last entry in this list should be a NULL value (sentinel).
+ */
+ const char * const * namespace_uris;
+
+ /*
+ ** ### this is not the final design. we want an open-ended way for
+ ** ### liveprop providers to attach *new* properties. To this end,
+ ** ### we'll have a "give me a list of the props you define", a way
+ ** ### to check for a prop's existence, a way to validate a set/remove
+ ** ### of a prop, and a way to execute/commit/rollback that change.
+ */
+
+ /*
+ ** Validate that the live property can be assigned a value, and that
+ ** the provided value is valid.
+ **
+ ** elem will point to the XML element that names the property. For
+ ** example:
+ ** <lp1:executable>T</lp1:executable>
+ **
+ ** The provider can access the cdata fields and the child elements
+ ** to extract the relevant pieces.
+ **
+ ** operation is one of DAV_PROP_OP_SET or _DELETE.
+ **
+ ** The provider may return a value in *context which will be passed
+ ** to each of the exec/commit/rollback functions. For example, this
+ ** may contain an internal value which has been processed from the
+ ** input element.
+ **
+ ** The provider must set defer_to_dead to true (non-zero) or false.
+ ** If true, then the set/remove is deferred to the dead property
+ ** database. Note: it will be set to zero on entry.
+ */
+ dav_error * (*patch_validate)(const dav_resource *resource,
+ const dav_xml_elem *elem,
+ int operation,
+ void **context,
+ int *defer_to_dead);
+
+ /* ### doc... */
+ dav_error * (*patch_exec)(dav_resource *resource,
+ const dav_xml_elem *elem,
+ int operation,
+ void *context,
+ dav_liveprop_rollback **rollback_ctx);
+
+ /* ### doc... */
+ void (*patch_commit)(dav_resource *resource,
+ int operation,
+ void *context,
+ dav_liveprop_rollback *rollback_ctx);
+
+ /* ### doc... */
+ dav_error * (*patch_rollback)(dav_resource *resource,
+ int operation,
+ void *context,
+ dav_liveprop_rollback *rollback_ctx);
+};
+
+/*
+** Property Identifier Registration
+**
+** At the moment, mod_dav requires live property providers to ensure that
+** each property returned has a unique value. For now, this is done through
+** central registration (there are no known providers other than the default,
+** so this remains manageable).
+**
+** WARNING: the TEST ranges should never be "shipped".
+*/
+#define DAV_PROPID_CORE 10000 /* ..10099. defined by mod_dav */
+#define DAV_PROPID_FS 10100 /* ..10299.
+ mod_dav filesystem provider. */
+#define DAV_PROPID_TEST1 10300 /* ..10399 */
+#define DAV_PROPID_TEST2 10400 /* ..10499 */
+#define DAV_PROPID_TEST3 10500 /* ..10599 */
+/* Next: 10600 */
+
+
+/* --------------------------------------------------------------------
+**
+** DATABASE FUNCTIONS
+*/
+
+typedef struct dav_db dav_db;
+typedef struct
+{
+ char *dptr;
+ size_t dsize;
+} dav_datum;
+
+/* hook functions to enable pluggable databases */
+struct dav_hooks_db
+{
+ dav_error * (*open)(pool *p, const dav_resource *resource, int ro,
+ dav_db **pdb);
+ void (*close)(dav_db *db);
+
+ /*
+ ** Fetch the value from the database. If the value does not exist,
+ ** then *pvalue should be zeroed.
+ **
+ ** Note: it is NOT an error for the key/value pair to not exist.
+ */
+ dav_error * (*fetch)(dav_db *db, dav_datum key, dav_datum *pvalue);
+
+ dav_error * (*store)(dav_db *db, dav_datum key, dav_datum value);
+ dav_error * (*remove)(dav_db *db, dav_datum key);
+
+ /* returns 1 if the record specified by "key" exists; 0 otherwise */
+ int (*exists)(dav_db *db, dav_datum key);
+
+ dav_error * (*firstkey)(dav_db *db, dav_datum *pkey);
+ dav_error * (*nextkey)(dav_db *db, dav_datum *pkey);
+
+ void (*freedatum)(dav_db *db, dav_datum data);
+};
+
+
+/* --------------------------------------------------------------------
+**
+** LOCK FUNCTIONS
+*/
+
+/* Used to represent a Timeout header of "Infinity" */
+#define DAV_TIMEOUT_INFINITE 0
+
+time_t dav_get_timeout(request_rec *r);
+
+/*
+** Opaque, repository-specific information for a lock database.
+*/
+typedef struct dav_lockdb_private dav_lockdb_private;
+
+/*
+** Opaque, repository-specific information for a lock record.
+*/
+typedef struct dav_lock_private dav_lock_private;
+
+/*
+** Lock database type. Lock providers are urged to implement a "lazy" open, so
+** doing an "open" is cheap until something is actually needed from the DB.
+*/
+typedef struct
+{
+ const dav_hooks_locks *hooks; /* the hooks used for this lockdb */
+ int ro; /* was it opened readonly? */
+
+ dav_lockdb_private *info;
+
+} dav_lockdb;
+
+typedef enum {
+ DAV_LOCKSCOPE_UNKNOWN,
+ DAV_LOCKSCOPE_EXCLUSIVE,
+ DAV_LOCKSCOPE_SHARED
+} dav_lock_scope;
+
+typedef enum {
+ DAV_LOCKTYPE_UNKNOWN,
+ DAV_LOCKTYPE_WRITE
+} dav_lock_type;
+
+typedef enum {
+ DAV_LOCKREC_DIRECT, /* lock asserted on this resource */
+ DAV_LOCKREC_INDIRECT, /* lock inherited from a parent */
+ DAV_LOCKREC_INDIRECT_PARTIAL /* most info is not filled in */
+} dav_lock_rectype;
+
+/*
+** dav_lock: hold information about a lock on a resource.
+**
+** This structure is used for both direct and indirect locks. A direct lock
+** is a lock applied to a specific resource by the client. An indirect lock
+** is one that is inherited from a parent resource by virtue of a non-zero
+** Depth: header when the lock was applied.
+**
+** mod_dav records both types of locks in the lock database, managing their
+** addition/removal as resources are moved about the namespace.
+**
+** Note that the lockdb is free to marshal this structure in any form that
+** it likes.
+**
+** For a "partial" lock, the <rectype> and <locktoken> fields must be filled
+** in. All other (user) fields should be zeroed. The lock provider will
+** usually fill in the <info> field, and the <next> field may be used to
+** construct a list of partial locks.
+**
+** The lock provider MUST use the info field to store a value such that a
+** dav_lock structure can locate itself in the underlying lock database.
+** This requirement is needed for refreshing: when an indirect dav_lock is
+** refreshed, its reference to the direct lock does not specify the direct's
+** resource, so the only way to locate the (refreshed, direct) lock in the
+** database is to use the info field.
+**
+** Note that <is_locknull> only refers to the resource where this lock was
+** found.
+** ### hrm. that says the abstraction is wrong. is_locknull may disappear.
+*/
+typedef struct dav_lock
+{
+ dav_lock_rectype rectype; /* type of lock record */
+ int is_locknull; /* lock establishes a locknull resource */
+
+ /* ### put the resource in here? */
+
+ dav_lock_scope scope; /* scope of the lock */
+ dav_lock_type type; /* type of lock */
+ int depth; /* depth of the lock */
+ time_t timeout; /* when the lock will timeout */
+
+ const dav_locktoken *locktoken; /* the token that was issued */
+
+ const char *owner; /* (XML) owner of the lock */
+ const char *auth_user; /* auth'd username owning lock */
+
+ dav_lock_private *info; /* private to the lockdb */
+
+ struct dav_lock *next; /* for managing a list of locks */
+} dav_lock;
+
+/* Property-related public lock functions */
+const char *dav_lock_get_activelock(request_rec *r, dav_lock *locks,
+ dav_buffer *pbuf);
+
+/* LockDB-related public lock functions */
+const char *dav_get_lockdb_path(const request_rec *r);
+dav_error * dav_lock_parse_lockinfo(request_rec *r,
+ const dav_resource *resrouce,
+ dav_lockdb *lockdb,
+ const dav_xml_doc *doc,
+ dav_lock **lock_request);
+int dav_unlock(request_rec *r, const dav_resource *resource,
+ const dav_locktoken *locktoken);
+dav_error * dav_add_lock(request_rec *r, const dav_resource *resource,
+ dav_lockdb *lockdb, dav_lock *request,
+ dav_response **response);
+dav_error * dav_notify_created(request_rec *r,
+ dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int resource_state,
+ int depth);
+
+dav_error * dav_lock_query(dav_lockdb *lockdb, const dav_resource *resource,
+ dav_lock **locks);
+
+dav_error * dav_validate_request(request_rec *r, dav_resource *resource,
+ int depth, dav_locktoken *locktoken,
+ dav_response **response, int flags,
+ dav_lockdb *lockdb);
+/*
+** flags:
+** 0x0F -- reserved for <dav_lock_scope> values
+**
+** other flags, detailed below
+*/
+#define DAV_VALIDATE_RESOURCE 0x0010 /* validate just the resource */
+#define DAV_VALIDATE_PARENT 0x0020 /* validate resource AND its parent */
+#define DAV_VALIDATE_ADD_LD 0x0040 /* add DAV:lockdiscovery into
+ the 424 DAV:response */
+#define DAV_VALIDATE_USE_424 0x0080 /* return 424 status, not 207 */
+#define DAV_VALIDATE_IS_PARENT 0x0100 /* for internal use */
+
+/* Lock-null related public lock functions */
+int dav_get_resource_state(request_rec *r, const dav_resource *resource);
+
+/* Lock provider hooks. Locking is optional, so there may be no
+ * lock provider for a given repository.
+ */
+struct dav_hooks_locks
+{
+ /* Return the supportedlock property for this provider */
+ /* ### maybe this should take a resource argument? */
+ const char * (*get_supportedlock)(void);
+
+ /* Parse a lock token URI, returning a lock token object allocated
+ * in the given pool.
+ */
+ dav_error * (*parse_locktoken)(
+ pool *p,
+ const char *char_token,
+ dav_locktoken **locktoken_p
+ );
+
+ /* Format a lock token object into a URI string, allocated in
+ * the given pool.
+ *
+ * Always returns non-NULL.
+ */
+ const char * (*format_locktoken)(
+ pool *p,
+ const dav_locktoken *locktoken
+ );
+
+ /* Compare two lock tokens.
+ *
+ * Result < 0 => lt1 < lt2
+ * Result == 0 => lt1 == lt2
+ * Result > 0 => lt1 > lt2
+ */
+ int (*compare_locktoken)(
+ const dav_locktoken *lt1,
+ const dav_locktoken *lt2
+ );
+
+ /* Open the provider's lock database.
+ *
+ * The provider may or may not use a "real" database for locks
+ * (a lock could be an attribute on a resource, for example).
+ *
+ * The provider may choose to use the value of the DAVLockDB directive
+ * (as returned by dav_get_lockdb_path()) to decide where to place
+ * any storage it may need.
+ *
+ * The request storage pool should be associated with the lockdb,
+ * so it can be used in subsequent operations.
+ *
+ * If ro != 0, only readonly operations will be performed.
+ * If force == 0, the open can be "lazy"; no subsequent locking operations
+ * may occur.
+ * If force != 0, locking operations will definitely occur.
+ */
+ dav_error * (*open_lockdb)(
+ request_rec *r,
+ int ro,
+ int force,
+ dav_lockdb **lockdb
+ );
+
+ /* Indicates completion of locking operations */
+ void (*close_lockdb)(
+ dav_lockdb *lockdb
+ );
+
+ /* Take a resource out of the lock-null state. */
+ dav_error * (*remove_locknull_state)(
+ dav_lockdb *lockdb,
+ const dav_resource *resource
+ );
+
+ /*
+ ** Create a (direct) lock structure for the given resource. A locktoken
+ ** will be created.
+ **
+ ** The lock provider may store private information into lock->info.
+ */
+ dav_error * (*create_lock)(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ dav_lock **lock);
+
+ /*
+ ** Get the locks associated with the specified resource.
+ **
+ ** If resolve_locks is true (non-zero), then any indirect locks are
+ ** resolved to their actual, direct lock (i.e. the reference to followed
+ ** to the original lock).
+ **
+ ** The locks, if any, are returned as a linked list in no particular
+ ** order. If no locks are present, then *locks will be NULL.
+ */
+ dav_error * (*get_locks)(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int calltype,
+ dav_lock **locks);
+
+#define DAV_GETLOCKS_RESOLVED 0 /* resolve indirects to directs */
+#define DAV_GETLOCKS_PARTIAL 1 /* leave indirects partially filled */
+#define DAV_GETLOCKS_COMPLETE 2 /* fill out indirect locks */
+
+ /*
+ ** Find a particular lock on a resource (specified by its locktoken).
+ **
+ ** *lock will be set to NULL if the lock is not found.
+ **
+ ** Note that the provider can optimize the unmarshalling -- only one
+ ** lock (or none) must be constructed and returned.
+ **
+ ** If partial_ok is true (non-zero), then an indirect lock can be
+ ** partially filled in. Otherwise, another lookup is done and the
+ ** lock structure will be filled out as a DAV_LOCKREC_INDIRECT.
+ */
+ dav_error * (*find_lock)(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ const dav_locktoken *locktoken,
+ int partial_ok,
+ dav_lock **lock);
+
+ /*
+ ** Quick test to see if the resource has *any* locks on it.
+ **
+ ** This is typically used to determine if a non-existent resource
+ ** has a lock and is (therefore) a locknull resource.
+ **
+ ** WARNING: this function may return TRUE even when timed-out locks
+ ** exist (i.e. it may not perform timeout checks).
+ */
+ dav_error * (*has_locks)(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int *locks_present);
+
+ /*
+ ** Append the specified lock(s) to the set of locks on this resource.
+ **
+ ** If "make_indirect" is true (non-zero), then the specified lock(s)
+ ** should be converted to an indirect lock (if it is a direct lock)
+ ** before appending. Note that the conversion to an indirect lock does
+ ** not alter the passed-in lock -- the change is internal the
+ ** append_locks function.
+ **
+ ** Multiple locks are specified using the lock->next links.
+ */
+ dav_error * (*append_locks)(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int make_indirect,
+ const dav_lock *lock);
+
+ /*
+ ** Remove any lock that has the specified locktoken.
+ **
+ ** If locktoken == NULL, then ALL locks are removed.
+ */
+ dav_error * (*remove_lock)(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ const dav_locktoken *locktoken);
+
+ /*
+ ** Refresh all locks, found on the specified resource, which has a
+ ** locktoken in the provided list.
+ **
+ ** If the lock is indirect, then the direct lock is referenced and
+ ** refreshed.
+ **
+ ** Each lock that is updated is returned in the <locks> argument.
+ ** Note that the locks will be fully resolved.
+ */
+ dav_error * (*refresh_locks)(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ const dav_locktoken_list *ltl,
+ time_t new_time,
+ dav_lock **locks);
+
+ /*
+ ** Look up the resource associated with a particular locktoken.
+ **
+ ** The search begins at the specified <start_resource> and the lock
+ ** specified by <locktoken>.
+ **
+ ** If the resource/token specifies an indirect lock, then the direct
+ ** lock will be looked up, and THAT resource will be returned. In other
+ ** words, this function always returns the resource where a particular
+ ** lock (token) was asserted.
+ **
+ ** NOTE: this function pointer is allowed to be NULL, indicating that
+ ** the provider does not support this type of functionality. The
+ ** caller should then traverse up the repository hierarchy looking
+ ** for the resource defining a lock with this locktoken.
+ */
+ dav_error * (*lookup_resource)(dav_lockdb *lockdb,
+ const dav_locktoken *locktoken,
+ const dav_resource *start_resource,
+ const dav_resource **resource);
+};
+
+/* what types of resources can be discovered by dav_get_resource_state() */
+#define DAV_RESOURCE_LOCK_NULL 10 /* resource lock-null */
+#define DAV_RESOURCE_NULL 11 /* resource null */
+#define DAV_RESOURCE_EXISTS 12 /* resource exists */
+#define DAV_RESOURCE_ERROR 13 /* an error occurred */
+
+
+/* --------------------------------------------------------------------
+**
+** PROPERTY HANDLING
+*/
+
+typedef struct dav_propdb dav_propdb;
+
+
+dav_error *dav_open_propdb(
+ request_rec *r,
+ dav_lockdb *lockdb,
+ dav_resource *resource,
+ int ro,
+ array_header *ns_xlate,
+ dav_propdb **propdb);
+
+void dav_close_propdb(dav_propdb *db);
+
+dav_get_props_result dav_get_props(
+ dav_propdb *db,
+ dav_xml_doc *doc);
+
+dav_get_props_result dav_get_allprops(
+ dav_propdb *db,
+ int getvals);
+
+/*
+** 3-phase property modification.
+**
+** 1) validate props. readable? unlocked? ACLs allow access?
+** 2) execute operation (set/delete)
+** 3) commit or rollback
+**
+** ### eventually, auth must be available. a ref to the request_rec (which
+** ### contains the auth info) should be in the shared context struct.
+**
+** Each function may alter the error values and information contained within
+** the context record. This should be done as an "increasing" level of
+** error, rather than overwriting any previous error.
+**
+** Note that commit() cannot generate errors. It should simply free the
+** rollback information.
+**
+** rollback() may generate additional errors because the rollback operation
+** can sometimes fail(!).
+**
+** The caller should allocate an array of these, one per operation. It should
+** be zero-initialized, then the db, operation, and prop fields should be
+** filled in before calling dav_prop_validate. Note that the set/delete
+** operations are order-dependent. For a given (logical) context, the same
+** pointer must be passed to each phase.
+**
+** error_type is an internal value, but will have the same numeric value
+** for each possible "desc" value. This allows the caller to group the
+** descriptions via the error_type variable, rather than through string
+** comparisons. Note that "status" does not provide enough granularity to
+** differentiate/group the "desc" values.
+**
+** Note that the propdb will maintain some (global) context across all
+** of the property change contexts. This implies that you can have only
+** one open transaction per propdb.
+*/
+typedef struct dav_prop_ctx
+{
+ dav_propdb *propdb;
+
+ int operation;
+#define DAV_PROP_OP_SET 1 /* set a property value */
+#define DAV_PROP_OP_DELETE 2 /* delete a prop value */
+/* ### add a GET? */
+
+ dav_xml_elem *prop; /* property to affect */
+
+ dav_error *err; /* error (if any) */
+
+ /* private items to the propdb */
+ int is_liveprop;
+ void *liveprop_ctx;
+ struct dav_rollback_item *rollback; /* optional rollback info */
+
+ /* private to mod_dav.c */
+ request_rec *r;
+
+} dav_prop_ctx;
+
+void dav_prop_validate(dav_prop_ctx *ctx);
+void dav_prop_exec(dav_prop_ctx *ctx);
+void dav_prop_commit(dav_prop_ctx *ctx);
+void dav_prop_rollback(dav_prop_ctx *ctx);
+
+#define DAV_PROP_CTX_HAS_ERR(dpc) ((dpc).err && (dpc).err->status >= 300)
+
+
+/* --------------------------------------------------------------------
+**
+** WALKER STRUCTURE
+*/
+
+/* private, opaque info structure for repository walking context */
+typedef struct dav_walker_private dav_walker_private;
+
+/* directory tree walking context */
+typedef struct dav_walker_ctx
+{
+ int walk_type;
+#define DAV_WALKTYPE_AUTH 1 /* limit to authorized files */
+#define DAV_WALKTYPE_ALL 2 /* walk normal files */
+#define DAV_WALKTYPE_HIDDEN 4 /* walk hidden files */
+#define DAV_WALKTYPE_LOCKNULL 8 /* walk locknull resources */
+
+ int postfix; /* call func for dirs after files */
+
+ dav_error * (*func)(struct dav_walker_ctx *ctx, int calltype);
+#define DAV_CALLTYPE_MEMBER 1 /* called for a member resource */
+#define DAV_CALLTYPE_COLLECTION 2 /* called for a collection */
+#define DAV_CALLTYPE_LOCKNULL 3 /* called for a locknull resource */
+#define DAV_CALLTYPE_POSTFIX 4 /* postfix call for a collection */
+
+ struct pool *pool;
+
+ request_rec *r; /* original request */
+ dav_buffer uri; /* current URI */
+ const dav_resource *resource; /* current resource */
+ const dav_resource *res2; /* optional secondary resource */
+
+ const dav_resource *root; /* RO: root resource of the walk */
+
+ dav_lockdb *lockdb;
+
+ dav_response *response; /* OUT: multistatus responses */
+
+ /* for PROPFIND operations */
+ dav_xml_doc *doc;
+ int propfind_type;
+#define DAV_PROPFIND_IS_ALLPROP 1
+#define DAV_PROPFIND_IS_PROPNAME 2
+#define DAV_PROPFIND_IS_PROP 3
+
+ dav_text *propstat_404; /* (cached) propstat giving a 404 error */
+
+ /* for COPY and MOVE operations */
+ int is_move;
+ dav_buffer work_buf;
+
+ const dav_if_header *if_header; /* for validation */
+ const dav_locktoken *locktoken; /* for UNLOCK */
+ const dav_lock *lock; /* for LOCK */
+ int skip_root; /* for dav_inherit_locks() */
+
+ int flags;
+
+ dav_walker_private *info; /* for use by repository manager */
+
+} dav_walker_ctx;
+
+void dav_add_response(dav_walker_ctx *ctx, const char *href, int status,
+ dav_get_props_result *propstats);
+
+
+/* --------------------------------------------------------------------
+**
+** "STREAM" STRUCTURE
+**
+** mod_dav uses this abstraction for interacting with the repository
+** while fetching/storing resources. mod_dav views resources as a stream
+** of bytes.
+**
+** Note that the structure is opaque -- it is private to the repository
+** that created the stream in the repository's "open" function.
+*/
+
+typedef struct dav_stream dav_stream;
+
+typedef enum {
+ DAV_MODE_READ, /* open for reading */
+ DAV_MODE_READ_SEEKABLE, /* open for random access reading */
+ DAV_MODE_WRITE_TRUNC, /* truncate and open for writing */
+ DAV_MODE_WRITE_SEEKABLE /* open for writing; random access */
+} dav_stream_mode;
+
+/* --------------------------------------------------------------------
+**
+** REPOSITORY FUNCTIONS
+*/
+
+/* Repository provider hooks */
+struct dav_hooks_repository
+{
+ /* Flag for whether repository requires special GET handling.
+ * If resources in the repository are not visible in the
+ * filesystem location which URLs map to, then special handling
+ * is required to first fetch a resource from the repository,
+ * respond to the GET request, then free the resource copy.
+ */
+ int handle_get;
+
+ /* Get a resource descriptor for the URI in a request.
+ * A descriptor is returned even if the resource does not exist.
+ * The return value should only be NULL for some kind of fatal error.
+ *
+ * The root_dir is the root of the directory for which this repository
+ * is configured.
+ * The workspace is the value of any Target-Selector header, or NULL
+ * if there is none.
+ *
+ * The provider may associate the request storage pool with the resource,
+ * to use in other operations on that resource.
+ */
+ dav_resource * (*get_resource)(
+ request_rec *r,
+ const char *root_dir,
+ const char *workspace
+ );
+
+ /* Get a resource descriptor for the parent of the given resource.
+ * The resources need not exist. NULL is returned if the resource
+ * is the root collection.
+ */
+ dav_resource * (*get_parent_resource)(
+ const dav_resource *resource
+ );
+
+ /* Determine whether two resource descriptors refer to the same resource.
+ *
+ * Result != 0 => the resources are the same.
+ */
+ int (*is_same_resource)(
+ const dav_resource *res1,
+ const dav_resource *res2
+ );
+
+ /* Determine whether one resource is a parent (immediate or otherwise)
+ * of another.
+ *
+ * Result != 0 => res1 is a parent of res2.
+ */
+ int (*is_parent_resource)(
+ const dav_resource *res1,
+ const dav_resource *res2
+ );
+
+ /*
+ ** Open a stream for this resource, using the specified mode. The
+ ** stream will be returned in *stream.
+ */
+ dav_error * (*open_stream)(const dav_resource *resource,
+ dav_stream_mode mode,
+ dav_stream **stream);
+
+ /*
+ ** Close the specified stream.
+ **
+ ** mod_dav will (ideally) make sure to call this. For safety purposes,
+ ** a provider should (ideally) register a cleanup function with the
+ ** request pool to get this closed and cleaned up.
+ **
+ ** Note the possibility of an error from the close -- it is entirely
+ ** feasible that the close does a "commit" of some kind, which can
+ ** produce an error.
+ **
+ ** commit should be TRUE (non-zero) or FALSE (0) if the stream was
+ ** opened for writing. This flag states whether to retain the file
+ ** or not.
+ ** Note: the commit flag is ignored for streams opened for reading.
+ */
+ dav_error * (*close_stream)(dav_stream *stream, int commit);
+
+ /*
+ ** Read data from the stream.
+ **
+ ** The size of the buffer is passed in *bufsize, and the amount read
+ ** is returned in *bufsize.
+ **
+ ** *bufsize should be set to zero when the end of file is reached.
+ ** As a corollary, this function should always read at least one byte
+ ** on each call, until the EOF condition is met.
+ */
+ dav_error * (*read_stream)(dav_stream *stream,
+ void *buf, size_t *bufsize);
+
+ /*
+ ** Write data to the stream.
+ **
+ ** All of the bytes must be written, or an error should be returned.
+ */
+ dav_error * (*write_stream)(dav_stream *stream,
+ const void *buf, size_t bufsize);
+
+ /*
+ ** Seek to an absolute position in the stream. This is used to support
+ ** Content-Range in a GET/PUT.
+ **
+ ** NOTE: if this function is NULL (which is allowed), then any
+ ** operations using Content-Range will be refused.
+ */
+ dav_error * (*seek_stream)(dav_stream *stream, off_t abs_position);
+
+ /*
+ ** If a GET is processed using a stream (open_stream, read_stream)
+ ** rather than via a sub-request (on get_pathname), then this function
+ ** is used to provide the repository with a way to set the headers
+ ** in the response.
+ **
+ ** It may be NULL if get_pathname is provided.
+ */
+ dav_error * (*set_headers)(request_rec *r,
+ const dav_resource *resource);
+
+ /* Get a pathname for the file represented by the resource descriptor.
+ * A provider may need to create a temporary copy of the file, if it is
+ * not directly accessible in a filesystem. free_handle_p will be set by
+ * the provider to point to information needed to clean up any temporary
+ * storage used.
+ *
+ * Returns NULL if the file could not be made accessible.
+ */
+ const char * (*get_pathname)(
+ const dav_resource *resource,
+ void **free_handle_p
+ );
+
+ /* Free any temporary storage associated with a file made accessible by
+ * get_pathname().
+ */
+ void (*free_file)(
+ void *free_handle
+ );
+
+ /* Create a collection resource. The resource must not already exist.
+ *
+ * Result == NULL if the collection was created successfully. Also, the
+ * resource object is updated to reflect that the resource exists, and
+ * is a collection.
+ */
+ dav_error * (*create_collection)(
+ pool *p, dav_resource *resource
+ );
+
+ /* Copy one resource to another. The destination must not exist.
+ * Handles both files and collections. Properties are copied as well.
+ * The depth argument is ignored for a file, and can be either 0 or
+ * DAV_INFINITY for a collection.
+ * If an error occurs in a child resource, then the return value is
+ * non-NULL, and *response is set to a multistatus response.
+ * If the copy is successful, the dst resource object is
+ * updated to reflect that the resource exists.
+ */
+ dav_error * (*copy_resource)(
+ const dav_resource *src,
+ dav_resource *dst,
+ int depth,
+ dav_response **response
+ );
+
+ /* Move one resource to another. The destination must not exist.
+ * Handles both files and collections. Properties are moved as well.
+ * If an error occurs in a child resource, then the return value is
+ * non-NULL, and *response is set to a multistatus response.
+ * If the move is successful, the src and dst resource objects are
+ * updated to reflect that the source no longer exists, and the
+ * destination does.
+ */
+ dav_error * (*move_resource)(
+ dav_resource *src,
+ dav_resource *dst,
+ dav_response **response
+ );
+
+ /* Remove a resource. Handles both files and collections.
+ * Removes any associated properties as well.
+ * If an error occurs in a child resource, then the return value is
+ * non-NULL, and *response is set to a multistatus response.
+ * If the delete is successful, the resource object is updated to
+ * reflect that the resource no longer exists.
+ */
+ dav_error * (*remove_resource)(
+ dav_resource *resource,
+ dav_response **response
+ );
+
+ /* Walk a resource hierarchy.
+ *
+ * Iterates over the resource hierarchy specified by wctx->resource.
+ * Parameter for control of the walk and the callback are specified
+ * by wctx.
+ *
+ * An HTTP_* status code is returned if an error occurs during the
+ * walk or the callback indicates an error. OK is returned on success.
+ */
+ dav_error * (*walk)(dav_walker_ctx *wctx, int depth);
+
+ /* Get the entity tag for a resource */
+ const char * (*getetag)(const dav_resource *resource);
+};
+
+
+/* --------------------------------------------------------------------
+**
+** VERSIONING FUNCTIONS
+*/
+
+/* dav_get_target_selector:
+ *
+ * Returns any Target-Selector header in a request
+ * (used by versioning clients)
+ */
+const char *dav_get_target_selector(request_rec *r);
+
+/* Ensure that a resource is writable. If there is no versioning
+ * provider, then this is essentially a no-op. Versioning repositories
+ * require explicit resource creation and checkout before they can
+ * be written to. If a new resource is to be created, or an existing
+ * resource deleted, the parent collection must be checked out as well.
+ *
+ * Set the parent_only flag to only make the parent collection writable.
+ * Otherwise, both parent and child are made writable as needed. If the
+ * child does not exist, then a new versioned resource is created and
+ * checked out.
+ *
+ * The parent_resource and parent_was_writable arguments are optional
+ * (i.e. they may be NULL). If parent_only is set, then the
+ * resource_existed and resource_was_writable arguments are ignored.
+ *
+ * The previous states of the resources are returned, so they can be
+ * restored after the operation completes (see
+ * dav_revert_resource_writability())
+ */
+dav_error *dav_ensure_resource_writable(request_rec *r,
+ dav_resource *resource,
+ int parent_only,
+ dav_resource **parent_resource,
+ int *resource_existed,
+ int *resource_was_writable,
+ int *parent_was_writable);
+
+/* Revert the writability of resources back to what they were
+ * before they were modified. If undo == 0, then the resource
+ * modifications are maintained (i.e. they are checked in).
+ * If undo != 0, then resource modifications are discarded
+ * (i.e. they are unchecked out).
+ *
+ * The resource and parent_resource arguments are optional
+ * (i.e. they may be NULL).
+ */
+dav_error *dav_revert_resource_writability(request_rec *r,
+ dav_resource *resource,
+ dav_resource *parent_resource,
+ int undo,
+ int resource_existed,
+ int resource_was_writable,
+ int parent_was_writable);
+
+/* Versioning provider hooks */
+struct dav_hooks_vsn
+{
+ /* Return supported versioning level
+ * for the Versioning header
+ */
+ const char * (*get_vsn_header)(void);
+
+ /* Create a new (empty) resource. If successful,
+ * the resource object state is updated appropriately.
+ */
+ dav_error * (*mkresource)(dav_resource *resource);
+
+ /* Checkout a resource. If successful, the resource
+ * object state is updated appropriately.
+ */
+ dav_error * (*checkout)(dav_resource *resource);
+
+ /* Uncheckout a resource. If successful, the resource
+ * object state is updated appropriately.
+ */
+ dav_error * (*uncheckout)(dav_resource *resource);
+
+ /* Checkin a working resource. If successful, the resource
+ * object state is updated appropriately.
+ */
+ dav_error * (*checkin)(dav_resource *resource);
+
+ /* Determine whether a non-versioned (or non-existent) resource
+ * is versionable. Returns != 0 if resource can be versioned.
+ */
+ int (*versionable)(const dav_resource *resource);
+
+ /* Determine whether auto-versioning is enabled for a resource
+ * (which may not exist, or may not be versioned).
+ * Returns != 0 if auto-versioning is enabled.
+ */
+ int (*auto_version_enabled)(const dav_resource *resource);
+};
+
+
+/* --------------------------------------------------------------------
+**
+** MISCELLANEOUS STUFF
+*/
+
+/* allow providers access to the per-directory parameters */
+table *dav_get_dir_params(const request_rec *r);
+
+/* fetch the "LimitXMLRequestBody" in force for this resource */
+size_t dav_get_limit_xml_body(const request_rec *r);
+
+/* manage an array of unique URIs: dav_insert_uri() and DAV_GET_URI_ITEM() */
+
+/* return the URI's (existing) index, or insert it and return a new index */
+int dav_insert_uri(array_header *uri_array, const char *uri);
+#define DAV_GET_URI_ITEM(ary, i) (((const char * const *)(ary)->elts)[i])
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MOD_DAV_H_ */
diff --git a/modules/dav/main/props.c b/modules/dav/main/props.c
new file mode 100644
index 0000000000..d4ab203bdc
--- /dev/null
+++ b/modules/dav/main/props.c
@@ -0,0 +1,1525 @@
+/*
+** Copyright (C) 1998-2000 Greg Stein. All Rights Reserved.
+**
+** By using this file, you agree to the terms and conditions set forth in
+** the LICENSE.html file which can be found at the top level of the mod_dav
+** distribution or at http://www.webdav.org/mod_dav/license-1.html.
+**
+** Contact information:
+** Greg Stein, PO Box 760, Palo Alto, CA, 94302
+** gstein@lyra.org, http://www.webdav.org/mod_dav/
+*/
+
+/*
+** DAV extension module for Apache 1.3.*
+** - Property database handling (repository-independent)
+**
+** Written by Greg Stein, gstein@lyra.org, http://www.lyra.org/
+**
+** NOTES:
+**
+** PROPERTY DATABASE
+**
+** This version assumes that there is a per-resource database provider
+** to record properties. The database provider decides how and where to
+** store these databases.
+**
+** The DBM keys for the properties have the following form:
+**
+** namespace ":" propname
+**
+** For example: 5:author
+**
+** The namespace provides an integer index into the namespace table
+** (see below). propname is simply the property name, without a namespace
+** prefix.
+**
+** A special case exists for properties that had a prefix starting with
+** "xml". The XML Specification reserves these for future use. mod_dav
+** stores and retrieves them unchanged. The keys for these properties
+** have the form:
+**
+** ":" propname
+**
+** The propname will contain the prefix and the property name. For
+** example, a key might be ":xmlfoo:name"
+**
+** The ":name" style will also be used for properties that do not
+** exist within a namespace.
+**
+** The DBM values consist of two null-terminated strings, appended
+** together (the null-terms are retained and stored in the database).
+** The first string is the xml:lang value for the property. An empty
+** string signifies that a lang value was not in context for the value.
+** The second string is the property value itself.
+**
+**
+** NAMESPACE TABLE
+**
+** The namespace table is an array that lists each of the namespaces
+** that are in use by the properties in the given propdb. Each entry
+** in the array is a simple URI.
+**
+** For example: http://www.foo.bar/standards/props/
+**
+** The prefix used for the property is stripped and the URI for it
+** is entered into the namespace table. Also, any namespaces used
+** within the property value will be entered into the table (and
+** stripped from the child elements).
+**
+** The namespaces are stored in the DBM database under the "METADATA" key.
+**
+**
+** STRIPPING NAMESPACES
+**
+** Within the property values, the namespace declarations (xmlns...)
+** are stripped. Each element and attribute will have its prefix removed
+** and a new prefix inserted.
+**
+** This must be done so that we can return multiple properties in a
+** PROPFIND which may have (originally) used conflicting prefixes. For
+** that case, we must bind all property value elements to new namespace
+** values.
+**
+** This implies that clients must NOT be sensitive to the namespace
+** prefix used for their properties. It WILL change when the properties
+** are returned (we return them as "ns<index>", e.g. "ns5"). Also, the
+** property value can contain ONLY XML elements and CDATA. PI and comment
+** elements will be stripped. CDATA whitespace will be preserved, but
+** whitespace within element tags will be altered. Attribute ordering
+** may be altered. Element and CDATA ordering will be preserved.
+**
+**
+** ATTRIBUTES ON PROPERTY NAME ELEMENTS
+**
+** When getting/setting properties, the XML used looks like:
+**
+** <prop>
+** <propname1>value</propname1>
+** <propname2>value</propname1>
+** </prop>
+**
+** This implementation (mod_dav) DOES NOT save any attributes that are
+** associated with the <propname1> element. The property value is deemed
+** to be only the contents ("value" in the above example).
+**
+** We do store the xml:lang value (if any) that applies to the context
+** of the <propname1> element. Whether the xml:lang attribute is on
+** <propname1> itself, or from a higher level element, we will store it
+** with the property value.
+**
+**
+** VERSIONING
+**
+** The DBM db contains a key named "METADATA" that holds database-level
+** information, such as the namespace table. The record also contains the
+** db's version number as the very first 16-bit value. This first number
+** is actually stored as two single bytes: the first byte is a "major"
+** version number. The second byte is a "minor" number.
+**
+** If the major number is not what mod_dav expects, then the db is closed
+** immediately and an error is returned. A minor number change is
+** acceptable -- it is presumed that old/new dav_props.c can deal with
+** the database format. For example, a newer dav_props might update the
+** minor value and append information to the end of the metadata record
+** (which would be ignored by previous versions).
+**
+**
+** ISSUES:
+**
+** At the moment, for the dav_get_allprops() and dav_get_props() functions,
+** we must return a set of xmlns: declarations for ALL known namespaces
+** in the file. There isn't a way to filter this because we don't know
+** which are going to be used or not. Examining property names is not
+** sufficient because the property values could use entirely different
+** namespaces.
+**
+** ==> we must devise a scheme where we can "garbage collect" the namespace
+** entries from the property database.
+*/
+
+#include "mod_dav.h"
+
+#include "http_log.h"
+#include "http_request.h"
+
+/*
+** There is some rough support for writeable DAV:getcontenttype and
+** DAV:getcontentlanguage properties. If this #define is (1), then
+** this support is disabled.
+**
+** We are disabling it because of a lack of support in GET and PUT
+** operations. For GET, it would be "expensive" to look for a propdb,
+** open it, and attempt to extract the Content-Type and Content-Language
+** values for the response.
+** (Handling the PUT would not be difficult, though)
+*/
+#define DAV_DISABLE_WRITEABLE_PROPS 1
+
+#define DAV_GDBM_NS_KEY "METADATA"
+#define DAV_GDBM_NS_KEY_LEN 8
+
+#define DAV_EMPTY_VALUE "\0" /* TWO null terms */
+
+/* the namespace URI was not found; no ID is available */
+#define DAV_NS_ERROR_NOT_FOUND (DAV_NS_ERROR_BASE)
+
+typedef struct {
+ unsigned char major;
+#define DAV_DBVSN_MAJOR 4
+ /*
+ ** V4 -- 0.9.9 ..
+ ** Prior versions could have keys or values with invalid
+ ** namespace prefixes as a result of the xmlns="" form not
+ ** resetting the default namespace to be "no namespace". The
+ ** namespace would be set to "" which is invalid; it should
+ ** be set to "no namespace".
+ **
+ ** V3 -- 0.9.8
+ ** Prior versions could have values with invalid namespace
+ ** prefixes due to an incorrect mapping of input to propdb
+ ** namespace indices. Version bumped to obsolete the old
+ ** values.
+ **
+ ** V2 -- 0.9.7
+ ** This introduced the xml:lang value into the property value's
+ ** record in the propdb.
+ **
+ ** V1 -- .. 0.9.6
+ ** Initial version.
+ */
+
+
+ unsigned char minor;
+#define DAV_DBVSN_MINOR 0
+
+ short ns_count;
+
+} dav_propdb_metadata;
+
+struct dav_propdb {
+ int version; /* *minor* version of this db */
+
+ pool *p; /* the pool we should use */
+ request_rec *r; /* the request record */
+
+ dav_resource *resource; /* the target resource */
+
+ int deferred; /* open of db has been deferred */
+ dav_db *db; /* underlying database containing props */
+
+ dav_buffer ns_table; /* table of namespace URIs */
+ short ns_count; /* number of entries in table */
+ int ns_table_dirty; /* ns_table was modified */
+
+ array_header *ns_xlate; /* translation of an elem->ns to URI */
+ int *ns_map; /* map elem->ns to propdb ns values */
+ int incomplete_map; /* some mappings do not exist */
+
+ dav_lockdb *lockdb; /* the lock database */
+
+ dav_buffer wb_key; /* work buffer for dav_gdbm_key */
+ dav_buffer wb_lock; /* work buffer for lockdiscovery property */
+
+ /* if we ever run a GET subreq, it will be stored here */
+ request_rec *subreq;
+
+ /* hooks we should use for processing (based on the target resource) */
+ const dav_hooks_db *db_hooks;
+ const dav_hooks_vsn *vsn_hooks;
+
+ const dav_dyn_hooks *liveprop; /* head of list */
+};
+
+/* ### move these into a "core" liveprop provider? */
+static const char * const dav_core_props[] =
+{
+ "getcontenttype",
+ "getcontentlanguage",
+ "lockdiscovery",
+ "resourcetype",
+ "supportedlock",
+
+ NULL /* sentinel */
+};
+enum {
+ DAV_PROPID_CORE_getcontenttype = DAV_PROPID_CORE,
+ DAV_PROPID_CORE_getcontentlanguage,
+ DAV_PROPID_CORE_lockdiscovery,
+ DAV_PROPID_CORE_resourcetype,
+ DAV_PROPID_CORE_supportedlock,
+
+ DAV_PROPID_CORE_UNKNOWN
+};
+#define DAV_IS_CORE_PROP(propid) ((propid) >= DAV_PROPID_CORE && \
+ (propid) <= DAV_PROPID_CORE_UNKNOWN)
+
+/*
+** This structure is used to track information needed for a rollback.
+** If a SET was performed and no prior value existed, then value.dptr
+** will be NULL.
+*/
+typedef struct dav_rollback_item {
+ dav_datum key; /* key for the item being saved */
+ dav_datum value; /* value before set/replace/delete */
+
+ /* or use the following (choice selected by dav_prop_ctx.is_liveprop) */
+ struct dav_liveprop_rollback *liveprop; /* liveprop rollback ctx */
+
+} dav_rollback_item;
+
+
+#if 0
+/* ### unused */
+static const char *dav_get_ns_table_uri(dav_propdb *propdb, int ns)
+{
+ const char *p = propdb->ns_table.buf + sizeof(dav_propdb_metadata);
+
+ while (ns--)
+ p += strlen(p) + 1;
+
+ return p;
+}
+#endif
+
+static void dav_find_liveprop(dav_propdb *propdb, dav_xml_elem *elem)
+{
+ int propid;
+ const char *ns_uri;
+ const dav_dyn_hooks *ddh;
+
+ if (elem->ns == DAV_NS_DAV_ID) {
+ const char * const *p = dav_core_props;
+
+ for (propid = DAV_PROPID_CORE; *p != NULL; ++p, ++propid)
+ if (strcmp(elem->name, *p) == 0) {
+ elem->propid = propid;
+ return;
+ }
+
+ /* didn't find it. fall thru. a provider can define DAV: props */
+ }
+ else if (elem->ns == DAV_NS_NONE) {
+ /* policy: liveprop providers cannot define no-namespace properties */
+ elem->propid = DAV_PROPID_CORE_UNKNOWN;
+ return;
+ }
+
+ ns_uri = DAV_GET_URI_ITEM(propdb->ns_xlate, elem->ns);
+
+ for (ddh = propdb->liveprop; ddh != NULL; ddh = ddh->next) {
+ propid = (*DAV_AS_HOOKS_LIVEPROP(ddh)->find_prop)(ns_uri, elem->name);
+ if (propid != 0) {
+ elem->propid = propid;
+ elem->provider = DAV_AS_HOOKS_LIVEPROP(ddh);
+ elem->ns_map = ddh->ctx.ns_map;
+ return;
+ }
+ }
+
+ elem->propid = DAV_PROPID_CORE_UNKNOWN;
+}
+
+/* is the live property read/write? */
+static int dav_rw_liveprop(dav_propdb *propdb, int propid)
+{
+ dav_prop_rw rw;
+ const dav_dyn_hooks *ddh;
+
+ /* these are defined as read-only */
+ if (propid == DAV_PROPID_CORE_lockdiscovery
+ || propid == DAV_PROPID_CORE_resourcetype
+#if DAV_DISABLE_WRITEABLE_PROPS
+ || propid == DAV_PROPID_CORE_getcontenttype
+ || propid == DAV_PROPID_CORE_getcontentlanguage
+#endif
+ || propid == DAV_PROPID_CORE_supportedlock) {
+
+ return 0;
+ }
+
+ /* these are defined as read/write */
+ if (propid == DAV_PROPID_CORE_getcontenttype
+ || propid == DAV_PROPID_CORE_getcontentlanguage
+ || propid == DAV_PROPID_CORE_UNKNOWN) {
+
+ return 1;
+ }
+
+ /*
+ ** Check the liveprop providers
+ */
+ for (ddh = propdb->liveprop; ddh != NULL; ddh = ddh->next) {
+ rw = (*DAV_AS_HOOKS_LIVEPROP(ddh)->is_writeable)(propdb->resource,
+ propid);
+ if (rw == DAV_PROP_RW_YES)
+ return 1;
+ if (rw == DAV_PROP_RW_NO)
+ return 0;
+ }
+
+ /*
+ ** No provider recognized the property, so it must be dead (and writable)
+ */
+ return 1;
+}
+
+/* do a sub-request to fetch properties for the target resource's URI. */
+static void dav_do_prop_subreq(dav_propdb *propdb)
+{
+ /* perform a "GET" on the resource's URI (note that the resource
+ may not correspond to the current request!). */
+ propdb->subreq = ap_sub_req_lookup_uri(propdb->resource->uri, propdb->r);
+}
+
+static dav_error * dav_insert_coreprop(dav_propdb *propdb,
+ int propid, const char *name,
+ int getvals,
+ dav_text_header *phdr,
+ int *inserted)
+{
+ const char *value = NULL;
+
+ *inserted = 0;
+
+ /* fast-path the common case */
+ if (propid == DAV_PROPID_CORE_UNKNOWN)
+ return NULL;
+
+ switch (propid) {
+
+ case DAV_PROPID_CORE_resourcetype:
+ switch (propdb->resource->type) {
+ case DAV_RESOURCE_TYPE_REGULAR:
+ if (propdb->resource->collection) {
+ value = "<D:collection/>";
+ }
+ else {
+ /* ### should we denote lock-null resources? */
+
+ value = ""; /* becomes: <D:resourcetype/> */
+ }
+ break;
+ case DAV_RESOURCE_TYPE_HISTORY:
+ value = "<D:history/>";
+ break;
+ case DAV_RESOURCE_TYPE_WORKSPACE:
+ value = "<D:workspace/>";
+ break;
+ case DAV_RESOURCE_TYPE_ACTIVITY:
+ value = "<D:activity/>";
+ break;
+ case DAV_RESOURCE_TYPE_CONFIGURATION:
+ value = "<D:configuration/>";
+ break;
+ case DAV_RESOURCE_TYPE_REVISION:
+ value = "<D:revision/>";
+ break;
+
+ default:
+ /* ### bad juju */
+ break;
+ }
+ break;
+
+ case DAV_PROPID_CORE_lockdiscovery:
+ if (propdb->lockdb != NULL) {
+ dav_error *err;
+ dav_lock *locks;
+
+ if ((err = dav_lock_query(propdb->lockdb, propdb->resource,
+ &locks)) != NULL) {
+ return dav_push_error(propdb->p, err->status, 0,
+ "DAV:lockdiscovery could not be "
+ "determined due to a problem fetching "
+ "the locks for this resource.",
+ err);
+ }
+
+ /* fast-path the no-locks case */
+ if (locks == NULL) {
+ value = "";
+ }
+ else {
+ /*
+ ** This may modify the buffer. value may point to
+ ** wb_lock.pbuf or a string constant.
+ */
+ value = dav_lock_get_activelock(propdb->r, locks,
+ &propdb->wb_lock);
+
+ /* make a copy to isolate it from changes to wb_lock */
+ value = ap_pstrdup(propdb->p, propdb->wb_lock.buf);
+ }
+ }
+ break;
+
+ case DAV_PROPID_CORE_supportedlock:
+ if (propdb->lockdb != NULL) {
+ value = (*propdb->lockdb->hooks->get_supportedlock)();
+ }
+ break;
+
+ case DAV_PROPID_CORE_getcontenttype:
+ if (propdb->subreq == NULL) {
+ dav_do_prop_subreq(propdb);
+ }
+ if (propdb->subreq->content_type != NULL) {
+ value = propdb->subreq->content_type;
+ }
+ break;
+
+ case DAV_PROPID_CORE_getcontentlanguage:
+ {
+ const char *lang;
+
+ if (propdb->subreq == NULL) {
+ dav_do_prop_subreq(propdb);
+ }
+ if ((lang = ap_table_get(propdb->subreq->headers_out,
+ "Content-Language")) != NULL) {
+ value = lang;
+ }
+ break;
+ }
+
+ case DAV_PROPID_CORE_UNKNOWN:
+ default:
+ /* fall through to interpret as a dead property */
+ break;
+ }
+
+ /* if something was supplied, then insert it */
+ if (value != NULL) {
+ const char *s;
+
+ if (getvals && *value != '\0') {
+ /* use D: prefix to refer to the DAV: namespace URI */
+ s = ap_psprintf(propdb->p, "<D:%s>%s</D:%s>" DEBUG_CR,
+ name, value, name);
+ }
+ else {
+ /* use D: prefix to refer to the DAV: namespace URI */
+ s = ap_psprintf(propdb->p, "<D:%s/>" DEBUG_CR, name);
+ }
+ dav_text_append(propdb->p, phdr, s);
+
+ *inserted = 1;
+ }
+
+ return NULL;
+}
+
+static dav_error * dav_insert_liveprop(dav_propdb *propdb,
+ const dav_xml_elem *elem,
+ int getvals,
+ dav_text_header *phdr,
+ int *inserted)
+{
+ dav_prop_insert pi;
+
+ *inserted = 0;
+
+ if (DAV_IS_CORE_PROP(elem->propid))
+ return dav_insert_coreprop(propdb, elem->propid, elem->name,
+ getvals, phdr, inserted);
+
+ /* ask the provider (that defined this prop) to insert the prop */
+ pi = (*elem->provider->insert_prop)(propdb->resource, elem->propid,
+ getvals, elem->ns_map, phdr);
+#if DAV_DEBUG
+ if (pi == DAV_PROP_INSERT_NOTME) {
+ /* ### the provider should have returned NOTDEF, at least */
+ return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "DESIGN ERROR: a liveprop provider defined "
+ "a property, but did not respond to the "
+ "insert_prop hook for it.");
+ }
+#endif
+
+ if (pi != DAV_PROP_INSERT_NOTDEF)
+ *inserted = 1;
+
+ return NULL;
+}
+
+static void dav_append_prop(dav_propdb *propdb,
+ const char *name, const char *value,
+ dav_text_header *phdr)
+{
+ const char *s;
+ const char *lang = value;
+
+ /* skip past the xml:lang value */
+ value += strlen(lang) + 1;
+
+ if (*value == '\0') {
+ /* the property is an empty value */
+ if (*name == ':') {
+ /* "no namespace" case */
+ s = ap_psprintf(propdb->p, "<%s/>" DEBUG_CR, name+1);
+ }
+ else {
+ s = ap_psprintf(propdb->p, "<ns%s/>" DEBUG_CR, name);
+ }
+ }
+ else if (*lang != '\0') {
+ if (*name == ':') {
+ /* "no namespace" case */
+ s = ap_psprintf(propdb->p, "<%s xml:lang=\"%s\">%s</%s>" DEBUG_CR,
+ name+1, lang, value, name+1);
+ }
+ else {
+ s = ap_psprintf(propdb->p, "<ns%s xml:lang=\"%s\">%s</ns%s>" DEBUG_CR,
+ name, lang, value, name);
+ }
+ }
+ else if (*name == ':') {
+ /* "no namespace" case */
+ s = ap_psprintf(propdb->p, "<%s>%s</%s>" DEBUG_CR, name+1, value, name+1);
+ }
+ else {
+ s = ap_psprintf(propdb->p, "<ns%s>%s</ns%s>" DEBUG_CR, name, value, name);
+ }
+ dav_text_append(propdb->p, phdr, s);
+}
+
+/*
+** Prepare the ns_map variable in the propdb structure. This entails copying
+** all URIs from the "input" namespace list (in propdb->ns_xlate) into the
+** propdb's list of namespaces. As each URI is copied (or pre-existing
+** URI looked up), the index mapping is stored into the ns_map variable.
+**
+** Note: we must copy all declared namespaces because we cannot easily
+** determine which input namespaces were actually used within the property
+** values that are being stored within the propdb. Theoretically, we can
+** determine this at the point where we serialize the property values
+** back into strings. This would require a bit more work, and will be
+** left to future optimizations.
+**
+** ### we should always initialize the propdb namespace array with "DAV:"
+** ### since we know it will be entered anyhow (by virtue of it always
+** ### occurring in the ns_xlate array). That will allow us to use
+** ### DAV_NS_DAV_ID for propdb ns values, too.
+*/
+static void dav_prep_ns_map(dav_propdb *propdb, int add_ns)
+{
+ int i;
+ const char **puri;
+ const int orig_count = propdb->ns_count;
+ int *pmap;
+ int updating = 0; /* are we updating an existing ns_map? */
+
+ if (propdb->ns_map) {
+ if (add_ns && propdb->incomplete_map) {
+ /* we must revisit the map and insert new entries */
+ updating = 1;
+ propdb->incomplete_map = 0;
+ }
+ else {
+ /* nothing to do: we have a proper ns_map */
+ return;
+ }
+ }
+ else {
+ propdb->ns_map = ap_palloc(propdb->p, propdb->ns_xlate->nelts * sizeof(*propdb->ns_map));
+ }
+
+ pmap = propdb->ns_map;
+
+ /* ### stupid O(n * orig_count) algorithm */
+ for (i = propdb->ns_xlate->nelts, puri = (const char **)propdb->ns_xlate->elts;
+ i-- > 0;
+ ++puri, ++pmap) {
+
+ const char *uri = *puri;
+ const size_t uri_len = strlen(uri);
+
+ if (updating) {
+ /* updating an existing mapping... we can skip a lot of stuff */
+
+ if (*pmap != DAV_NS_ERROR_NOT_FOUND) {
+ /* This entry has been filled in, so we can skip it */
+ continue;
+ }
+ }
+ else {
+ int j;
+ size_t len;
+ const char *p;
+
+ /*
+ ** GIVEN: uri (a namespace URI from the request input)
+ **
+ ** FIND: an equivalent URI in the propdb namespace table
+ */
+
+ /* only scan original entries (we may have added some in here) */
+ for (p = propdb->ns_table.buf + sizeof(dav_propdb_metadata),
+ j = 0;
+ j < orig_count;
+ ++j, p += len + 1) {
+
+ len = strlen(p);
+
+ if (uri_len != len)
+ continue;
+ if (memcmp(uri, p, len) == 0) {
+ *pmap = j;
+ goto next_input_uri;
+ }
+ }
+
+ if (!add_ns) {
+ *pmap = DAV_NS_ERROR_NOT_FOUND;
+
+ /*
+ ** This flag indicates that we have an ns_map with missing
+ ** entries. If dav_prep_ns_map() is called with add_ns==1 AND
+ ** this flag is set, then we zip thru the array and add those
+ ** URIs (effectively updating the ns_map as if add_ns=1 was
+ ** passed when the initial prep was called).
+ */
+ propdb->incomplete_map = 1;
+
+ continue;
+ }
+ }
+
+ /*
+ ** The input URI was not found in the propdb namespace table, and
+ ** we are supposed to add it. Append it to the table and store
+ ** the index into the ns_map.
+ */
+ dav_check_bufsize(propdb->p, &propdb->ns_table, uri_len + 1);
+ memcpy(propdb->ns_table.buf + propdb->ns_table.cur_len, uri, uri_len + 1);
+ propdb->ns_table.cur_len += uri_len + 1;
+
+ propdb->ns_table_dirty = 1;
+
+ *pmap = propdb->ns_count++;
+
+ next_input_uri:
+ ;
+ }
+}
+
+/* find the "DAV:" namespace in our table and return its ID. */
+static int dav_find_dav_id(dav_propdb *propdb)
+{
+ const char *p = propdb->ns_table.buf + sizeof(dav_propdb_metadata);
+ int ns;
+
+ for (ns = 0; ns < propdb->ns_count; ++ns) {
+ size_t len = strlen(p);
+
+ if (len == 4 && memcmp(p, "DAV:", 5) == 0)
+ return ns;
+ p += len + 1;
+ }
+
+ /* the "DAV:" namespace is not present */
+ return -1;
+}
+
+static void dav_insert_xmlns(pool *p, const char *pre_prefix, int ns,
+ const char *ns_uri, dav_text_header *phdr)
+{
+ const char *s;
+
+ s = ap_psprintf(p, " xmlns:%s%d=\"%s\"", pre_prefix, ns, ns_uri);
+ dav_text_append(p, phdr, s);
+}
+
+/* return all known namespaces (in this propdb) */
+static void dav_get_propdb_xmlns(dav_propdb *propdb, dav_text_header *phdr)
+{
+ int i;
+ const char *p = propdb->ns_table.buf + sizeof(dav_propdb_metadata);
+ size_t len;
+
+ /* note: ns_count == 0 when we have no propdb file */
+ for (i = 0; i < propdb->ns_count; ++i, p += len + 1) {
+
+ len = strlen(p);
+
+ dav_insert_xmlns(propdb->p, "ns", i, p, phdr);
+ }
+}
+
+/* add a namespace decl from one of the namespace tables */
+static void dav_add_marked_xmlns(dav_propdb *propdb, char *marks, int ns,
+ array_header *ns_table,
+ const char *pre_prefix,
+ dav_text_header *phdr)
+{
+ if (marks[ns])
+ return;
+ marks[ns] = 1;
+
+ dav_insert_xmlns(propdb->p,
+ pre_prefix, ns, DAV_GET_URI_ITEM(ns_table, ns),
+ phdr);
+}
+
+/*
+** Internal function to build a key
+**
+** WARNING: returns a pointer to a "static" buffer holding the key. The
+** value must be copied or no longer used if this function is
+** called again.
+*/
+static dav_datum dav_gdbm_key(dav_propdb *propdb, const dav_xml_elem *elem)
+{
+ int ns;
+ char nsbuf[20];
+ size_t l_ns;
+ size_t l_name = strlen(elem->name);
+ dav_datum key = { 0 };
+
+ /*
+ * Convert namespace ID to a string. "no namespace" is an empty string,
+ * so the keys will have the form ":name". Otherwise, the keys will
+ * have the form "#:name".
+ */
+ if (elem->ns == DAV_NS_NONE) {
+ nsbuf[0] = '\0';
+ l_ns = 0;
+ }
+ else {
+ if (propdb->ns_map == NULL) {
+ /*
+ * Note that we prep the map and do NOT add namespaces. If that
+ * is required, then the caller should have called prep
+ * beforehand, passing the correct values.
+ */
+ dav_prep_ns_map(propdb, 0);
+ }
+
+ ns = propdb->ns_map[elem->ns];
+ if (DAV_NS_IS_ERROR(ns))
+ return key; /* zeroed */
+
+ l_ns = sprintf(nsbuf, "%d", ns);
+ }
+
+ /* assemble: #:name */
+ dav_set_bufsize(propdb->p, &propdb->wb_key, l_ns + 1 + l_name + 1);
+ memcpy(propdb->wb_key.buf, nsbuf, l_ns);
+ propdb->wb_key.buf[l_ns] = ':';
+ memcpy(&propdb->wb_key.buf[l_ns + 1], elem->name, l_name + 1);
+
+ /* build the database key */
+ key.dsize = l_ns + 1 + l_name + 1;
+ key.dptr = propdb->wb_key.buf;
+
+ return key;
+}
+
+dav_error *dav_really_open_db(dav_propdb *propdb, int ro)
+{
+ dav_error *err;
+ dav_datum key;
+ dav_datum value = { 0 };
+
+ /* we're trying to open the db; turn off the 'deferred' flag */
+ propdb->deferred = 0;
+
+ /* ask the DB provider to open the thing */
+ err = (*propdb->db_hooks->open)(propdb->p, propdb->resource, ro,
+ &propdb->db);
+ if (err != NULL) {
+ return dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
+ DAV_ERR_PROP_OPENING,
+ "Could not open the property database.",
+ err);
+ }
+
+ /*
+ ** NOTE: propdb->db could be NULL if we attempted to open a readonly
+ ** database that doesn't exist. If we require read/write
+ ** access, then a database was created and opened.
+ */
+
+ if (propdb->db != NULL) {
+ key.dptr = DAV_GDBM_NS_KEY;
+ key.dsize = DAV_GDBM_NS_KEY_LEN;
+ if ((err = (*propdb->db_hooks->fetch)(propdb->db, key,
+ &value)) != NULL) {
+ /* ### push a higher-level description? */
+ return err;
+ }
+ }
+ if (value.dptr == NULL) {
+ dav_propdb_metadata m = {
+ DAV_DBVSN_MAJOR, DAV_DBVSN_MINOR, 0
+ };
+
+ if (propdb->db != NULL) {
+ /*
+ * If there is no METADATA key, then the database may be
+ * from versions 0.9.0 .. 0.9.4 (which would be incompatible).
+ * These can be identified by the presence of an NS_TABLE entry.
+ */
+ key.dptr = "NS_TABLE";
+ key.dsize = 8;
+ if ((*propdb->db_hooks->exists)(propdb->db, key)) {
+ (*propdb->db_hooks->close)(propdb->db);
+
+ /* call it a major version error */
+ return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
+ DAV_ERR_PROP_BAD_MAJOR,
+ "Prop database has the wrong major "
+ "version number and cannot be used.");
+ }
+ }
+
+ /* initialize a new metadata structure */
+ dav_set_bufsize(propdb->p, &propdb->ns_table, sizeof(m));
+ memcpy(propdb->ns_table.buf, &m, sizeof(m));
+ }
+ else {
+ dav_propdb_metadata m;
+
+ dav_set_bufsize(propdb->p, &propdb->ns_table, value.dsize);
+ memcpy(propdb->ns_table.buf, value.dptr, value.dsize);
+
+ memcpy(&m, value.dptr, sizeof(m));
+ if (m.major != DAV_DBVSN_MAJOR) {
+ (*propdb->db_hooks->close)(propdb->db);
+
+ return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
+ DAV_ERR_PROP_BAD_MAJOR,
+ "Prop database has the wrong major "
+ "version number and cannot be used.");
+ }
+ propdb->version = m.minor;
+ propdb->ns_count = ntohs(m.ns_count);
+
+ (*propdb->db_hooks->freedatum)(propdb->db, value);
+ }
+
+ return NULL;
+}
+
+dav_error *dav_open_propdb(request_rec *r, dav_lockdb *lockdb,
+ dav_resource *resource,
+ int ro,
+ array_header * ns_xlate,
+ dav_propdb **p_propdb)
+{
+ dav_propdb *propdb = ap_pcalloc(r->pool, sizeof(*propdb));
+ dav_error *err;
+
+ *p_propdb = NULL;
+
+#if DAV_DEBUG
+ if (resource->uri == NULL) {
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "INTERNAL DESIGN ERROR: resource must define "
+ "its URI.");
+ }
+#endif
+
+ propdb->version = DAV_DBVSN_MINOR;
+ propdb->r = r;
+ propdb->p = r->pool; /* ### get rid of this */
+ propdb->resource = resource;
+ propdb->ns_xlate = ns_xlate;
+
+ propdb->db_hooks = DAV_GET_HOOKS_PROPDB(r);
+ propdb->vsn_hooks = DAV_GET_HOOKS_VSN(r);
+
+ propdb->liveprop = dav_get_provider_hooks(r, DAV_DYN_TYPE_LIVEPROP);
+
+ propdb->lockdb = lockdb;
+
+ if (!ro) {
+ propdb->deferred = 1;
+ }
+ else if ((err = dav_really_open_db(propdb, 1 /* ro */)) != NULL) {
+ return err;
+ }
+
+ /* ### what to do about closing the propdb on server failure? */
+
+ *p_propdb = propdb;
+ return NULL;
+}
+
+void dav_close_propdb(dav_propdb *propdb)
+{
+ if (propdb->db == NULL)
+ return;
+
+ if (propdb->ns_table_dirty) {
+ dav_propdb_metadata m;
+ dav_datum key;
+ dav_datum value;
+ dav_error *err;
+
+ key.dptr = DAV_GDBM_NS_KEY;
+ key.dsize = DAV_GDBM_NS_KEY_LEN;
+
+ value.dptr = propdb->ns_table.buf;
+ value.dsize = propdb->ns_table.cur_len;
+
+ /* fill in the metadata that we store into the prop db. */
+ m.major = DAV_DBVSN_MAJOR;
+ m.minor = propdb->version; /* ### keep current minor version? */
+ m.ns_count = htons(propdb->ns_count);
+
+ memcpy(propdb->ns_table.buf, &m, sizeof(m));
+
+ err = (*propdb->db_hooks->store)(propdb->db, key, value);
+ /* ### what to do with the error? */
+ }
+
+ (*propdb->db_hooks->close)(propdb->db);
+}
+
+dav_get_props_result dav_get_allprops(dav_propdb *propdb, int getvals)
+{
+ const dav_hooks_db *db_hooks = propdb->db_hooks;
+ dav_text_header hdr = { 0 };
+ dav_text_header hdr_ns = { 0 };
+ dav_get_props_result result = { 0 };
+ int found_resourcetype = 0;
+ int found_contenttype = 0;
+ int found_contentlang = 0;
+ int unused_inserted;
+ int i;
+ const char * const * scan_uri;
+ const dav_dyn_hooks *ddh;
+
+ /* generate all the namespaces that are in the propdb */
+ dav_get_propdb_xmlns(propdb, &hdr_ns);
+
+ /* initialize the result with some start tags... */
+ dav_text_append(propdb->p, &hdr,
+ "<D:propstat>" DEBUG_CR
+ "<D:prop>" DEBUG_CR);
+
+ /* if there ARE properties, then scan them */
+ if (propdb->db != NULL) {
+ dav_datum key;
+ int dav_id = dav_find_dav_id(propdb);
+
+ (void) (*db_hooks->firstkey)(propdb->db, &key);
+ while (key.dptr) {
+ dav_datum prevkey;
+
+ /* any keys with leading capital letters should be skipped
+ (real keys start with a number or a colon) */
+ if (*key.dptr >= 'A' && *key.dptr <= 'Z')
+ goto next_key;
+
+ /*
+ ** See if this is the <DAV:resourcetype> property. We need to
+ ** know whether it was found (and therefore, whether to supply
+ ** a default later).
+ **
+ ** We also look for <DAV:getcontenttype> and
+ ** <DAV:getcontentlanguage>. If they are not stored as dead
+ ** properties, then we need to perform a subrequest to get
+ ** their values (if any).
+ */
+ if (dav_id != -1
+ && *key.dptr != ':'
+ && dav_id == atoi(key.dptr)) {
+
+ const char *colon;
+
+ /* find the colon */
+ if ( key.dptr[1] == ':' ) {
+ colon = key.dptr + 1;
+ }
+ else {
+ colon = strchr(key.dptr + 2, ':');
+ }
+
+ if (colon[1] == 'r'
+ && strcmp(colon + 1, "resourcetype") == 0) {
+
+ found_resourcetype = 1;
+ }
+ else if (colon[1] == 'g') {
+ if (strcmp(colon + 1, "getcontenttype") == 0) {
+ found_contenttype = 1;
+ }
+ else if (strcmp(colon + 1, "getcontentlanguage") == 0) {
+ found_contentlang = 1;
+ }
+ }
+ }
+
+ if (getvals) {
+ dav_datum value;
+
+ (void) (*db_hooks->fetch)(propdb->db, key, &value);
+ if (value.dptr == NULL) {
+ /* ### anything better to do? */
+ /* ### probably should enter a 500 error */
+ goto next_key;
+ }
+
+ /* put the prop name and value into the result */
+ dav_append_prop(propdb, key.dptr, value.dptr, &hdr);
+
+ (*db_hooks->freedatum)(propdb->db, value);
+ }
+ else {
+ /* simple, empty element if a value isn't needed */
+ dav_append_prop(propdb, key.dptr, DAV_EMPTY_VALUE, &hdr);
+ }
+
+ next_key:
+ prevkey = key;
+ (void) (*db_hooks->nextkey)(propdb->db, &key);
+ (*db_hooks->freedatum)(propdb->db, prevkey);
+ }
+ }
+
+ /* add namespaces for all the liveprop providers */
+ for (i = 0, scan_uri = (const char * const *)dav_liveprop_uris->elts;
+ i < dav_liveprop_uris->nelts;
+ ++i, ++scan_uri)
+ dav_insert_xmlns(propdb->p, "lp", i, *scan_uri, &hdr_ns);
+
+ /* ask the liveprop providers to insert their properties */
+ for (ddh = propdb->liveprop; ddh != NULL; ddh = ddh->next) {
+ (*DAV_AS_HOOKS_LIVEPROP(ddh)->insert_all)(propdb->resource, getvals,
+ ddh->ctx.ns_map,
+ &hdr);
+ }
+
+ /* insert the standard properties */
+ /* ### should be handling the return errors here */
+ (void)dav_insert_coreprop(propdb,
+ DAV_PROPID_CORE_supportedlock, "supportedlock",
+ getvals, &hdr, &unused_inserted);
+ (void)dav_insert_coreprop(propdb,
+ DAV_PROPID_CORE_lockdiscovery, "lockdiscovery",
+ getvals, &hdr, &unused_inserted);
+
+ /* if the resourcetype wasn't stored, then prepare one */
+ if (!found_resourcetype) {
+ /* ### should be handling the return error here */
+ (void)dav_insert_coreprop(propdb,
+ DAV_PROPID_CORE_resourcetype, "resourcetype",
+ getvals, &hdr, &unused_inserted);
+ }
+
+ /* if we didn't find these, then do the whole subreq thing. */
+ if (!found_contenttype) {
+ /* ### should be handling the return error here */
+ (void)dav_insert_coreprop(propdb,
+ DAV_PROPID_CORE_getcontenttype,
+ "getcontenttype",
+ getvals, &hdr, &unused_inserted);
+ }
+ if (!found_contentlang) {
+ /* ### should be handling the return error here */
+ (void)dav_insert_coreprop(propdb,
+ DAV_PROPID_CORE_getcontentlanguage,
+ "getcontentlanguage",
+ getvals, &hdr, &unused_inserted);
+ }
+
+ /* terminate the result */
+ dav_text_append(propdb->p, &hdr,
+ "</D:prop>" DEBUG_CR
+ "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
+ "</D:propstat>" DEBUG_CR);
+
+ result.propstats = hdr.first;
+ result.xmlns = hdr_ns.first;
+ return result;
+}
+
+dav_get_props_result dav_get_props(dav_propdb *propdb, dav_xml_doc *doc)
+{
+ const dav_hooks_db *db_hooks = propdb->db_hooks;
+ dav_xml_elem *elem = dav_find_child(doc->root, "prop");
+ dav_text_header hdr_good = { 0 };
+ dav_text_header hdr_bad = { 0 };
+ dav_text_header hdr_ns = { 0 };
+ int have_good = 0;
+ dav_get_props_result result = { 0 };
+ char *marks_input;
+ char *marks_liveprop;
+
+ /* ### NOTE: we should pass in TWO buffers -- one for keys, one for
+ the marks */
+
+ /* we will ALWAYS provide a "good" result, even if it is EMPTY */
+ dav_text_append(propdb->p, &hdr_good,
+ "<D:propstat>" DEBUG_CR
+ "<D:prop>" DEBUG_CR);
+
+ /* generate all the namespaces that are in the propdb */
+ dav_get_propdb_xmlns(propdb, &hdr_ns);
+
+ /* ### the marks should be in a buffer! */
+ /* allocate zeroed-memory for the marks. These marks indicate which
+ input namespaces we've generated into the output xmlns buffer */
+ marks_input = ap_pcalloc(propdb->p, propdb->ns_xlate->nelts);
+
+ /* same for the liveprops */
+ marks_liveprop = ap_pcalloc(propdb->p, dav_liveprop_uris->nelts);
+
+ for (elem = elem->first_child; elem; elem = elem->next) {
+ dav_datum key;
+ dav_datum value = { 0 };
+
+ /*
+ ** Note: the key may be NULL if we have no properties that are in
+ ** a namespace that matches the requested prop's namespace.
+ */
+ key = dav_gdbm_key(propdb, elem);
+
+ /* fetch IF we have a db and a key. otherwise, value is NULL */
+ if (propdb->db != NULL && key.dptr != NULL) {
+ (void) (*db_hooks->fetch)(propdb->db, key, &value);
+ }
+
+ /*
+ ** If we did not find the property in the database, then it may
+ ** be a liveprop that we can handle specially.
+ */
+ if (value.dptr == NULL) {
+ dav_error *err;
+ int inserted;
+
+ /* cache the propid; dav_get_props() could be called many times */
+ if (elem->propid == 0)
+ dav_find_liveprop(propdb, elem);
+
+ /* insert the property. returns 1 if an insertion was done. */
+ if ((err = dav_insert_liveprop(propdb, elem, 1, &hdr_good,
+ &inserted)) != NULL) {
+ /* ### need to propagate the error to the caller... */
+ /* ### skip it for now, as if nothing was inserted */
+ }
+ if (inserted) {
+ have_good = 1;
+
+ /*
+ ** Add the liveprop's namespace URIs. Note that provider==NULL
+ ** for core properties.
+ */
+ if (elem->provider != NULL) {
+ const char * const * scan_ns_uri;
+ const int * scan_ns;
+
+ for (scan_ns_uri = elem->provider->namespace_uris,
+ scan_ns = elem->ns_map;
+ *scan_ns_uri != NULL;
+ ++scan_ns_uri, ++scan_ns) {
+
+ dav_add_marked_xmlns(propdb, marks_liveprop, *scan_ns,
+ dav_liveprop_uris, "lp", &hdr_ns);
+ }
+ }
+
+ continue;
+ }
+ }
+
+ if (value.dptr == NULL) {
+ /* not found. add a record to the "bad" propstats */
+
+ /* make sure we've started our "bad" propstat */
+ if (hdr_bad.first == NULL) {
+ dav_text_append(propdb->p, &hdr_bad,
+ "<D:propstat>" DEBUG_CR
+ "<D:prop>" DEBUG_CR);
+ }
+
+ /* note: key.dptr may be NULL if the propdb doesn't have an
+ equivalent namespace stored */
+ if (key.dptr == NULL) {
+ const char *s;
+
+ if (elem->ns == DAV_NS_NONE) {
+ /*
+ * elem has a prefix already (xml...:name) or the elem
+ * simply has no namespace.
+ */
+ s = ap_psprintf(propdb->p, "<%s/>" DEBUG_CR, elem->name);
+ }
+ else {
+ /* ensure that an xmlns is generated for the
+ input namespace */
+ dav_add_marked_xmlns(propdb, marks_input, elem->ns,
+ propdb->ns_xlate, "i", &hdr_ns);
+ s = ap_psprintf(propdb->p, "<i%d:%s/>" DEBUG_CR,
+ elem->ns, elem->name);
+ }
+ dav_text_append(propdb->p, &hdr_bad, s);
+ }
+ else {
+ /* add in the bad prop using our namespace decl */
+ dav_append_prop(propdb, key.dptr, DAV_EMPTY_VALUE, &hdr_bad);
+ }
+ }
+ else {
+ /* found it. put the value into the "good" propstats */
+
+ have_good = 1;
+
+ dav_append_prop(propdb, key.dptr, value.dptr, &hdr_good);
+
+ (*db_hooks->freedatum)(propdb->db, value);
+ }
+ }
+
+ dav_text_append(propdb->p, &hdr_good,
+ "</D:prop>" DEBUG_CR
+ "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
+ "</D:propstat>" DEBUG_CR);
+
+ /* default to start with the good */
+ result.propstats = hdr_good.first;
+
+ /* we may not have any "bad" results */
+ if (hdr_bad.first != NULL) {
+ dav_text_append(propdb->p, &hdr_bad,
+ "</D:prop>" DEBUG_CR
+ "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR
+ "</D:propstat>" DEBUG_CR);
+
+ /* if there are no good props, then just return the bad */
+ if (!have_good) {
+ result.propstats = hdr_bad.first;
+ }
+ else {
+ /* hook the bad propstat to the end of the good one */
+ hdr_good.last->next = hdr_bad.first;
+ }
+ }
+
+ result.xmlns = hdr_ns.first;
+ return result;
+}
+
+void dav_prop_validate(dav_prop_ctx *ctx)
+{
+ dav_propdb *propdb = ctx->propdb;
+ dav_xml_elem *prop = ctx->prop;
+
+ /*
+ ** Check to see if this is a live property, and fill the fields
+ ** in the XML elem, as appropriate.
+ **
+ ** Verify that the property is read/write. If not, then it cannot
+ ** be SET or DELETEd.
+ */
+ if (prop->propid == 0) {
+ dav_find_liveprop(propdb, prop);
+
+ /* it's a liveprop if a provider was found */
+ /* ### actually the "core" props should really be liveprops, but
+ ### there is no "provider" for those and the r/w props are
+ ### treated as dead props anyhow */
+ ctx->is_liveprop = prop->provider != NULL;
+ }
+
+ if (!dav_rw_liveprop(propdb, prop->propid)) {
+ ctx->err = dav_new_error(propdb->p, HTTP_CONFLICT,
+ DAV_ERR_PROP_READONLY,
+ "Property is read-only.");
+ return;
+ }
+
+ if (ctx->is_liveprop) {
+ int defer_to_dead = 0;
+
+ ctx->err = (*prop->provider->patch_validate)(propdb->resource,
+ prop, ctx->operation,
+ &ctx->liveprop_ctx,
+ &defer_to_dead);
+ if (ctx->err != NULL || !defer_to_dead)
+ return;
+
+ /* clear is_liveprop -- act as a dead prop now */
+ ctx->is_liveprop = 0;
+ }
+
+ /*
+ ** The property is supposed to be stored into the dead-property
+ ** database. Make sure the thing is truly open (and writeable).
+ */
+ if (propdb->deferred
+ && (ctx->err = dav_really_open_db(propdb, 0 /* ro */)) != NULL) {
+ return;
+ }
+
+ /*
+ ** There should be an open, writable database in here!
+ **
+ ** Note: the database would be NULL if it was opened readonly and it
+ ** did not exist.
+ */
+ if (propdb->db == NULL) {
+ ctx->err = dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
+ DAV_ERR_PROP_NO_DATABASE,
+ "Attempted to set/remove a property "
+ "without a valid, open, read/write "
+ "property database.");
+ return;
+ }
+
+ if (ctx->operation == DAV_PROP_OP_SET) {
+ /*
+ ** Prep the element => propdb namespace index mapping, inserting
+ ** namespace URIs into the propdb that don't exist.
+ */
+ dav_prep_ns_map(propdb, 1);
+ }
+ else if (ctx->operation == DAV_PROP_OP_DELETE) {
+ /*
+ ** There are no checks to perform here. If a property exists, then
+ ** we will delete it. If it does not exist, then it does not matter
+ ** (see S12.13.1).
+ **
+ ** Note that if a property does not exist, that does not rule out
+ ** that a SET will occur during this PROPPATCH (thusly creating it).
+ */
+ }
+}
+
+void dav_prop_exec(dav_prop_ctx *ctx)
+{
+ dav_propdb *propdb = ctx->propdb;
+ dav_error *err = NULL;
+ dav_rollback_item *rollback;
+
+ rollback = ap_pcalloc(propdb->p, sizeof(*rollback));
+ ctx->rollback = rollback;
+
+ if (ctx->is_liveprop) {
+ err = (*ctx->prop->provider->patch_exec)(propdb->resource,
+ ctx->prop, ctx->operation,
+ ctx->liveprop_ctx,
+ &ctx->rollback->liveprop);
+ }
+ else {
+ dav_datum key;
+
+ /* we're going to need the key for all operations */
+ key = dav_gdbm_key(propdb, ctx->prop);
+
+ /* save the old value so that we can do a rollback. */
+ rollback->key = key;
+ if ((err = (*propdb->db_hooks->fetch)(propdb->db, key,
+ &rollback->value)) != NULL)
+ goto error;
+
+ if (ctx->operation == DAV_PROP_OP_SET) {
+
+ dav_datum value;
+
+ /* Note: propdb->ns_map was set in dav_prop_validate() */
+
+ /* quote all the values in the element */
+ dav_quote_xml_elem(propdb->p, ctx->prop);
+
+ /* generate a text blob for the xml:lang plus the contents */
+ dav_xml2text(propdb->p, ctx->prop, DAV_X2T_LANG_INNER, NULL,
+ propdb->ns_map,
+ (const char **)&value.dptr, &value.dsize);
+
+ err = (*propdb->db_hooks->store)(propdb->db, key, value);
+
+ /*
+ ** If an error occurred, then assume that we didn't change the
+ ** value. Remove the rollback item so that we don't try to set
+ ** its value during the rollback.
+ */
+ }
+ else if (ctx->operation == DAV_PROP_OP_DELETE) {
+
+ /*
+ ** Delete the property. Ignore errors -- the property is there, or
+ ** we are deleting it for a second time.
+ */
+ /* ### but what about other errors? */
+ (void) (*propdb->db_hooks->remove)(propdb->db, key);
+ }
+ }
+
+ error:
+ /* push a more specific error here */
+ if (err != NULL) {
+ /*
+ ** Use HTTP_INTERNAL_SERVER_ERROR because we shouldn't have seen
+ ** any errors at this point.
+ */
+ ctx->err = dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
+ DAV_ERR_PROP_EXEC,
+ "Could not execute PROPPATCH.", err);
+ }
+}
+
+void dav_prop_commit(dav_prop_ctx *ctx)
+{
+ /*
+ ** Note that a commit implies ctx->err is NULL. The caller should assume
+ ** a status of HTTP_OK for this case.
+ */
+
+ if (ctx->is_liveprop) {
+ (*ctx->prop->provider->patch_commit)(ctx->propdb->resource,
+ ctx->operation,
+ ctx->liveprop_ctx,
+ ctx->rollback->liveprop);
+ }
+}
+
+void dav_prop_rollback(dav_prop_ctx *ctx)
+{
+ dav_error *err = NULL;
+
+ /* do nothing if there is no rollback information. */
+ if (ctx->rollback == NULL)
+ return;
+
+ /*
+ ** ### if we have an error, and a rollback occurs, then the namespace
+ ** ### mods should not happen at all. Basically, the namespace management
+ ** ### is simply a bitch.
+ */
+
+ if (ctx->is_liveprop) {
+ err = (*ctx->prop->provider->patch_rollback)(ctx->propdb->resource,
+ ctx->operation,
+ ctx->liveprop_ctx,
+ ctx->rollback->liveprop);
+ }
+ else if (ctx->rollback->value.dptr == NULL) {
+ /* don't fail if the thing isn't really there */
+ /* ### but what about other errors? */
+ (void) (*ctx->propdb->db_hooks->remove)(ctx->propdb->db,
+ ctx->rollback->key);
+ }
+ else {
+ err = (*ctx->propdb->db_hooks->store)(ctx->propdb->db,
+ ctx->rollback->key,
+ ctx->rollback->value);
+ }
+
+ if (err != NULL) {
+ if (ctx->err == NULL)
+ ctx->err = err;
+ else {
+ dav_error *scan = err;
+
+ /* hook previous errors at the end of the rollback error */
+ while (scan->prev != NULL)
+ scan = scan->prev;
+ scan->prev = ctx->err;
+ ctx->err = err;
+ }
+ }
+}
diff --git a/modules/dav/main/providers.c b/modules/dav/main/providers.c
new file mode 100644
index 0000000000..7417309641
--- /dev/null
+++ b/modules/dav/main/providers.c
@@ -0,0 +1,90 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+#include "apr_pools.h"
+#include "apr_hash.h"
+
+#include "ap_hooks.h" /* ### for ap_global_hook_pool */
+
+#include "mod_dav.h"
+
+
+static apr_hash_t *dav_repos_providers = NULL;
+
+
+static apr_status_t dav_cleanup_providers(void *ctx)
+{
+ dav_repos_providers = NULL;
+ return APR_SUCCESS;
+}
+
+void dav_register_repository(apr_pool_t *p, const char *name,
+ const dav_hooks_repository *hooks)
+{
+ /* ### ignore the pool; it is NULL right now */
+ p = ap_global_hook_pool;
+
+ if (dav_repos_providers == NULL) {
+ dav_repos_providers = apr_make_hash(p);
+ apr_register_cleanup(p, NULL, dav_cleanup_providers, apr_null_cleanup);
+ }
+
+ /* just set it. no biggy if it was there before. */
+ apr_hash_set(dav_repos_providers, name, 0, hooks);
+}
+
+const dav_hooks_repository * dav_lookup_repository(const char *name)
+{
+ return apr_hash_get(dav_repos_providers, name, 0);
+}
diff --git a/modules/dav/main/std_liveprop.c b/modules/dav/main/std_liveprop.c
new file mode 100644
index 0000000000..a7acd24c29
--- /dev/null
+++ b/modules/dav/main/std_liveprop.c
@@ -0,0 +1,289 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+#include "httpd.h"
+#include "util_xml.h"
+#include "apr_strings.h"
+
+#include "mod_dav.h"
+
+/* forward-declare */
+static const dav_hooks_liveprop dav_core_hooks_liveprop;
+
+/*
+** The namespace URIs that we use. There will only ever be "DAV:".
+*/
+static const char * const dav_core_namespace_uris[] =
+{
+ "DAV:",
+ NULL /* sentinel */
+};
+
+/*
+** Define each of the core properties that this provider will handle.
+** Note that all of them are in the DAV: namespace, which has a
+** provider-local index of 0.
+*/
+static const dav_liveprop_spec dav_core_props[] =
+{
+ { 0, "comment", DAV_PROPID_comment, 1 },
+ { 0, "creator-displayname", DAV_PROPID_creator_displayname, 1 },
+ { 0, "displayname", DAV_PROPID_displayname, 1 },
+ { 0, "resourcetype", DAV_PROPID_resourcetype, 0 },
+ { 0, "source", DAV_PROPID_source, 1 },
+ { 0, "supported-live-property-set",
+ DAV_PROPID_supported_live_property_set, 0 },
+ { 0, "supported-method-set", DAV_PROPID_supported_method_set, 0 },
+ { 0, "supported-report-set", DAV_PROPID_supported_report_set, 0 },
+
+ { 0 } /* sentinel */
+};
+
+static const dav_liveprop_group dav_core_liveprop_group =
+{
+ dav_core_props,
+ dav_core_namespace_uris,
+ &dav_core_hooks_liveprop
+};
+
+static dav_prop_insert dav_core_insert_prop(const dav_resource *resource,
+ int propid, int insvalue,
+ ap_text_header *phdr)
+{
+ const char *value;
+ const char *s;
+ dav_prop_insert which;
+ apr_pool_t *p = resource->pool;
+ const dav_liveprop_spec *info;
+ int global_ns;
+
+ switch (propid)
+ {
+ case DAV_PROPID_resourcetype:
+ switch (resource->type) {
+ case DAV_RESOURCE_TYPE_VERSION:
+ if (resource->baselined) {
+ value = "<D:baseline/>";
+ break;
+ }
+ /* fall through */
+ case DAV_RESOURCE_TYPE_REGULAR:
+ case DAV_RESOURCE_TYPE_WORKING:
+ if (resource->collection) {
+ value = "<D:collection/>";
+ }
+ else {
+ /* ### should we denote lock-null resources? */
+
+ value = ""; /* becomes: <D:resourcetype/> */
+ }
+ break;
+ case DAV_RESOURCE_TYPE_HISTORY:
+ value = "<D:version-history/>";
+ break;
+ case DAV_RESOURCE_TYPE_WORKSPACE:
+ value = "<D:collection/>";
+ break;
+ case DAV_RESOURCE_TYPE_ACTIVITY:
+ value = "<D:activity/>";
+ break;
+
+ default:
+ /* ### bad juju */
+ return DAV_PROP_INSERT_NOTDEF;
+ }
+ break;
+
+ case DAV_PROPID_supported_live_property_set:
+ /* ### insert all live property names ### */
+ break;
+
+ case DAV_PROPID_supported_method_set:
+ /* ### leverage code from dav_method_options ### */
+ break;
+
+ case DAV_PROPID_supported_report_set:
+#if 0
+ {
+ /* ### where to get "r" ??? */
+ const dav_hooks_vsn *vsn_hooks = dav_get_vsn_hooks(r);
+
+ if (vsn_hooks != NULL) {
+ const dav_report_elem *reports;
+ dav_error *err;
+
+ if ((err = (*vsn_hooks->avail_reports)(resource,
+ &reports)) != NULL) {
+ err = dav_push_error(p, err->status, 0,
+ "DAV:supported-report-set could not "
+ "be determined due to a problem "
+ "fetching the available reports "
+ "for this resource.",
+ err);
+ /* ### can't return err... sigh. punt for now. */
+ return DAV_PROP_INSERT_NOTDEF;
+ }
+
+ value = "";
+
+ if (reports == NULL) {
+ /* no reports are defined. break with value="" */
+ break;
+ }
+
+ for (; reports->nmspace != NULL; ++reports) {
+ /* Note: presume reports->namespace is XML/URL quoted */
+ const char *v = apr_psprintf(p, "<%s xmlns=\"%s\"/>" DEBUG_CR,
+ reports->name, reports->nmspace);
+
+ /* This isn't very memory-efficient, but there should only
+ be a small number of reports */
+ value = apr_pstrcat(p, value, v, NULL);
+ }
+ }
+ break;
+ }
+#endif
+ /* above code disabled. FALLTHROUGH */
+
+ case DAV_PROPID_comment:
+ case DAV_PROPID_creator_displayname:
+ case DAV_PROPID_displayname:
+ case DAV_PROPID_source:
+ default:
+ /*
+ ** This property is known, but not defined as a liveprop. However,
+ ** it may be a dead property.
+ */
+ return DAV_PROP_INSERT_NOTDEF;
+ }
+
+ /* assert: value != NULL */
+
+ /* get the information and global NS index for the property */
+ global_ns = dav_get_liveprop_info(propid, &dav_core_liveprop_group, &info);
+
+ /* assert: info != NULL && info->name != NULL */
+
+ if (insvalue) {
+ s = apr_psprintf(p, "<lp%d:%s>%s</lp%d:%s>" DEBUG_CR,
+ global_ns, info->name, value, global_ns, info->name);
+ which = DAV_PROP_INSERT_VALUE;
+ }
+ else {
+ s = apr_psprintf(p, "<lp%d:%s/>" DEBUG_CR, global_ns, info->name);
+ which = DAV_PROP_INSERT_NAME;
+ }
+ ap_text_append(p, phdr, s);
+
+ /* we inserted a name or value (this prop is done) */
+ return which;
+}
+
+static int dav_core_is_writable(const dav_resource *resource, int propid)
+{
+ const dav_liveprop_spec *info;
+
+ (void) dav_get_liveprop_info(propid, &dav_core_liveprop_group, &info);
+ return info->is_writable;
+}
+
+static dav_error * dav_core_patch_validate(const dav_resource *resource,
+ const ap_xml_elem *elem,
+ int operation, void **context,
+ int *defer_to_dead)
+{
+ /* all of our writeable props go in the dead prop database */
+ *defer_to_dead = 1;
+
+ return NULL;
+}
+
+static const dav_hooks_liveprop dav_core_hooks_liveprop = {
+ dav_core_insert_prop,
+ dav_core_is_writable,
+ dav_core_namespace_uris,
+ dav_core_patch_validate,
+ NULL, /* patch_exec */
+ NULL, /* patch_commit */
+ NULL, /* patch_rollback */
+};
+
+int dav_core_find_liveprop(request_rec *r, const char *ns_uri,
+ const char *name,
+ const dav_hooks_liveprop **hooks)
+{
+ return dav_do_find_liveprop(ns_uri, name, &dav_core_liveprop_group, hooks);
+}
+
+void dav_core_insert_all_liveprops(request_rec *r,
+ const dav_resource *resource,
+ int insvalue, ap_text_header *phdr)
+{
+ (void) dav_core_insert_prop(resource, DAV_PROPID_resourcetype,
+ insvalue, phdr);
+ (void) dav_core_insert_prop(resource,
+ DAV_PROPID_supported_live_property_set,
+ insvalue, phdr);
+ (void) dav_core_insert_prop(resource, DAV_PROPID_supported_method_set,
+ insvalue, phdr);
+ (void) dav_core_insert_prop(resource, DAV_PROPID_supported_report_set,
+ insvalue, phdr);
+}
+
+void dav_core_register_uris(apr_pool_t *p)
+{
+ /* register the namespace URIs */
+ dav_register_liveprop_group(p, &dav_core_liveprop_group);
+}
diff --git a/modules/dav/main/util.c b/modules/dav/main/util.c
new file mode 100644
index 0000000000..63ae2c880a
--- /dev/null
+++ b/modules/dav/main/util.c
@@ -0,0 +1,2087 @@
+/*
+** Copyright (C) 1998-2000 Greg Stein. All Rights Reserved.
+**
+** By using this file, you agree to the terms and conditions set forth in
+** the LICENSE.html file which can be found at the top level of the mod_dav
+** distribution or at http://www.webdav.org/mod_dav/license-1.html.
+**
+** Contact information:
+** Greg Stein, PO Box 760, Palo Alto, CA, 94302
+** gstein@lyra.org, http://www.webdav.org/mod_dav/
+*/
+
+/*
+** DAV extension module for Apache 1.3.*
+** - various utilities, repository-independent
+**
+** Written by Greg Stein, gstein@lyra.org, http://www.lyra.org/
+*/
+
+#include "mod_dav.h"
+
+#include "http_request.h"
+#include "http_config.h"
+#include "http_vhost.h"
+#include "http_log.h"
+#include "http_protocol.h"
+
+
+dav_error *dav_new_error(pool *p, int status, int error_id, const char *desc)
+{
+ int save_errno = errno;
+ dav_error *err = ap_pcalloc(p, sizeof(*err));
+
+ /* DBG3("dav_new_error: %d %d %s", status, error_id, desc ? desc : "(no desc)"); */
+
+ err->status = status;
+ err->error_id = error_id;
+ err->desc = desc;
+ err->save_errno = save_errno;
+
+ return err;
+}
+
+dav_error *dav_push_error(pool *p, int status, int error_id, const char *desc,
+ dav_error *prev)
+{
+ dav_error *err = ap_pcalloc(p, sizeof(*err));
+
+ err->status = status;
+ err->error_id = error_id;
+ err->desc = desc;
+ err->prev = prev;
+
+ return err;
+}
+
+void dav_text_append(pool * p, dav_text_header *hdr, const char *text)
+{
+ dav_text *t = ap_palloc(p, sizeof(*t));
+
+ t->text = text;
+ t->next = NULL;
+
+ if (hdr->first == NULL) {
+ /* no text elements yet */
+ hdr->first = hdr->last = t;
+ }
+ else {
+ /* append to the last text element */
+ hdr->last->next = t;
+ hdr->last = t;
+ }
+}
+
+void dav_check_bufsize(pool * p, dav_buffer *pbuf, size_t extra_needed)
+{
+ /* grow the buffer if necessary */
+ if (pbuf->cur_len + extra_needed > pbuf->alloc_len) {
+ char *newbuf;
+
+ pbuf->alloc_len += extra_needed + DAV_BUFFER_PAD;
+ newbuf = ap_palloc(p, pbuf->alloc_len);
+ memcpy(newbuf, pbuf->buf, pbuf->cur_len);
+ pbuf->buf = newbuf;
+ }
+}
+
+void dav_set_bufsize(pool * p, dav_buffer *pbuf, size_t size)
+{
+ /* NOTE: this does not retain prior contents */
+
+ /* NOTE: this function is used to init the first pointer, too, since
+ the PAD will be larger than alloc_len (0) for zeroed structures */
+
+ /* grow if we don't have enough for the requested size plus padding */
+ if (size + DAV_BUFFER_PAD > pbuf->alloc_len) {
+ /* set the new length; min of MINSIZE */
+ pbuf->alloc_len = size + DAV_BUFFER_PAD;
+ if (pbuf->alloc_len < DAV_BUFFER_MINSIZE)
+ pbuf->alloc_len = DAV_BUFFER_MINSIZE;
+
+ pbuf->buf = ap_palloc(p, pbuf->alloc_len);
+ }
+ pbuf->cur_len = size;
+}
+
+
+/* initialize a buffer and copy the specified (null-term'd) string into it */
+void dav_buffer_init(pool *p, dav_buffer *pbuf, const char *str)
+{
+ dav_set_bufsize(p, pbuf, strlen(str));
+ memcpy(pbuf->buf, str, pbuf->cur_len + 1);
+}
+
+/* append a string to the end of the buffer, adjust length */
+void dav_buffer_append(pool *p, dav_buffer *pbuf, const char *str)
+{
+ size_t len = strlen(str);
+
+ dav_check_bufsize(p, pbuf, len + 1);
+ memcpy(pbuf->buf + pbuf->cur_len, str, len + 1);
+ pbuf->cur_len += len;
+}
+
+/* place a string on the end of the buffer, do NOT adjust length */
+void dav_buffer_place(pool *p, dav_buffer *pbuf, const char *str)
+{
+ size_t len = strlen(str);
+
+ dav_check_bufsize(p, pbuf, len + 1);
+ memcpy(pbuf->buf + pbuf->cur_len, str, len + 1);
+}
+
+/* place some memory on the end of a buffer; do NOT adjust length */
+void dav_buffer_place_mem(pool *p, dav_buffer *pbuf, const void *mem,
+ size_t amt, size_t pad)
+{
+ dav_check_bufsize(p, pbuf, amt + pad);
+ memcpy(pbuf->buf + pbuf->cur_len, mem, amt);
+}
+
+
+#if APACHE_RELEASE == 10304100
+/* ### this code can be used for 1.3.4 installations. use this function
+ * ### instead of ap_sub_req_method_uri()
+ */
+/*
+ * ### don't look at this code.
+ * ### it is a Crime Against All That is Right and Good
+ */
+#include "http_core.h" /* for SATISFY_* */
+static request_rec *gross_hack(const char *new_file, const request_rec * r)
+{
+ request_rec *rnew = ap_sub_req_lookup_uri(new_file, r);
+ int res;
+
+ /* ### these aren't exported properly from the headers */
+ extern int ap_check_access(request_rec *); /* check access on non-auth basis */
+ extern int ap_check_user_id(request_rec *); /* obtain valid username from client auth */
+ extern int ap_check_auth(request_rec *); /* check (validated) user is authorized here */
+
+ if (rnew->status != HTTP_OK)
+ return rnew;
+
+ /* re-run portions with a modified method */
+ rnew->method = r->method;
+ rnew->method_number = r->method_number;
+
+ if ((ap_satisfies(rnew) == SATISFY_ALL
+ || ap_satisfies(rnew) == SATISFY_NOSPEC)
+ ? ((res = ap_check_access(rnew))
+ || (ap_some_auth_required(rnew)
+ && ((res = ap_check_user_id(rnew))
+ || (res = ap_check_auth(rnew)))))
+ : ((res = ap_check_access(rnew))
+ && (!ap_some_auth_required(rnew)
+ || ((res = ap_check_user_id(rnew))
+ || (res = ap_check_auth(rnew)))))
+ ) {
+ rnew->status = res;
+ }
+
+ return rnew;
+}
+#endif /* APACHE_RELEASE == 10304100 */
+
+/*
+** dav_lookup_uri()
+**
+** Extension for ap_sub_req_lookup_uri() which can't handle absolute
+** URIs properly.
+**
+** If NULL is returned, then an error occurred with parsing the URI or
+** the URI does not match the current server.
+*/
+dav_lookup_result dav_lookup_uri(const char *uri, request_rec * r)
+{
+ dav_lookup_result result = { 0 };
+ const char *scheme;
+ unsigned short port = ntohs(r->connection->local_addr.sin_port);
+ uri_components comp;
+ char *new_file;
+ const char *domain;
+
+ /* first thing to do is parse the URI into various components */
+ if (ap_parse_uri_components(r->pool, uri, &comp) != HTTP_OK) {
+ result.err.status = HTTP_BAD_REQUEST;
+ result.err.desc = "Invalid syntax in Destination URI.";
+ return result;
+ }
+
+ /* the URI must be an absoluteURI (WEBDAV S9.3) */
+ if (comp.scheme == NULL) {
+ result.err.status = HTTP_BAD_REQUEST;
+ result.err.desc = "Destination URI must be an absolute URI.";
+ return result;
+ }
+
+ /* ### not sure this works if the current request came in via https: */
+ scheme = r->parsed_uri.scheme;
+ if (scheme == NULL)
+ scheme = ap_http_method(r);
+
+ /* insert a port if the URI did not contain one */
+ if (comp.port == 0)
+ comp.port = ap_default_port_for_scheme(comp.scheme);
+
+ /* now, verify that the URI uses the same scheme as the current request.
+ the port, must match our port.
+ the URI must not have a query (args) or a fragment
+ */
+ if (strcasecmp(comp.scheme, scheme) != 0 ||
+ comp.port != port) {
+ result.err.status = HTTP_BAD_GATEWAY;
+ result.err.desc = ap_psprintf(r->pool,
+ "Destination URI refers to different "
+ "scheme or port (%s://hostname:%d)\n"
+ "(want: %s://hostname:%d)",
+ comp.scheme ? comp.scheme : scheme,
+ comp.port ? comp.port : port,
+ scheme, port);
+ return result;
+ }
+ if (comp.query != NULL || comp.fragment != NULL) {
+ result.err.status = HTTP_BAD_REQUEST;
+ result.err.desc =
+ "Destination URI contains invalid components "
+ "(a query or a fragment).";
+ return result;
+ }
+
+ /* we have verified the scheme, port, and general structure */
+
+ /*
+ ** Hrm. IE5 will pass unqualified hostnames for both the
+ ** Host: and Destination: headers. This breaks the
+ ** http_vhost.c::matches_aliases function.
+ **
+ ** For now, qualify unqualified comp.hostnames with
+ ** r->server->server_hostname.
+ **
+ ** ### this is a big hack. Apache should provide a better way.
+ ** ### maybe the admin should list the unqualified hosts in a
+ ** ### <ServerAlias> block?
+ */
+ if (strrchr(comp.hostname, '.') == NULL &&
+ (domain = strchr(r->server->server_hostname, '.')) != NULL) {
+ comp.hostname = ap_pstrcat(r->pool, comp.hostname, domain, NULL);
+ }
+
+ /* now, if a hostname was provided, then verify that it represents the
+ same server as the current connection. note that we just use our
+ port, since we've verified the URI matches ours */
+ if (comp.hostname != NULL &&
+ !ap_matches_request_vhost(r, comp.hostname, port)) {
+ result.err.status = HTTP_BAD_GATEWAY;
+ result.err.desc = "Destination URI refers to a different server.";
+ return result;
+ }
+
+ /* we have verified that the requested URI denotes the same server as
+ the current request. Therefore, we can use ap_sub_req_lookup_uri() */
+
+ /* reconstruct a URI as just the path */
+ new_file = ap_unparse_uri_components(r->pool, &comp, UNP_OMITSITEPART);
+
+ /*
+ * Lookup the URI and return the sub-request. Note that we use the
+ * same HTTP method on the destination. This allows the destination
+ * to apply appropriate restrictions (e.g. readonly).
+ */
+#if APACHE_RELEASE == 10304100
+ result.rnew = gross_hack(new_file, r);
+#else
+ result.rnew = ap_sub_req_method_uri(r->method, new_file, r);
+#endif
+
+ return result;
+}
+
+/* ---------------------------------------------------------------
+**
+** XML UTILITY FUNCTIONS
+*/
+
+/* validate that the root element uses a given DAV: tagname (TRUE==valid) */
+int dav_validate_root(const dav_xml_doc *doc, const char *tagname)
+{
+ return doc->root &&
+ doc->root->ns == DAV_NS_DAV_ID &&
+ strcmp(doc->root->name, tagname) == 0;
+}
+
+/* find and return the (unique) child with a given DAV: tagname */
+dav_xml_elem *dav_find_child(const dav_xml_elem *elem, const char *tagname)
+{
+ dav_xml_elem *child = elem->first_child;
+
+ for (; child; child = child->next)
+ if (child->ns == DAV_NS_DAV_ID && !strcmp(child->name, tagname))
+ return child;
+ return NULL;
+}
+
+
+/* how many characters for the given integer? */
+#define DAV_NS_LEN(ns) ((ns) < 10 ? 1 : (ns) < 100 ? 2 : (ns) < 1000 ? 3 : \
+ (ns) < 10000 ? 4 : (ns) < 100000 ? 5 : \
+ (ns) < 1000000 ? 6 : (ns) < 10000000 ? 7 : \
+ (ns) < 100000000 ? 8 : (ns) < 1000000000 ? 9 : 10)
+
+static int dav_text_size(const dav_text *t)
+{
+ int size = 0;
+
+ for (; t; t = t->next)
+ size += strlen(t->text);
+ return size;
+}
+
+static size_t dav_elem_size(const dav_xml_elem *elem, int style,
+ array_header *namespaces, int *ns_map)
+{
+ size_t size;
+
+ if (style == DAV_X2T_FULL || style == DAV_X2T_FULL_NS_LANG) {
+ const dav_xml_attr *attr;
+
+ size = 0;
+
+ if (style == DAV_X2T_FULL_NS_LANG) {
+ int i;
+
+ /*
+ ** The outer element will contain xmlns:ns%d="%s" attributes
+ ** and an xml:lang attribute, if applicable.
+ */
+
+ for (i = namespaces->nelts; i--;) {
+ /* compute size of: ' xmlns:ns%d="%s"' */
+ size += (9 + DAV_NS_LEN(i) + 2 +
+ strlen(DAV_GET_URI_ITEM(namespaces, i)) + 1);
+ }
+
+ if (elem->lang != NULL) {
+ /* compute size of: ' xml:lang="%s"' */
+ size += 11 + strlen(elem->lang) + 1;
+ }
+ }
+
+ if (elem->ns == DAV_NS_NONE) {
+ /* compute size of: <%s> */
+ size += 1 + strlen(elem->name) + 1;
+ }
+ else {
+ int ns = ns_map ? ns_map[elem->ns] : elem->ns;
+
+ /* compute size of: <ns%d:%s> */
+ size += 3 + DAV_NS_LEN(ns) + 1 + strlen(elem->name) + 1;
+ }
+
+ if (DAV_ELEM_IS_EMPTY(elem)) {
+ /* insert a closing "/" */
+ size += 1;
+ }
+ else {
+ /*
+ * two of above plus "/":
+ * <ns%d:%s> ... </ns%d:%s>
+ * OR <%s> ... </%s>
+ */
+ size = 2 * size + 1;
+ }
+
+ for (attr = elem->attr; attr; attr = attr->next) {
+ if (attr->ns == DAV_NS_NONE) {
+ /* compute size of: ' %s="%s"' */
+ size += 1 + strlen(attr->name) + 2 + strlen(attr->value) + 1;
+ }
+ else {
+ /* compute size of: ' ns%d:%s="%s"' */
+ size += 3 + DAV_NS_LEN(attr->ns) + 1 + strlen(attr->name) + 2 + strlen(attr->value) + 1;
+ }
+ }
+
+ /*
+ ** If the element has an xml:lang value that is *different* from
+ ** its parent, then add the thing in: ' xml:lang="%s"'.
+ **
+ ** NOTE: we take advantage of the pointer equality established by
+ ** the parsing for "inheriting" the xml:lang values from parents.
+ */
+ if (elem->lang != NULL &&
+ (elem->parent == NULL || elem->lang != elem->parent->lang)) {
+ size += 11 + strlen(elem->lang) + 1;
+ }
+ }
+ else if (style == DAV_X2T_LANG_INNER) {
+ /*
+ * This style prepends the xml:lang value plus a null terminator.
+ * If a lang value is not present, then we insert a null term.
+ */
+ size = elem->lang ? strlen(elem->lang) + 1 : 1;
+ }
+ else
+ size = 0;
+
+ size += dav_text_size(elem->first_cdata.first);
+
+ for (elem = elem->first_child; elem; elem = elem->next) {
+ /* the size of the child element plus the CDATA that follows it */
+ size += (dav_elem_size(elem, DAV_X2T_FULL, NULL, ns_map) +
+ dav_text_size(elem->following_cdata.first));
+ }
+
+ return size;
+}
+
+static char *dav_write_text(char *s, const dav_text *t)
+{
+ for (; t; t = t->next) {
+ size_t len = strlen(t->text);
+ memcpy(s, t->text, len);
+ s += len;
+ }
+ return s;
+}
+
+static char *dav_write_elem(char *s, const dav_xml_elem *elem, int style,
+ array_header *namespaces, int *ns_map)
+{
+ const dav_xml_elem *child;
+ size_t len;
+ int ns;
+
+ if (style == DAV_X2T_FULL || style == DAV_X2T_FULL_NS_LANG) {
+ int empty = DAV_ELEM_IS_EMPTY(elem);
+ const dav_xml_attr *attr;
+
+ if (elem->ns == DAV_NS_NONE) {
+ len = sprintf(s, "<%s", elem->name);
+ }
+ else {
+ ns = ns_map ? ns_map[elem->ns] : elem->ns;
+ len = sprintf(s, "<ns%d:%s", ns, elem->name);
+ }
+ s += len;
+
+ for (attr = elem->attr; attr; attr = attr->next) {
+ if (attr->ns == DAV_NS_NONE)
+ len = sprintf(s, " %s=\"%s\"", attr->name, attr->value);
+ else
+ len = sprintf(s, " ns%d:%s=\"%s\"", attr->ns, attr->name, attr->value);
+ s += len;
+ }
+
+ /* add the xml:lang value if necessary */
+ if (elem->lang != NULL &&
+ (style == DAV_X2T_FULL_NS_LANG ||
+ elem->parent == NULL ||
+ elem->lang != elem->parent->lang)) {
+ len = sprintf(s, " xml:lang=\"%s\"", elem->lang);
+ s += len;
+ }
+
+ /* add namespace definitions, if required */
+ if (style == DAV_X2T_FULL_NS_LANG) {
+ int i;
+
+ for (i = namespaces->nelts; i--;) {
+ len = sprintf(s, " xmlns:ns%d=\"%s\"", i,
+ DAV_GET_URI_ITEM(namespaces, i));
+ s += len;
+ }
+ }
+
+ /* no more to do. close it up and go. */
+ if (empty) {
+ *s++ = '/';
+ *s++ = '>';
+ return s;
+ }
+
+ /* just close it */
+ *s++ = '>';
+ }
+ else if (style == DAV_X2T_LANG_INNER) {
+ /* prepend the xml:lang value */
+ if (elem->lang != NULL) {
+ len = strlen(elem->lang);
+ memcpy(s, elem->lang, len);
+ s += len;
+ }
+ *s++ = '\0';
+ }
+
+ s = dav_write_text(s, elem->first_cdata.first);
+
+ for (child = elem->first_child; child; child = child->next) {
+ s = dav_write_elem(s, child, DAV_X2T_FULL, NULL, ns_map);
+ s = dav_write_text(s, child->following_cdata.first);
+ }
+
+ if (style == DAV_X2T_FULL || style == DAV_X2T_FULL_NS_LANG) {
+ if (elem->ns == DAV_NS_NONE) {
+ len = sprintf(s, "</%s>", elem->name);
+ }
+ else {
+ ns = ns_map ? ns_map[elem->ns] : elem->ns;
+ len = sprintf(s, "</ns%d:%s>", ns, elem->name);
+ }
+ s += len;
+ }
+
+ return s;
+}
+
+/* convert an element to a text string */
+void dav_xml2text(pool * p,
+ const dav_xml_elem *elem,
+ int style,
+ array_header *namespaces,
+ int *ns_map,
+ const char **pbuf,
+ size_t *psize)
+{
+ /* get the exact size, plus a null terminator */
+ size_t size = dav_elem_size(elem, style, namespaces, ns_map) + 1;
+ char *s = ap_palloc(p, size);
+
+ (void) dav_write_elem(s, elem, style, namespaces, ns_map);
+ s[size - 1] = '\0';
+
+ *pbuf = s;
+ if (psize)
+ *psize = size;
+}
+
+const char *dav_empty_elem(pool * p, const dav_xml_elem *elem)
+{
+ if (elem->ns == DAV_NS_NONE) {
+ /*
+ * The prefix (xml...) is already within the prop name, or
+ * the element simply has no prefix.
+ */
+ return ap_psprintf(p, "<%s/>" DEBUG_CR, elem->name);
+ }
+
+ return ap_psprintf(p, "<ns%d:%s/>" DEBUG_CR, elem->ns, elem->name);
+}
+
+/*
+** dav_quote_string: quote an XML string
+**
+** Replace '<', '>', and '&' with '&lt;', '&gt;', and '&amp;'.
+** If quotes is true, then replace '"' with '&quot;'.
+**
+** quotes is typically set to true for XML strings that will occur within
+** double quotes -- attribute values.
+*/
+const char * dav_quote_string(pool *p, const char *s, int quotes)
+{
+ const char *scan;
+ int len = 0;
+ int extra = 0;
+ char *qstr;
+ char *qscan;
+ char c;
+
+ for (scan = s; (c = *scan) != '\0'; ++scan, ++len) {
+ if (c == '<' || c == '>')
+ extra += 3; /* &lt; or &gt; */
+ else if (c == '&')
+ extra += 4; /* &amp; */
+ else if (quotes && c == '"')
+ extra += 5; /* &quot; */
+ }
+
+ /* nothing to do? */
+ if (extra == 0)
+ return s;
+
+ qstr = ap_palloc(p, len + extra + 1);
+ for (scan = s, qscan = qstr; (c = *scan) != '\0'; ++scan) {
+ if (c == '<') {
+ *qscan++ = '&';
+ *qscan++ = 'l';
+ *qscan++ = 't';
+ *qscan++ = ';';
+ }
+ else if (c == '>') {
+ *qscan++ = '&';
+ *qscan++ = 'g';
+ *qscan++ = 't';
+ *qscan++ = ';';
+ }
+ else if (c == '&') {
+ *qscan++ = '&';
+ *qscan++ = 'a';
+ *qscan++ = 'm';
+ *qscan++ = 'p';
+ *qscan++ = ';';
+ }
+ else if (quotes && c == '"') {
+ *qscan++ = '&';
+ *qscan++ = 'q';
+ *qscan++ = 'u';
+ *qscan++ = 'o';
+ *qscan++ = 't';
+ *qscan++ = ';';
+ }
+ else {
+ *qscan++ = c;
+ }
+ }
+
+ *qscan = '\0';
+ return qstr;
+}
+
+void dav_quote_xml_elem(pool *p, dav_xml_elem *elem)
+{
+ dav_text *scan_txt;
+ dav_xml_attr *scan_attr;
+ dav_xml_elem *scan_elem;
+
+ /* convert the element's text */
+ for (scan_txt = elem->first_cdata.first;
+ scan_txt != NULL;
+ scan_txt = scan_txt->next) {
+ scan_txt->text = dav_quote_string(p, scan_txt->text, 0);
+ }
+ for (scan_txt = elem->following_cdata.first;
+ scan_txt != NULL;
+ scan_txt = scan_txt->next) {
+ scan_txt->text = dav_quote_string(p, scan_txt->text, 0);
+ }
+
+ /* convert the attribute values */
+ for (scan_attr = elem->attr;
+ scan_attr != NULL;
+ scan_attr = scan_attr->next) {
+ scan_attr->value = dav_quote_string(p, scan_attr->value, 1);
+ }
+
+ /* convert the child elements */
+ for (scan_elem = elem->first_child;
+ scan_elem != NULL;
+ scan_elem = scan_elem->next) {
+ dav_quote_xml_elem(p, scan_elem);
+ }
+}
+
+/* ---------------------------------------------------------------
+**
+** Timeout header processing
+**
+*/
+
+/* dav_get_timeout: If the Timeout: header exists, return a time_t
+ * when this lock is expected to expire. Otherwise, return
+ * a time_t of DAV_TIMEOUT_INFINITE.
+ *
+ * It's unclear if DAV clients are required to understand
+ * Seconds-xxx and Infinity time values. We assume that they do.
+ * In addition, for now, that's all we understand, too.
+ */
+time_t dav_get_timeout(request_rec *r)
+{
+ time_t now, expires = DAV_TIMEOUT_INFINITE;
+
+ const char *timeout_const = ap_table_get(r->headers_in, "Timeout");
+ const char *timeout = ap_pstrdup(r->pool, timeout_const), *val;
+
+ if (timeout == NULL)
+ return DAV_TIMEOUT_INFINITE;
+
+ /* Use the first thing we understand, or infinity if
+ * we don't understand anything.
+ */
+
+ while ((val = ap_getword_white(r->pool, &timeout)) && strlen(val)) {
+ if (!strncmp(val, "Infinite", 8)) {
+ return DAV_TIMEOUT_INFINITE;
+ }
+
+ if (!strncmp(val, "Second-", 7)) {
+ val += 7;
+ /* ### We need to handle overflow better:
+ * ### timeout will be <= 2^32 - 1
+ */
+ expires = atol(val);
+ now = time(NULL);
+ return now + expires;
+ }
+ }
+
+ return DAV_TIMEOUT_INFINITE;
+}
+
+/* ---------------------------------------------------------------
+**
+** If Header processing
+**
+*/
+
+/* add_if_resource returns a new if_header, linking it to next_ih.
+ */
+static dav_if_header *dav_add_if_resource(pool *p, dav_if_header *next_ih,
+ const char *uri, size_t uri_len)
+{
+ dav_if_header *ih;
+
+ if ((ih = ap_pcalloc(p, sizeof(*ih))) == NULL)
+ return NULL;
+
+ ih->uri = uri;
+ ih->uri_len = uri_len;
+ ih->next = next_ih;
+
+ return ih;
+}
+
+/* add_if_state adds a condition to an if_header.
+ */
+static dav_error * dav_add_if_state(pool *p, dav_if_header *ih,
+ const char *state_token,
+ dav_if_state_type t, int condition,
+ const dav_hooks_locks *locks_hooks)
+{
+ dav_if_state_list *new_sl;
+
+ new_sl = ap_pcalloc(p, sizeof(*new_sl));
+
+ new_sl->condition = condition;
+ new_sl->type = t;
+
+ if (t == dav_if_opaquelock) {
+ dav_error *err;
+
+ if ((err = (*locks_hooks->parse_locktoken)(p, state_token,
+ &new_sl->locktoken)) != NULL) {
+ /* ### maybe add a higher-level description */
+ return err;
+ }
+ }
+ else
+ new_sl->etag = state_token;
+
+ new_sl->next = ih->state;
+ ih->state = new_sl;
+
+ return NULL;
+}
+
+/* fetch_next_token returns the substring from str+1
+ * to the next occurence of char term, or \0, whichever
+ * occurs first. Leading whitespace is ignored.
+ */
+static char *dav_fetch_next_token(char **str, char term)
+{
+ char *sp;
+ char *token;
+
+ token = *str + 1;
+
+ while (*token && (*token == ' ' || *token == '\t'))
+ token++;
+
+ if ((sp = strchr(token, term)) == NULL)
+ return NULL;
+
+ *sp = '\0';
+ *str = sp;
+ return token;
+}
+
+/* dav_process_if_header:
+ *
+ * If NULL (no error) is returned, then **if_header points to the
+ * "If" productions structure (or NULL if "If" is not present).
+ *
+ * ### this part is bogus:
+ * If an error is encountered, the error is logged. Parent should
+ * return err->status.
+ */
+static dav_error * dav_process_if_header(request_rec *r, dav_if_header **p_ih)
+{
+ dav_error *err;
+ char *str;
+ char *list;
+ const char *state_token;
+ const char *uri = NULL; /* scope of current production; NULL=no-tag */
+ size_t uri_len;
+ dav_if_header *ih = NULL;
+ uri_components parsed_uri;
+ const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
+ enum {no_tagged, tagged, unknown} list_type = unknown;
+ int condition;
+
+ *p_ih = NULL;
+
+ if ((str = ap_pstrdup(r->pool, ap_table_get(r->headers_in, "If"))) == NULL)
+ return NULL;
+
+ while (*str) {
+ switch(*str) {
+ case '<':
+ /* Tagged-list production - following states apply to this uri */
+ if (list_type == no_tagged
+ || ((uri = dav_fetch_next_token(&str, '>')) == NULL)) {
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_TAGGED,
+ "Invalid If-header: unclosed \"<\" or "
+ "unexpected tagged-list production.");
+ }
+
+ /* 2518 specifies this must be an absolute URI; just take the
+ * relative part for later comparison against r->uri */
+ if (ap_parse_uri_components(r->pool, uri, &parsed_uri) != HTTP_OK) {
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_TAGGED,
+ "Invalid URI in tagged If-header.");
+ }
+ /* note that parsed_uri.path is allocated; we can trash it */
+
+ /* clean up the URI a bit */
+ ap_getparents(parsed_uri.path);
+ uri_len = strlen(parsed_uri.path);
+ if (uri_len > 1 && parsed_uri.path[uri_len - 1] == '/')
+ parsed_uri.path[--uri_len] = '\0';
+
+ uri = parsed_uri.path;
+ list_type = tagged;
+ break;
+
+ case '(':
+ /* List production */
+
+ /* If a uri has not been encountered, this is a No-Tagged-List */
+ if (list_type == unknown)
+ list_type = no_tagged;
+
+ if ((list = dav_fetch_next_token(&str, ')')) == NULL) {
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_UNCLOSED_PAREN,
+ "Invalid If-header: unclosed \"(\".");
+ }
+
+ if ((ih = dav_add_if_resource(r->pool, ih, uri, uri_len)) == NULL) {
+ /* ### dav_add_if_resource() should return an error for us! */
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_PARSE,
+ "Internal server error parsing \"If:\" "
+ "header.");
+ }
+
+ condition = DAV_IF_COND_NORMAL;
+
+ while (*list) {
+ /* List is the entire production (in a uri scope) */
+
+ switch (*list) {
+ case '<':
+ if ((state_token = dav_fetch_next_token(&list, '>')) == NULL) {
+ /* ### add a description to this error */
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_PARSE, NULL);
+ }
+
+ if ((err = dav_add_if_state(r->pool, ih, state_token, dav_if_opaquelock,
+ condition, locks_hooks)) != NULL) {
+ /* ### maybe add a higher level description */
+ return err;
+ }
+ condition = DAV_IF_COND_NORMAL;
+ break;
+
+ case '[':
+ if ((state_token = dav_fetch_next_token(&list, ']')) == NULL) {
+ /* ### add a description to this error */
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_PARSE, NULL);
+ }
+
+ if ((err = dav_add_if_state(r->pool, ih, state_token, dav_if_etag,
+ condition, locks_hooks)) != NULL) {
+ /* ### maybe add a higher level description */
+ return err;
+ }
+ condition = DAV_IF_COND_NORMAL;
+ break;
+
+ case 'N':
+ if (list[1] == 'o' && list[2] == 't') {
+ if (condition != DAV_IF_COND_NORMAL) {
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_MULTIPLE_NOT,
+ "Invalid \"If:\" header: "
+ "Multiple \"not\" entries "
+ "for the same state.");
+ }
+ condition = DAV_IF_COND_NOT;
+ }
+ list += 2;
+ break;
+
+ case ' ':
+ case '\t':
+ break;
+
+ default:
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_UNK_CHAR,
+ ap_psprintf(r->pool,
+ "Invalid \"If:\" "
+ "header: Unexpected "
+ "character encountered "
+ "(0x%02x, '%c').",
+ *list, *list));
+ }
+
+ list++;
+ }
+ break;
+
+ case ' ':
+ case '\t':
+ break;
+
+ default:
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_UNK_CHAR,
+ ap_psprintf(r->pool,
+ "Invalid \"If:\" header: "
+ "Unexpected character "
+ "encountered (0x%02x, '%c').",
+ *str, *str));
+ }
+
+ str++;
+ }
+
+ *p_ih = ih;
+ return NULL;
+}
+
+static int dav_find_submitted_locktoken(const dav_if_header *if_header,
+ const dav_lock *lock_list,
+ const dav_hooks_locks *locks_hooks)
+{
+ for (; if_header != NULL; if_header = if_header->next) {
+ const dav_if_state_list *state_list;
+
+ for (state_list = if_header->state;
+ state_list != NULL;
+ state_list = state_list->next) {
+
+ if (state_list->type == dav_if_opaquelock) {
+ const dav_lock *lock;
+
+ /* given state_list->locktoken, match it */
+
+ /*
+ ** The resource will have one or more lock tokens. We only
+ ** need to match one of them against any token in the
+ ** If: header.
+ **
+ ** One token case: It is an exclusive or shared lock. Either
+ ** way, we must find it.
+ **
+ ** N token case: They are shared locks. By policy, we need
+ ** to match only one. The resource's other
+ ** tokens may belong to somebody else (so we
+ ** shouldn't see them in the If: header anyway)
+ */
+ for (lock = lock_list; lock != NULL; lock = lock->next) {
+
+ if (!(*locks_hooks->compare_locktoken)(state_list->locktoken, lock->locktoken)) {
+ return 1;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* dav_validate_resource_state:
+ * Returns NULL if path/uri meets if-header and lock requirements
+ */
+static dav_error * dav_validate_resource_state(pool *p,
+ const dav_resource *resource,
+ dav_lockdb *lockdb,
+ const dav_if_header *if_header,
+ int flags,
+ dav_buffer *pbuf,
+ request_rec *r)
+{
+ dav_error *err;
+ const char *uri;
+ const char *etag;
+ const dav_hooks_locks *locks_hooks = (lockdb ? lockdb->hooks : NULL);
+ const dav_if_header *ifhdr_scan;
+ dav_if_state_list *state_list;
+ dav_lock *lock_list;
+ dav_lock *lock;
+ int num_matched;
+ int num_that_apply;
+ int seen_locktoken;
+ size_t uri_len;
+ const char *reason = NULL;
+
+ /* DBG1("validate: <%s>", resource->uri); */
+
+ /*
+ ** The resource will have one of three states:
+ **
+ ** 1) No locks. We have no special requirements that the user supply
+ ** specific locktokens. One of the state lists must match, and
+ ** we're done.
+ **
+ ** 2) One exclusive lock. The locktoken must appear *anywhere* in the
+ ** If: header. Of course, asserting the token in a "Not" term will
+ ** quickly fail that state list :-). If the locktoken appears in
+ ** one of the state lists *and* one state list matches, then we're
+ ** done.
+ **
+ ** 3) One or more shared locks. One of the locktokens must appear
+ ** *anywhere* in the If: header. If one of the locktokens appears,
+ ** and we match one state list, then we are done.
+ **
+ ** The <seen_locktoken> variable determines whether we have seen one
+ ** of this resource's locktokens in the If: header.
+ */
+
+ /*
+ ** If this is a new lock request, <flags> will contain the requested
+ ** lock scope. Three rules apply:
+ **
+ ** 1) Do not require a (shared) locktoken to be seen (when we are
+ ** applying another shared lock)
+ ** 2) If the scope is exclusive and we see any locks, fail.
+ ** 3) If the scope is shared and we see an exclusive lock, fail.
+ */
+
+ if (lockdb == NULL) {
+ /* we're in State 1. no locks. */
+ lock_list = NULL;
+ }
+ else {
+ /*
+ ** ### hrm... we don't need to have these fully
+ ** ### resolved since we're only looking at the
+ ** ### locktokens...
+ **
+ ** ### use get_locks w/ calltype=PARTIAL
+ */
+ if ((err = dav_lock_query(lockdb, resource, &lock_list)) != NULL) {
+ return dav_push_error(p,
+ HTTP_INTERNAL_SERVER_ERROR, 0,
+ "The locks could not be queried for "
+ "verification against a possible \"If:\" "
+ "header.",
+ err);
+ }
+
+ /* lock_list now determines whether we're in State 1, 2, or 3. */
+ }
+
+ /*
+ ** For a new, exclusive lock: if any locks exist, fail.
+ ** For a new, shared lock: if an exclusive lock exists, fail.
+ ** else, do not require a token to be seen.
+ */
+ if (flags & DAV_LOCKSCOPE_EXCLUSIVE) {
+ if (lock_list != NULL) {
+ return dav_new_error(p, HTTP_LOCKED, 0,
+ "Existing lock(s) on the requested resource "
+ "prevent an exclusive lock.");
+ }
+
+ /*
+ ** There are no locks, so we can pretend that we've already met
+ ** any requirement to find the resource's locks in an If: header.
+ */
+ seen_locktoken = 1;
+ }
+ else if (flags & DAV_LOCKSCOPE_SHARED) {
+ /*
+ ** Strictly speaking, we don't need this loop. Either the first
+ ** (and only) lock will be EXCLUSIVE, or none of them will be.
+ */
+ for (lock = lock_list; lock != NULL; lock = lock->next) {
+ if (lock->scope == DAV_LOCKSCOPE_EXCLUSIVE) {
+ return dav_new_error(p, HTTP_LOCKED, 0,
+ "The requested resource is already "
+ "locked exclusively.");
+ }
+ }
+
+ /*
+ ** The locks on the resource (if any) are all shared. Set the
+ ** <seen_locktoken> flag to indicate that we do not need to find
+ ** the locks in an If: header.
+ */
+ seen_locktoken = 1;
+ }
+ else {
+ /*
+ ** For methods other than LOCK:
+ **
+ ** If we have no locks, then <seen_locktoken> can be set to true --
+ ** pretending that we've already met the requirement of seeing one
+ ** of the resource's locks in the If: header.
+ **
+ ** Otherwise, it must be cleared and we'll look for one.
+ */
+ seen_locktoken = (lock_list == NULL);
+ }
+
+ /*
+ ** If there is no If: header, then we can shortcut some logic:
+ **
+ ** 1) if we do not need to find a locktoken in the (non-existent) If:
+ ** header, then we are successful.
+ **
+ ** 2) if we must find a locktoken in the (non-existent) If: header, then
+ ** we fail.
+ */
+ if (if_header == NULL) {
+ if (seen_locktoken)
+ return NULL;
+
+ return dav_new_error(p, HTTP_LOCKED, 0,
+ "This resource is locked and an \"If:\" header "
+ "was not supplied to allow access to the "
+ "resource.");
+ }
+ /* the If: header is present */
+
+ /*
+ ** If a dummy header is present (because of a Lock-Token: header), then
+ ** we are required to find that token in this resource's set of locks.
+ ** If we have no locks, then we immediately fail.
+ **
+ ** This is a 400 (Bad Request) since they should only submit a locktoken
+ ** that actually exists.
+ **
+ ** Don't issue this response if we're talking about the parent resource.
+ ** It is okay for that resource to NOT have this locktoken.
+ ** (in fact, it certainly will not: a dummy_header only occurs for the
+ ** UNLOCK method, the parent is checked only for locknull resources,
+ ** and the parent certainly does not have the (locknull's) locktoken)
+ */
+ if (lock_list == NULL && if_header->dummy_header) {
+ if (flags & DAV_VALIDATE_IS_PARENT)
+ return NULL;
+ return dav_new_error(p, HTTP_BAD_REQUEST, 0,
+ "The locktoken specified in the \"Lock-Token:\" "
+ "header is invalid because this resource has no "
+ "outstanding locks.");
+ }
+
+ /*
+ ** Prepare the input URI. We want the URI to never have a trailing slash.
+ **
+ ** When URIs are placed into the dav_if_header structure, they are
+ ** guaranteed to never have a trailing slash. If the URIs are equivalent,
+ ** then it doesn't matter if they both lack a trailing slash -- they're
+ ** still equivalent.
+ **
+ ** Note: we could also ensure that a trailing slash is present on both
+ ** URIs, but the majority of URIs provided to us via a resource walk
+ ** will not contain that trailing slash.
+ */
+ uri = resource->uri;
+ uri_len = strlen(uri);
+ if (uri[uri_len - 1] == '/') {
+ dav_set_bufsize(p, pbuf, uri_len);
+ memcpy(pbuf->buf, uri, uri_len);
+ pbuf->buf[--uri_len] = '\0';
+ uri = pbuf->buf;
+ }
+
+ /* get the resource's etag; we may need it during the checks */
+ etag = (*resource->hooks->getetag)(resource);
+
+ /* how many state_lists apply to this URI? */
+ num_that_apply = 0;
+
+ /* If there are if-headers, fail if this resource
+ * does not match at least one state_list.
+ */
+ for (ifhdr_scan = if_header;
+ ifhdr_scan != NULL;
+ ifhdr_scan = ifhdr_scan->next) {
+
+ /* DBG2("uri=<%s> if_uri=<%s>", uri, ifhdr_scan->uri ? ifhdr_scan->uri : "(no uri)"); */
+
+ if (ifhdr_scan->uri != NULL
+ && (uri_len != ifhdr_scan->uri_len
+ || memcmp(uri, ifhdr_scan->uri, uri_len) != 0)) {
+ /*
+ ** A tagged-list's URI doesn't match this resource's URI.
+ ** Skip to the next state_list to see if it will match.
+ */
+ continue;
+ }
+
+ /* this state_list applies to this resource */
+
+ /*
+ ** ### only one state_list should ever apply! a no-tag, or a tagged
+ ** ### where S9.4.2 states only one can match.
+ **
+ ** ### revamp this code to loop thru ifhdr_scan until we find the
+ ** ### matching state_list. process it. stop.
+ */
+ ++num_that_apply;
+
+ /* To succeed, resource must match *all* of the states
+ * specified in the state_list.
+ */
+ for (state_list = ifhdr_scan->state;
+ state_list != NULL;
+ state_list = state_list->next) {
+
+ switch(state_list->type) {
+ case dav_if_etag:
+ {
+ int mismatch = strcmp(state_list->etag, etag);
+
+ if (state_list->condition == DAV_IF_COND_NORMAL && mismatch) {
+ /*
+ ** The specified entity-tag does not match the
+ ** entity-tag on the resource. This state_list is
+ ** not going to match. Bust outta here.
+ */
+ reason =
+ "an entity-tag was specified, but the resource's "
+ "actual ETag does not match.";
+ goto state_list_failed;
+ }
+ else if (state_list->condition == DAV_IF_COND_NOT
+ && !mismatch) {
+ /*
+ ** The specified entity-tag DOES match the
+ ** entity-tag on the resource. This state_list is
+ ** not going to match. Bust outta here.
+ */
+ reason =
+ "an entity-tag was specified using the \"Not\" form, "
+ "but the resource's actual ETag matches the provided "
+ "entity-tag.";
+ goto state_list_failed;
+ }
+ break;
+ }
+
+ case dav_if_opaquelock:
+ if (lockdb == NULL) {
+ if (state_list->condition == DAV_IF_COND_NOT) {
+ /* the locktoken is definitely not there! (success) */
+ continue;
+ }
+
+ /* condition == DAV_IF_COND_NORMAL */
+
+ /*
+ ** If no lockdb is provided, then validation fails for
+ ** this state_list (NORMAL means we were supposed to
+ ** find the token, which we obviously cannot do without
+ ** a lock database).
+ **
+ ** Go and try the next state list.
+ */
+ reason =
+ "a State-token was supplied, but a lock database "
+ "is not available for to provide the required lock.";
+ goto state_list_failed;
+ }
+
+ /* Resource validation 'fails' if:
+ * ANY of the lock->locktokens match
+ * a NOT state_list->locktoken,
+ * OR
+ * NONE of the lock->locktokens match
+ * a NORMAL state_list->locktoken.
+ */
+ num_matched = 0;
+ for (lock = lock_list; lock != NULL; lock = lock->next) {
+
+ /*
+ DBG2("compare: rsrc=%s ifhdr=%s",
+ (*locks_hooks->format_locktoken)(p, lock->locktoken),
+ (*locks_hooks->format_locktoken)(p, state_list->locktoken));
+ */
+
+ /* nothing to do if the locktokens do not match. */
+ if ((*locks_hooks->compare_locktoken)(state_list->locktoken, lock->locktoken)) {
+ continue;
+ }
+
+ /*
+ ** We have now matched up one of the resource's locktokens
+ ** to a locktoken in a State-token in the If: header.
+ ** Note this fact, so that we can pass the overall
+ ** requirement of seeing at least one of the resource's
+ ** locktokens.
+ */
+ seen_locktoken = 1;
+
+ if (state_list->condition == DAV_IF_COND_NOT) {
+ /*
+ ** This state requires that the specified locktoken
+ ** is NOT present on the resource. But we just found
+ ** it. There is no way this state-list can now
+ ** succeed, so go try another one.
+ */
+ reason =
+ "a State-token was supplied, which used a "
+ "\"Not\" condition. The State-token was found "
+ "in the locks on this resource";
+ goto state_list_failed;
+ }
+
+ /* condition == DAV_IF_COND_NORMAL */
+
+ /* Validate auth_user: If an authenticated user created
+ ** the lock, only the same user may submit that locktoken
+ ** to manipulate a resource.
+ */
+ if (lock->auth_user &&
+ (!r->connection->user ||
+ strcmp(lock->auth_user, r->connection->user))) {
+ const char *errmsg;
+
+ errmsg = ap_pstrcat(p, "User \"",
+ r->connection->user,
+ "\" submitted a locktoken created "
+ "by user \"",
+ lock->auth_user, "\".", NULL);
+ return dav_new_error(p, HTTP_UNAUTHORIZED, 0, errmsg);
+ }
+
+ /*
+ ** We just matched a specified State-Token to one of the
+ ** resource's locktokens.
+ **
+ ** Break out of the lock scan -- we only needed to find
+ ** one match (actually, there shouldn't be any other
+ ** matches in the lock list).
+ */
+ num_matched = 1;
+ break;
+ }
+
+ if (num_matched == 0
+ && state_list->condition == DAV_IF_COND_NORMAL) {
+ /*
+ ** We had a NORMAL state, meaning that we should have
+ ** found the State-Token within the locks on this
+ ** resource. We didn't, so this state_list must fail.
+ */
+ reason =
+ "a State-token was supplied, but it was not found "
+ "in the locks on this resource.";
+ goto state_list_failed;
+ }
+
+ break;
+
+ } /* switch */
+ } /* foreach ( state_list ) */
+
+ /*
+ ** We've checked every state in this state_list and none of them
+ ** have failed. Since all of them succeeded, then we have a matching
+ ** state list and we may be done.
+ **
+ ** The next requirement is that we have seen one of the resource's
+ ** locktokens (if any). If we have, then we can just exit. If we
+ ** haven't, then we need to keep looking.
+ */
+ if (seen_locktoken) {
+ /* woo hoo! */
+ return NULL;
+ }
+
+ /*
+ ** Haven't seen one. Let's break out of the search and just look
+ ** for a matching locktoken.
+ */
+ break;
+
+ /*
+ ** This label is used when we detect that a state_list is not
+ ** going to match this resource. We bust out and try the next
+ ** state_list.
+ */
+ state_list_failed:
+ ;
+
+ } /* foreach ( ifhdr_scan ) */
+
+ /*
+ ** The above loop exits for one of two reasons:
+ ** 1) a state_list matched and seen_locktoken is false.
+ ** 2) all if_header structures were scanned, without (1) occurring
+ */
+
+ if (ifhdr_scan == NULL) {
+ /*
+ ** We finished the loop without finding any matching state lists.
+ */
+
+ /*
+ ** If none of the state_lists apply to this resource, then we
+ ** may have succeeded. Note that this scenario implies a
+ ** tagged-list with no matching state_lists. If the If: header
+ ** was a no-tag-list, then it would have applied to this resource.
+ **
+ ** S9.4.2 states that when no state_lists apply, then the header
+ ** should be ignored.
+ **
+ ** If we saw one of the resource's locktokens, then we're done.
+ ** If we did not see a locktoken, then we fail.
+ */
+ if (num_that_apply == 0) {
+ if (seen_locktoken)
+ return NULL;
+
+ /*
+ ** We may have aborted the scan before seeing the locktoken.
+ ** Rescan the If: header to see if we can find the locktoken
+ ** somewhere.
+ */
+ if (dav_find_submitted_locktoken(if_header, lock_list,
+ locks_hooks)) {
+ /*
+ ** We found a match! We're set... none of the If: header
+ ** assertions apply (implicit success), and the If: header
+ ** specified the locktoken somewhere. We're done.
+ */
+ return NULL;
+ }
+
+ return dav_new_error(p, HTTP_LOCKED, 0 /* error_id */,
+ "This resource is locked and the \"If:\" "
+ "header did not specify one of the "
+ "locktokens for this resource's lock(s).");
+ }
+ /* else: one or more state_lists were applicable, but failed. */
+
+ /*
+ ** If the dummy_header did not match, then they specified an
+ ** incorrect token in the Lock-Token header. Forget whether the
+ ** If: statement matched or not... we'll tell them about the
+ ** bad Lock-Token first. That is considered a 400 (Bad Request).
+ */
+ if (if_header->dummy_header) {
+ return dav_new_error(p, HTTP_BAD_REQUEST, 0,
+ "The locktoken specified in the "
+ "\"Lock-Token:\" header did not specify one "
+ "of this resource's locktoken(s).");
+ }
+
+ if (reason == NULL) {
+ return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0,
+ "The preconditions specified by the \"If:\" "
+ "header did not match this resource.");
+ }
+
+ return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0,
+ ap_psprintf(p,
+ "The precondition(s) specified by "
+ "the \"If:\" header did not match "
+ "this resource. At least one "
+ "failure is because: %s", reason));
+ }
+
+ /* assert seen_locktoken == 0 */
+
+ /*
+ ** ifhdr_scan != NULL implies we found a matching state_list.
+ **
+ ** Since we're still here, it also means that we have not yet found
+ ** one the resource's locktokens in the If: header.
+ **
+ ** Scan all the if_headers and states looking for one of this
+ ** resource's locktokens. Note that we need to go back and scan them
+ ** all -- we may have aborted a scan with a failure before we saw a
+ ** matching token.
+ **
+ ** Note that seen_locktoken == 0 implies lock_list != NULL which implies
+ ** locks_hooks != NULL.
+ */
+ if (dav_find_submitted_locktoken(if_header, lock_list, locks_hooks)) {
+ /*
+ ** We found a match! We're set... we have a matching state list,
+ ** and the If: header specified the locktoken somewhere. We're done.
+ */
+ return NULL;
+ }
+
+ /*
+ ** We had a matching state list, but the user agent did not specify one
+ ** of this resource's locktokens. Tell them so.
+ **
+ ** Note that we need to special-case the message on whether a "dummy"
+ ** header exists. If it exists, yet we didn't see a needed locktoken,
+ ** then that implies the dummy header (Lock-Token header) did NOT
+ ** specify one of this resource's locktokens. (this implies something
+ ** in the real If: header matched)
+ **
+ ** We want to note the 400 (Bad Request) in favor of a 423 (Locked).
+ */
+ if (if_header->dummy_header) {
+ return dav_new_error(p, HTTP_BAD_REQUEST, 0,
+ "The locktoken specified in the "
+ "\"Lock-Token:\" header did not specify one "
+ "of this resource's locktoken(s).");
+ }
+
+ return dav_new_error(p, HTTP_LOCKED, 1 /* error_id */,
+ "This resource is locked and the \"If:\" header "
+ "did not specify one of the "
+ "locktokens for this resource's lock(s).");
+}
+
+/* dav_validate_walker: Walker callback function to validate resource state */
+static dav_error * dav_validate_walker(dav_walker_ctx *ctx, int calltype)
+{
+ dav_error *err;
+
+ if ((err = dav_validate_resource_state(ctx->pool, ctx->resource,
+ ctx->lockdb,
+ ctx->if_header, ctx->flags,
+ &ctx->work_buf, ctx->r)) == NULL) {
+ /* There was no error, so just bug out. */
+ return NULL;
+ }
+
+ /*
+ ** If we have a serious server error, or if the request itself failed,
+ ** then just return error (not a multistatus).
+ */
+ if (ap_is_HTTP_SERVER_ERROR(err->status)
+ || (*ctx->resource->hooks->is_same_resource)(ctx->resource,
+ ctx->root)) {
+ /* ### maybe push a higher-level description? */
+ return err;
+ }
+
+ /* associate the error with the current URI */
+ dav_add_response(ctx, ctx->uri.buf, err->status, NULL);
+
+ return NULL;
+}
+
+/*
+** dav_validate_request: Validate if-headers (and check for locks) on:
+** (1) r->filename @ depth;
+** (2) Parent of r->filename if check_parent == 1
+**
+** The check of parent should be done when it is necessary to verify that
+** the parent collection will accept a new member (ie current resource
+** state is null).
+**
+** Return OK on successful validation.
+** On error, return appropriate HTTP_* code, and log error. If a multi-stat
+** error is necessary, response will point to it, else NULL.
+*/
+dav_error * dav_validate_request(request_rec *r, dav_resource *resource,
+ int depth, dav_locktoken *locktoken,
+ dav_response **response, int flags,
+ dav_lockdb *lockdb)
+{
+ dav_error *err;
+ int result;
+ dav_if_header *if_header;
+ int lock_db_opened_locally = 0;
+ const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
+ const dav_hooks_repository *repos_hooks = resource->hooks;
+ dav_buffer work_buf = { 0 };
+ dav_response *new_response;
+
+#if DAV_DEBUG
+ if (depth && response == NULL) {
+ /*
+ ** ### bleck. we can't return errors for other URIs unless we have
+ ** ### a "response" ptr.
+ */
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "DESIGN ERROR: dav_validate_request called "
+ "with depth>0, but no response ptr.");
+ }
+#endif
+
+ if (response != NULL)
+ *response = NULL;
+
+ /* Do the standard checks for conditional requests using
+ * If-..-Since, If-Match etc */
+ if ((result = ap_meets_conditions(r)) != OK) {
+ /* ### fix this up... how? */
+ return dav_new_error(r->pool, result, 0, NULL);
+ }
+
+ /* always parse (and later process) the If: header */
+ if ((err = dav_process_if_header(r, &if_header)) != NULL) {
+ /* ### maybe add higher-level description */
+ return err;
+ }
+
+ /* If a locktoken was specified, create a dummy if_header with which
+ * to validate resources. In the interim, figure out why DAV uses
+ * locktokens in an if-header without a Lock-Token header to refresh
+ * locks, but a Lock-Token header without an if-header to remove them.
+ */
+ if (locktoken != NULL) {
+ dav_if_header *ifhdr_new;
+
+ ifhdr_new = ap_pcalloc(r->pool, sizeof(*ifhdr_new));
+ ifhdr_new->uri = resource->uri;
+ ifhdr_new->uri_len = strlen(resource->uri);
+ ifhdr_new->dummy_header = 1;
+
+ ifhdr_new->state = ap_pcalloc(r->pool, sizeof(*ifhdr_new->state));
+ ifhdr_new->state->type = dav_if_opaquelock;
+ ifhdr_new->state->condition = DAV_IF_COND_NORMAL;
+ ifhdr_new->state->locktoken = locktoken;
+
+ ifhdr_new->next = if_header;
+ if_header = ifhdr_new;
+ }
+
+ /*
+ ** If necessary, open the lock database (read-only, lazily);
+ ** the validation process may need to retrieve or update lock info.
+ ** Otherwise, assume provided lockdb is valid and opened rw.
+ */
+ if (lockdb == NULL) {
+ if (locks_hooks != NULL) {
+ if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) {
+ /* ### maybe insert higher-level comment */
+ return err;
+ }
+ lock_db_opened_locally = 1;
+ }
+ else {
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Resource validation failed because no "
+ "lock hooks were found.");
+ }
+ }
+
+ /* (1) Validate the specified resource, at the specified depth */
+ if (resource->exists && depth > 0) {
+ dav_walker_ctx ctx = { 0 };
+
+ ctx.walk_type = DAV_WALKTYPE_ALL;
+ ctx.postfix = 0;
+ ctx.func = dav_validate_walker;
+ ctx.pool = r->pool;
+ ctx.if_header = if_header;
+ ctx.r = r;
+ ctx.flags = flags;
+ ctx.resource = resource;
+
+ if (lockdb != NULL) {
+ ctx.lockdb = lockdb;
+ ctx.walk_type |= DAV_WALKTYPE_LOCKNULL;
+ }
+
+ dav_buffer_init(r->pool, &ctx.uri, resource->uri);
+
+ err = (*repos_hooks->walk)(&ctx, DAV_INFINITY);
+ if (err == NULL) {
+ *response = ctx.response;
+ }
+ /* else: implies a 5xx status code occurred. */
+ }
+ else {
+ err = dav_validate_resource_state(r->pool, resource, lockdb,
+ if_header, flags, &work_buf, r);
+ }
+
+ /* (2) Validate the parent resource if requested */
+ if (err == NULL && (flags & DAV_VALIDATE_PARENT)) {
+ dav_resource *parent_resource = (*repos_hooks->get_parent_resource)(resource);
+
+ if (parent_resource == NULL) {
+ err = dav_new_error(r->pool, HTTP_FORBIDDEN, 0,
+ "Cannot access parent of repository root.");
+ }
+ else {
+ err = dav_validate_resource_state(r->pool, parent_resource, lockdb,
+ if_header,
+ flags | DAV_VALIDATE_IS_PARENT,
+ &work_buf, r);
+
+ /*
+ ** This error occurred on the parent resource. This implies that
+ ** we have to create a multistatus response (to report the error
+ ** against a URI other than the Request-URI). "Convert" this error
+ ** into a multistatus response.
+ */
+ if (err != NULL) {
+ new_response = ap_pcalloc(r->pool, sizeof(*new_response));
+
+ new_response->href = parent_resource->uri;
+ new_response->status = err->status;
+ new_response->desc =
+ "A validation error has occurred on the parent resource, "
+ "preventing the operation on the resource specified by "
+ "the Request-URI.";
+ if (err->desc != NULL) {
+ new_response->desc = ap_pstrcat(r->pool,
+ new_response->desc,
+ " The error was: ",
+ err->desc, NULL);
+ }
+
+ /* assert: DAV_VALIDATE_PARENT implies response != NULL */
+ new_response->next = *response;
+ *response = new_response;
+
+ err = NULL;
+ }
+ }
+ }
+
+ if (lock_db_opened_locally)
+ (*locks_hooks->close_lockdb)(lockdb);
+
+ /*
+ ** If we don't have a (serious) error, and we have multistatus responses,
+ ** then we need to construct an "error". This error will be the overall
+ ** status returned, and the multistatus responses will go into its body.
+ **
+ ** For certain methods, the overall error will be a 424. The default is
+ ** to construct a standard 207 response.
+ */
+ if (err == NULL && response != NULL && *response != NULL) {
+ dav_text *propstat = NULL;
+
+ if ((flags & DAV_VALIDATE_USE_424) != 0) {
+ /* manufacture a 424 error to hold the multistatus response(s) */
+ return dav_new_error(r->pool, HTTP_FAILED_DEPENDENCY, 0,
+ "An error occurred on another resource, "
+ "preventing the requested operation on "
+ "this resource.");
+ }
+
+ /*
+ ** Whatever caused the error, the Request-URI should have a 424
+ ** associated with it since we cannot complete the method.
+ **
+ ** For a LOCK operation, insert an empty DAV:lockdiscovery property.
+ ** For other methods, return a simple 424.
+ */
+ if ((flags & DAV_VALIDATE_ADD_LD) != 0) {
+ propstat = ap_pcalloc(r->pool, sizeof(*propstat));
+ propstat->text =
+ "<D:propstat>" DEBUG_CR
+ "<D:prop><D:lockdiscovery/></D:prop>" DEBUG_CR
+ "<D:status>HTTP/1.1 424 Failed Dependency</D:status>" DEBUG_CR
+ "</D:propstat>" DEBUG_CR;
+ }
+
+ /* create the 424 response */
+ new_response = ap_pcalloc(r->pool, sizeof(*new_response));
+ new_response->href = resource->uri;
+ new_response->status = HTTP_FAILED_DEPENDENCY;
+ new_response->propresult.propstats = propstat;
+ new_response->desc =
+ "An error occurred on another resource, preventing the "
+ "requested operation on this resource.";
+
+ new_response->next = *response;
+ *response = new_response;
+
+ /* manufacture a 207 error for the multistatus response(s) */
+ return dav_new_error(r->pool, HTTP_MULTI_STATUS, 0,
+ "Error(s) occurred on resources during the "
+ "validation process.");
+ }
+
+ return err;
+}
+
+/* dav_get_locktoken_list:
+ *
+ * Sets ltl to a locktoken_list of all positive locktokens in header,
+ * else NULL if no If-header, or no positive locktokens.
+ */
+dav_error * dav_get_locktoken_list(request_rec *r, dav_locktoken_list **ltl)
+{
+ dav_error *err;
+ dav_if_header *if_header;
+ dav_if_state_list *if_state;
+ dav_locktoken_list *lock_token = NULL;
+
+ *ltl = NULL;
+
+ if ((err = dav_process_if_header(r, &if_header)) != NULL) {
+ /* ### add a higher-level description? */
+ return err;
+ }
+
+ while (if_header != NULL) {
+ if_state = if_header->state; /* Begining of the if_state linked list */
+ while (if_state != NULL) {
+ if (if_state->condition == DAV_IF_COND_NORMAL
+ && if_state->type == dav_if_opaquelock) {
+ lock_token = ap_pcalloc(r->pool, sizeof(dav_locktoken_list));
+ lock_token->locktoken = if_state->locktoken;
+ lock_token->next = *ltl;
+ *ltl = lock_token;
+ }
+ if_state = if_state->next;
+ }
+ if_header = if_header->next;
+ }
+ if (*ltl == NULL) {
+ /* No nodes added */
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST, DAV_ERR_IF_ABSENT,
+ "No locktokens were specified in the \"If:\" "
+ "header, so the refresh could not be performed.");
+ }
+
+ return NULL;
+}
+
+/* dav_get_target_selector:
+ *
+ * Returns any Target-Selector header in a request
+ * (used by versioning clients)
+ */
+const char *dav_get_target_selector(request_rec *r)
+{
+ return ap_table_get(r->headers_in, "Target-Selector");
+}
+
+/* Ensure that a resource is writable. If there is no versioning
+ * provider, then this is essentially a no-op. Versioning repositories
+ * require explicit resource creation and checkout before they can
+ * be written to. If a new resource is to be created, or an existing
+ * resource deleted, the parent collection must be checked out as well.
+ *
+ * Set the parent_only flag to only make the parent collection writable.
+ * Otherwise, both parent and child are made writable as needed. If the
+ * child does not exist, then a new versioned resource is created and
+ * checked out.
+ *
+ * The parent_resource and parent_was_writable arguments are optional
+ * (i.e. they may be NULL). If parent_only is set, then the
+ * resource_existed and resource_was_writable arguments are ignored.
+ *
+ * The previous states of the resources are returned, so they can be
+ * restored after the operation completes (see
+ * dav_revert_resource_writability())
+ */
+dav_error *dav_ensure_resource_writable(request_rec *r,
+ dav_resource *resource,
+ int parent_only,
+ dav_resource **parent_resource,
+ int *resource_existed,
+ int *resource_was_writable,
+ int *parent_was_writable)
+{
+ const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
+ dav_resource *parent = NULL;
+ const char *body;
+ int auto_version;
+ dav_error *err;
+
+ if (parent_resource != NULL)
+ *parent_resource = NULL;
+
+ if (!parent_only) {
+ *resource_existed = resource->exists;
+ *resource_was_writable = 0;
+ }
+
+ if (parent_was_writable != NULL)
+ *parent_was_writable = 0;
+
+ /* if a Target-Selector header is present, then the client knows about
+ * versioning, so it should not be relying on implicit versioning
+ */
+ auto_version = (dav_get_target_selector(r) == NULL);
+
+ /* check parent resource if requested or if resource must be created */
+ if (!resource->exists || parent_only) {
+ parent = (*resource->hooks->get_parent_resource)(resource);
+ if (parent == NULL || !parent->exists) {
+ body = ap_psprintf(r->pool,
+ "Missing one or more intermediate collections. "
+ "Cannot create resource %s.",
+ ap_escape_html(r->pool, resource->uri));
+ return dav_new_error(r->pool, HTTP_CONFLICT, 0, body);
+ }
+
+ if (parent_resource != NULL)
+ *parent_resource = parent;
+
+ /* if parent not versioned, assume child can be created */
+ if (!parent->versioned) {
+ if (!parent_only)
+ *resource_was_writable = 1;
+
+ if (parent_was_writable != NULL)
+ *parent_was_writable = 1;
+ return NULL;
+ }
+
+ /* if no versioning provider, something is terribly wrong */
+ if (vsn_hooks == NULL) {
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "INTERNAL ERROR: "
+ "versioned resource with no versioning "
+ "provider?");
+ }
+
+ /* remember whether parent was already writable */
+ if (parent_was_writable != NULL)
+ *parent_was_writable = parent->working;
+
+ /* parent must be checked out */
+ if (!parent->working) {
+ if ((err = (*vsn_hooks->checkout)(parent)) != NULL) {
+ body = ap_psprintf(r->pool,
+ "Unable to checkout parent collection. "
+ "Cannot create resource %s.",
+ ap_escape_html(r->pool, resource->uri));
+ return dav_push_error(r->pool, HTTP_CONFLICT, 0, body, err);
+ }
+ }
+
+ /* if not just checking parent, create new child resource */
+ if (!parent_only) {
+ if ((err = (*vsn_hooks->mkresource)(resource)) != NULL) {
+ body = ap_psprintf(r->pool,
+ "Unable to create versioned resource %s.",
+ ap_escape_html(r->pool, resource->uri));
+ return dav_push_error(r->pool, HTTP_CONFLICT, 0, body, err);
+ }
+ }
+ }
+ else {
+ /* resource exists: if not versioned, then assume it is writable */
+ if (!resource->versioned) {
+ *resource_was_writable = 1;
+ return NULL;
+ }
+
+ *resource_was_writable = resource->working;
+ }
+
+ /* if not just checking parent, make sure child resource is checked out */
+ if (!parent_only && !resource->working) {
+ if ((err = (*vsn_hooks->checkout)(resource)) != NULL) {
+ body = ap_psprintf(r->pool,
+ "Unable to checkout resource %s.",
+ ap_escape_html(r->pool, resource->uri));
+ return dav_push_error(r->pool, HTTP_CONFLICT, 0, body, err);
+ }
+ }
+
+ return NULL;
+}
+
+/* Revert the writability of resources back to what they were
+ * before they were modified. If undo == 0, then the resource
+ * modifications are maintained (i.e. they are checked in).
+ * If undo != 0, then resource modifications are discarded
+ * (i.e. they are unchecked out).
+ *
+ * The resource and parent_resource arguments are optional
+ * (i.e. they may be NULL).
+ */
+dav_error *dav_revert_resource_writability(request_rec *r,
+ dav_resource *resource,
+ dav_resource *parent_resource,
+ int undo,
+ int resource_existed,
+ int resource_was_writable,
+ int parent_was_writable)
+{
+ const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
+ const char *body;
+ dav_error *err;
+
+ if (resource != NULL) {
+ if (!resource_was_writable
+ && resource->versioned && resource->working) {
+
+ if (undo)
+ err = (*vsn_hooks->uncheckout)(resource);
+ else
+ err = (*vsn_hooks->checkin)(resource);
+
+ if (err != NULL) {
+ body = ap_psprintf(r->pool,
+ "Unable to %s resource %s.",
+ undo ? "uncheckout" : "checkin",
+ ap_escape_html(r->pool, resource->uri));
+ return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ body, err);
+ }
+ }
+
+ if (undo && !resource_existed && resource->exists) {
+ dav_response *response;
+
+ /* ### should we do anything with the response? */
+ if ((err = (*resource->hooks->remove_resource)(resource,
+ &response)) != NULL) {
+ body = ap_psprintf(r->pool,
+ "Unable to undo creation of resource %s.",
+ ap_escape_html(r->pool, resource->uri));
+ return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ body, err);
+ }
+ }
+ }
+
+ if (parent_resource != NULL && !parent_was_writable
+ && parent_resource->versioned && parent_resource->working) {
+
+ if (undo)
+ err = (*vsn_hooks->uncheckout)(parent_resource);
+ else
+ err = (*vsn_hooks->checkin)(parent_resource);
+
+ if (err != NULL) {
+ body = ap_psprintf(r->pool,
+ "Unable to %s parent collection of %s.",
+ undo ? "uncheckout" : "checkin",
+ ap_escape_html(r->pool, resource->uri));
+ return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ body, err);
+ }
+ }
+
+ return NULL;
+}
+
+/* return the URI's (existing) index, or insert it and return a new index */
+int dav_insert_uri(array_header *uri_array, const char *uri)
+{
+ int i;
+ const char **pelt;
+
+ for (i = uri_array->nelts; i--;) {
+ if (strcmp(uri, DAV_GET_URI_ITEM(uri_array, i)) == 0)
+ return i;
+ }
+
+ pelt = ap_push_array(uri_array);
+ *pelt = uri; /* assume uri is const or in a pool */
+ return uri_array->nelts - 1;
+}
diff --git a/modules/dav/main/util_lock.c b/modules/dav/main/util_lock.c
new file mode 100644
index 0000000000..a3441bf53f
--- /dev/null
+++ b/modules/dav/main/util_lock.c
@@ -0,0 +1,764 @@
+/*
+** Copyright (C) 1998-2000 Greg Stein. All Rights Reserved.
+**
+** By using this file, you agree to the terms and conditions set forth in
+** the LICENSE.html file which can be found at the top level of the mod_dav
+** distribution or at http://www.webdav.org/mod_dav/license-1.html.
+**
+** Contact information:
+** Greg Stein, PO Box 760, Palo Alto, CA, 94302
+** gstein@lyra.org, http://www.webdav.org/mod_dav/
+*/
+
+/*
+** DAV repository-independent lock functions
+**
+** Written 06/99 by Keith Wannamaker, wannamak@us.ibm.com
+**
+** Modified 08/99 by John Vasta, vasta@rational.com, to move filesystem-based
+** implementation to dav_fs_lock.c
+*/
+
+#include "mod_dav.h"
+#include "http_log.h"
+#include "http_config.h"
+#include "http_protocol.h"
+#include "http_core.h"
+#include "memory.h"
+
+
+/* ---------------------------------------------------------------
+**
+** Property-related lock functions
+**
+*/
+
+/*
+** dav_lock_get_activelock: Returns a <lockdiscovery> containing
+** an activelock element for every item in the lock_discovery tree
+*/
+const char *dav_lock_get_activelock(request_rec *r, dav_lock *lock,
+ dav_buffer *pbuf)
+{
+ dav_lock *lock_scan;
+ const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
+ int count = 0;
+ dav_buffer work_buf = { 0 };
+ pool *p = r->pool;
+
+ /* If no locks or no lock provider, there are no locks */
+ if (lock == NULL || hooks == NULL) {
+ /*
+ ** Since resourcediscovery is defined with (activelock)*,
+ ** <D:activelock/> shouldn't be necessary for an empty lock.
+ */
+ return "";
+ }
+
+ /*
+ ** Note: it could be interesting to sum the lengths of the owners
+ ** and locktokens during this loop. However, the buffer
+ ** mechanism provides some rough padding so that we don't
+ ** really need to have an exact size. Further, constructing
+ ** locktoken strings could be relatively expensive.
+ */
+ for (lock_scan = lock; lock_scan != NULL; lock_scan = lock_scan->next)
+ count++;
+
+ /* if a buffer was not provided, then use an internal buffer */
+ if (pbuf == NULL)
+ pbuf = &work_buf;
+
+ /* reset the length before we start appending stuff */
+ pbuf->cur_len = 0;
+
+ /* prep the buffer with a "good" size */
+ dav_check_bufsize(p, pbuf, count * 300);
+
+ for (; lock != NULL; lock = lock->next) {
+ char tmp[100];
+
+#if DAV_DEBUG
+ if (lock->rectype == DAV_LOCKREC_INDIRECT_PARTIAL) {
+ /* ### crap. design error */
+ dav_buffer_append(p, pbuf,
+ "DESIGN ERROR: attempted to product an "
+ "activelock element from a partial, indirect "
+ "lock record. Creating an XML parsing error "
+ "to ease detection of this situation: <");
+ }
+#endif
+
+ dav_buffer_append(p, pbuf, "<D:activelock>" DEBUG_CR "<D:locktype>");
+ switch (lock->type) {
+ case DAV_LOCKTYPE_WRITE:
+ dav_buffer_append(p, pbuf, "<D:write/>");
+ break;
+ default:
+ /* ### internal error. log something? */
+ break;
+ }
+ dav_buffer_append(p, pbuf, "</D:locktype>" DEBUG_CR "<D:lockscope>");
+ switch (lock->scope) {
+ case DAV_LOCKSCOPE_EXCLUSIVE:
+ dav_buffer_append(p, pbuf, "<D:exclusive/>");
+ break;
+ case DAV_LOCKSCOPE_SHARED:
+ dav_buffer_append(p, pbuf, "<D:shared/>");
+ break;
+ default:
+ /* ### internal error. log something? */
+ break;
+ }
+ dav_buffer_append(p, pbuf, "</D:lockscope>" DEBUG_CR);
+ sprintf(tmp, "<D:depth>%s</D:depth>" DEBUG_CR,
+ lock->depth == DAV_INFINITY ? "infinity" : "0");
+ dav_buffer_append(p, pbuf, tmp);
+
+ if (lock->owner) {
+ /*
+ ** This contains a complete, self-contained <DAV:owner> element,
+ ** with namespace declarations and xml:lang handling. Just drop
+ ** it in.
+ */
+ dav_buffer_append(p, pbuf, lock->owner);
+ }
+
+ dav_buffer_append(p, pbuf, "<D:timeout>");
+ if (lock->timeout == DAV_TIMEOUT_INFINITE) {
+ dav_buffer_append(p, pbuf, "Infinite");
+ }
+ else {
+ time_t now = time(NULL);
+ sprintf(tmp, "Second-%lu", lock->timeout - now);
+ dav_buffer_append(p, pbuf, tmp);
+ }
+
+ dav_buffer_append(p, pbuf,
+ "</D:timeout>" DEBUG_CR
+ "<D:locktoken>" DEBUG_CR
+ "<D:href>");
+ dav_buffer_append(p, pbuf,
+ (*hooks->format_locktoken)(p, lock->locktoken));
+ dav_buffer_append(p, pbuf,
+ "</D:href>" DEBUG_CR
+ "</D:locktoken>" DEBUG_CR
+ "</D:activelock>" DEBUG_CR);
+ }
+
+ return pbuf->buf;
+}
+
+/*
+** dav_lock_parse_lockinfo: Validates the given xml_doc to contain a
+** lockinfo XML element, then populates a dav_lock structure
+** with its contents.
+*/
+dav_error * dav_lock_parse_lockinfo(request_rec *r,
+ const dav_resource *resource,
+ dav_lockdb *lockdb,
+ const dav_xml_doc *doc,
+ dav_lock **lock_request)
+{
+ const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
+ pool *p = r->pool;
+ dav_error *err;
+ dav_xml_elem *child;
+ dav_lock *lock;
+
+ if (!dav_validate_root(doc, "lockinfo")) {
+ return dav_new_error(p, HTTP_BAD_REQUEST, 0,
+ "The request body contains an unexpected "
+ "XML root element.");
+ }
+
+ if ((err = (*hooks->create_lock)(lockdb, resource, &lock)) != NULL) {
+ return dav_push_error(p, err->status, 0,
+ "Could not parse the lockinfo due to an "
+ "internal problem creating a lock structure.",
+ err);
+ }
+
+ lock->depth = dav_get_depth(r, DAV_INFINITY);
+ if (lock->depth == -1) {
+ return dav_new_error(p, HTTP_BAD_REQUEST, 0,
+ "An invalid Depth header was specified.");
+ }
+ lock->timeout = dav_get_timeout(r);
+
+ /* Parse elements in the XML body */
+ for (child = doc->root->first_child; child; child = child->next) {
+ if (strcmp(child->name, "locktype") == 0
+ && child->first_child
+ && lock->type == DAV_LOCKTYPE_UNKNOWN) {
+ if (strcmp(child->first_child->name, "write") == 0) {
+ lock->type = DAV_LOCKTYPE_WRITE;
+ continue;
+ }
+ }
+ if (strcmp(child->name, "lockscope") == 0
+ && child->first_child
+ && lock->scope == DAV_LOCKSCOPE_UNKNOWN) {
+ if (strcmp(child->first_child->name, "exclusive") == 0)
+ lock->scope = DAV_LOCKSCOPE_EXCLUSIVE;
+ else if (strcmp(child->first_child->name, "shared") == 0)
+ lock->scope = DAV_LOCKSCOPE_SHARED;
+ if (lock->scope != DAV_LOCKSCOPE_UNKNOWN)
+ continue;
+ }
+
+ if (strcmp(child->name, "owner") == 0 && lock->owner == NULL) {
+ const char *text;
+
+ /* quote all the values in the <DAV:owner> element */
+ dav_quote_xml_elem(p, child);
+
+ /*
+ ** Store a full <DAV:owner> element with namespace definitions
+ ** and an xml:lang definition, if applicable.
+ */
+ dav_xml2text(p, child, DAV_X2T_FULL_NS_LANG, doc->namespaces, NULL,
+ &text, NULL);
+ lock->owner = text;
+
+ continue;
+ }
+
+ return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0,
+ ap_psprintf(p,
+ "The server cannot satisfy the "
+ "LOCK request due to an unknown XML "
+ "element (\"%s\") within the "
+ "DAV:lockinfo element.",
+ child->name));
+ }
+
+ *lock_request = lock;
+ return NULL;
+}
+
+/* ---------------------------------------------------------------
+**
+** General lock functions
+**
+*/
+
+/* dav_lock_walker: Walker callback function to record indirect locks */
+static dav_error * dav_lock_walker(dav_walker_ctx *ctx, int calltype)
+{
+ dav_error *err;
+
+ /* We don't want to set indirects on the target */
+ if ((*ctx->resource->hooks->is_same_resource)(ctx->resource, ctx->root))
+ return NULL;
+
+ if ((err = (*ctx->lockdb->hooks->append_locks)(ctx->lockdb, ctx->resource,
+ 1,
+ ctx->lock)) != NULL) {
+ if (ap_is_HTTP_SERVER_ERROR(err->status)) {
+ /* ### add a higher-level description? */
+ return err;
+ }
+
+ /* add to the multistatus response */
+ dav_add_response(ctx, ctx->resource->uri, err->status, NULL);
+
+ /*
+ ** ### actually, this is probably wrong: we want to fail the whole
+ ** ### LOCK process if something goes bad. maybe the caller should
+ ** ### do a dav_unlock() (e.g. a rollback) if any errors occurred.
+ */
+ }
+
+ return NULL;
+}
+
+/*
+** dav_add_lock: Add a direct lock for resource, and indirect locks for
+** all children, bounded by depth.
+** ### assume request only contains one lock
+*/
+dav_error * dav_add_lock(request_rec *r, const dav_resource *resource,
+ dav_lockdb *lockdb, dav_lock *lock,
+ dav_response **response)
+{
+ const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
+ dav_error *err;
+ int depth = lock->depth;
+
+ *response = NULL;
+
+ /* Requested lock can be:
+ * Depth: 0 for null resource, existing resource, or existing collection
+ * Depth: Inf for existing collection
+ */
+
+ /*
+ ** 2518 9.2 says to ignore depth if target is not a collection (it has
+ ** no internal children); pretend the client gave the correct depth.
+ */
+ if (!resource->collection) {
+ depth = 0;
+ }
+
+ /* In all cases, first add direct entry in lockdb */
+
+ /*
+ ** Append the new (direct) lock to the resource's existing locks.
+ **
+ ** Note: this also handles locknull resources
+ */
+ if ((err = (*hooks->append_locks)(lockdb, resource, 0, lock)) != NULL) {
+ /* ### maybe add a higher-level description */
+ return err;
+ }
+
+ if (depth > 0) {
+ /* Walk existing collection and set indirect locks */
+ dav_walker_ctx ctx = { 0 };
+
+ ctx.walk_type = DAV_WALKTYPE_ALL | DAV_WALKTYPE_AUTH;
+ ctx.postfix = 0;
+ ctx.func = dav_lock_walker;
+ ctx.pool = r->pool;
+ ctx.r = r;
+ ctx.resource = resource;
+ ctx.lockdb = lockdb;
+ ctx.lock = lock;
+
+ dav_buffer_init(r->pool, &ctx.uri, resource->uri);
+
+ err = (*resource->hooks->walk)(&ctx, DAV_INFINITY);
+ if (err != NULL) {
+ /* implies a 5xx status code occurred. screw the multistatus */
+ return err;
+ }
+
+ if (ctx.response != NULL) {
+ /* manufacture a 207 error for the multistatus response */
+ *response = ctx.response;
+ return dav_new_error(r->pool, HTTP_MULTI_STATUS, 0,
+ "Error(s) occurred on resources during the "
+ "addition of a depth lock.");
+ }
+ }
+
+ return NULL;
+}
+
+/*
+** dav_lock_query: Opens the lock database. Returns a linked list of
+** dav_lock structures for all direct locks on path.
+*/
+dav_error * dav_lock_query(dav_lockdb *lockdb, const dav_resource *resource,
+ dav_lock **locks)
+{
+ /* If no lock database, return empty result */
+ if (lockdb == NULL) {
+ *locks = NULL;
+ return NULL;
+ }
+
+ /* ### insert a higher-level description? */
+ return (*lockdb->hooks->get_locks)(lockdb, resource,
+ DAV_GETLOCKS_RESOLVED,
+ locks);
+}
+
+/* dav_unlock_walker: Walker callback function to remove indirect locks */
+static dav_error * dav_unlock_walker(dav_walker_ctx *ctx, int calltype)
+{
+ dav_error *err;
+
+ if ((err = (*ctx->lockdb->hooks->remove_lock)(ctx->lockdb, ctx->resource,
+ ctx->locktoken)) != NULL) {
+ /* ### should we stop or return a multistatus? looks like STOP */
+ /* ### add a higher-level description? */
+ return err;
+ }
+
+ return NULL;
+}
+
+/*
+** dav_get_direct_resource:
+**
+** Find a lock on the specified resource, then return the resource the
+** lock was applied to (in other words, given a (possibly) indirect lock,
+** return the direct lock's corresponding resource).
+**
+** If the lock is an indirect lock, this usually means traversing up the
+** namespace [repository] hierarchy. Note that some lock providers may be
+** able to return this information with a traversal.
+*/
+static dav_error * dav_get_direct_resource(pool *p,
+ dav_lockdb *lockdb,
+ const dav_locktoken *locktoken,
+ const dav_resource *resource,
+ const dav_resource **direct_resource)
+{
+ if (lockdb->hooks->lookup_resource != NULL) {
+ return (*lockdb->hooks->lookup_resource)(lockdb, locktoken,
+ resource, direct_resource);
+ }
+
+ *direct_resource = NULL;
+
+ /* Find the top of this lock-
+ * If r->filename's direct locks include locktoken, use r->filename.
+ * If r->filename's indirect locks include locktoken, retry r->filename/..
+ * Else fail.
+ */
+ while (resource != NULL) {
+ dav_error *err;
+ dav_lock *lock;
+
+ /*
+ ** Find the lock specified by <locktoken> on <resource>. If it is
+ ** an indirect lock, then partial results are okay. We're just
+ ** trying to find the thing and know whether it is a direct or
+ ** an indirect lock.
+ */
+ if ((err = (*lockdb->hooks->find_lock)(lockdb, resource, locktoken,
+ 1, &lock)) != NULL) {
+ /* ### add a higher-level desc? */
+ return err;
+ }
+
+ /* not found! that's an error. */
+ if (lock == NULL) {
+ return dav_new_error(p, HTTP_BAD_REQUEST, 0,
+ "The specified locktoken does not correspond "
+ "to an existing lock on this resource.");
+ }
+
+ if (lock->rectype == DAV_LOCKREC_DIRECT) {
+ /* we found the direct lock. return this resource. */
+
+ *direct_resource = resource;
+ return NULL;
+ }
+
+ /* the lock was indirect. move up a level in the URL namespace */
+ resource = (*resource->hooks->get_parent_resource)(resource);
+ }
+
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "The lock database is corrupt. A direct lock could "
+ "not be found for the corresponding indirect lock "
+ "on this resource.");
+}
+
+/*
+** dav_unlock: Removes all direct and indirect locks for r->filename,
+** with given locktoken. If locktoken == null_locktoken, all locks
+** are removed. If r->filename represents an indirect lock,
+** we must unlock the appropriate direct lock.
+** Returns OK or appropriate HTTP_* response and logs any errors.
+**
+** ### We've already crawled the tree to ensure everything was locked
+** by us; there should be no need to incorporate a rollback.
+*/
+int dav_unlock(request_rec *r, const dav_resource *resource,
+ const dav_locktoken *locktoken)
+{
+ int result;
+ dav_lockdb *lockdb;
+ const dav_resource *lock_resource = resource;
+ const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
+ const dav_hooks_repository *repos_hooks = resource->hooks;
+ dav_error *err;
+
+ /* If no locks provider, we shouldn't have been called */
+ if (hooks == NULL) {
+ /* ### map result to something nice; log an error */
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* 2518 requires the entire lock to be removed if resource/locktoken
+ * point to an indirect lock. We need resource of the _direct_
+ * lock in order to walk down the tree and remove the locks. So,
+ * If locktoken != null_locktoken,
+ * Walk up the resource hierarchy until we see a direct lock.
+ * Or, we could get the direct lock's db/key, pick out the URL
+ * and do a subrequest. I think walking up is faster and will work
+ * all the time.
+ * Else
+ * Just start removing all locks at and below resource.
+ */
+
+ if ((err = (*hooks->open_lockdb)(r, 0, 1, &lockdb)) != NULL) {
+ /* ### return err! maybe add a higher-level desc */
+ /* ### map result to something nice; log an error */
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (locktoken != NULL
+ && (err = dav_get_direct_resource(r->pool, lockdb,
+ locktoken, resource,
+ &lock_resource)) != NULL) {
+ /* ### add a higher-level desc? */
+ /* ### should return err! */
+ return err->status;
+ }
+
+ /* At this point, lock_resource/locktoken refers to a direct lock (key), ie
+ * the root of a depth > 0 lock, or locktoken is null.
+ */
+ if ((err = (*hooks->remove_lock)(lockdb, lock_resource,
+ locktoken)) != NULL) {
+ /* ### add a higher-level desc? */
+ /* ### return err! */
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (lock_resource->collection) {
+ dav_walker_ctx ctx = { 0 };
+
+ ctx.walk_type = DAV_WALKTYPE_ALL | DAV_WALKTYPE_LOCKNULL;
+ ctx.postfix = 0;
+ ctx.func = dav_unlock_walker;
+ ctx.pool = r->pool;
+ ctx.resource = lock_resource;
+ ctx.r = r;
+ ctx.lockdb = lockdb;
+ ctx.locktoken = locktoken;
+
+ dav_buffer_init(r->pool, &ctx.uri, lock_resource->uri);
+
+ err = (*repos_hooks->walk)(&ctx, DAV_INFINITY);
+
+ /* ### fix this! */
+ result = err == NULL ? OK : err->status;
+ }
+ else
+ result = OK;
+
+ (*hooks->close_lockdb)(lockdb);
+
+ return result;
+}
+
+/* dav_inherit_walker: Walker callback function to inherit locks */
+static dav_error * dav_inherit_walker(dav_walker_ctx *ctx, int calltype)
+{
+ if (ctx->skip_root
+ && (*ctx->resource->hooks->is_same_resource)(ctx->resource,
+ ctx->root)) {
+ return NULL;
+ }
+
+ /* ### maybe add a higher-level desc */
+ return (*ctx->lockdb->hooks->append_locks)(ctx->lockdb, ctx->resource, 1,
+ ctx->lock);
+}
+
+/*
+** dav_inherit_locks: When a resource or collection is added to a collection,
+** locks on the collection should be inherited to the resource/collection.
+** (MOVE, MKCOL, etc) Here we propagate any direct or indirect locks from
+** parent of resource to resource and below.
+*/
+static dav_error * dav_inherit_locks(request_rec *r, dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int use_parent)
+{
+ dav_error *err;
+ const dav_resource *which_resource;
+ dav_lock *locks;
+ dav_lock *scan;
+ dav_lock *prev;
+ dav_walker_ctx ctx = { 0 };
+ const dav_hooks_repository *repos_hooks = resource->hooks;
+
+ if (use_parent) {
+ which_resource = (*repos_hooks->get_parent_resource)(resource);
+ if (which_resource == NULL) {
+ /* ### map result to something nice; log an error */
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
+ "Could not fetch parent resource. Unable to "
+ "inherit locks from the parent and apply "
+ "them to this resource.");
+ }
+ }
+ else {
+ which_resource = resource;
+ }
+
+ if ((err = (*lockdb->hooks->get_locks)(lockdb, which_resource,
+ DAV_GETLOCKS_PARTIAL,
+ &locks)) != NULL) {
+ /* ### maybe add a higher-level desc */
+ return err;
+ }
+
+ if (locks == NULL) {
+ /* No locks to propagate, just return */
+ return NULL;
+ }
+
+ /*
+ ** (1) Copy all indirect locks from our parent;
+ ** (2) Create indirect locks for the depth infinity, direct locks
+ ** in our parent.
+ **
+ ** The append_locks call in the walker callback will do the indirect
+ ** conversion, but we need to remove any direct locks that are NOT
+ ** depth "infinity".
+ */
+ for (scan = locks, prev = NULL;
+ scan != NULL;
+ prev = scan, scan = scan->next) {
+
+ if (scan->rectype == DAV_LOCKREC_DIRECT
+ && scan->depth != DAV_INFINITY) {
+
+ if (prev == NULL)
+ locks = scan->next;
+ else
+ prev->next = scan->next;
+ }
+ }
+
+ /* <locks> has all our new locks. Walk down and propagate them. */
+
+ ctx.walk_type = DAV_WALKTYPE_ALL | DAV_WALKTYPE_LOCKNULL;
+ ctx.postfix = 0;
+ ctx.func = dav_inherit_walker;
+ ctx.pool = r->pool;
+ ctx.resource = resource;
+ ctx.r = r;
+ ctx.lockdb = lockdb;
+ ctx.lock = locks;
+ ctx.skip_root = !use_parent;
+
+ dav_buffer_init(r->pool, &ctx.uri, resource->uri);
+
+ return (*repos_hooks->walk)(&ctx, DAV_INFINITY);
+}
+
+/* ---------------------------------------------------------------
+**
+** Functions dealing with lock-null resources
+**
+*/
+
+/*
+** dav_get_resource_state: Returns the state of the resource
+** r->filename: DAV_RESOURCE_NULL, DAV_RESOURCE_LOCK_NULL,
+** or DAV_RESOURCE_EXIST.
+**
+** Returns DAV_RESOURCE_ERROR if an error occurs.
+*/
+int dav_get_resource_state(request_rec *r, const dav_resource *resource)
+{
+ const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
+
+ if (resource->exists)
+ return DAV_RESOURCE_EXISTS;
+
+ if (hooks != NULL) {
+ dav_error *err;
+ dav_lockdb *lockdb;
+ int locks_present;
+
+ /*
+ ** A locknull resource has the form:
+ **
+ ** known-dir "/" locknull-file
+ **
+ ** It would be nice to look into <resource> to verify this form,
+ ** but it does not have enough information for us. Instead, we
+ ** can look at the path_info. If the form does not match, then
+ ** there is no way we could have a locknull resource -- it must
+ ** be a plain, null resource.
+ **
+ ** Apache sets r->filename to known-dir/unknown-file and r->path_info
+ ** to "" for the "proper" case. If anything is in path_info, then
+ ** it can't be a locknull resource.
+ **
+ ** ### I bet this path_info hack doesn't work for repositories.
+ ** ### Need input from repository implementors! What kind of
+ ** ### restructure do we need? New provider APIs?
+ */
+ if (r->path_info != NULL && *r->path_info != '\0') {
+ return DAV_RESOURCE_NULL;
+ }
+
+ if ((err = (*hooks->open_lockdb)(r, 1, 1, &lockdb)) == NULL) {
+ /* note that we might see some expired locks... *shrug* */
+ err = (*hooks->has_locks)(lockdb, resource, &locks_present);
+ (*hooks->close_lockdb)(lockdb);
+ }
+
+ if (err != NULL) {
+ /* ### don't log an error. return err. add higher-level desc. */
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "Failed to query lock-null status for %s",
+ r->filename);
+
+ return DAV_RESOURCE_ERROR;
+ }
+
+ if (locks_present)
+ return DAV_RESOURCE_LOCK_NULL;
+ }
+
+ return DAV_RESOURCE_NULL;
+}
+
+dav_error * dav_notify_created(request_rec *r,
+ dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int resource_state,
+ int depth)
+{
+ dav_error *err;
+
+ if (resource_state == DAV_RESOURCE_LOCK_NULL) {
+
+ /*
+ ** The resource is no longer a locknull resource. This will remove
+ ** the special marker.
+ **
+ ** Note that a locknull resource has already inherited all of the
+ ** locks from the parent. We do not need to call dav_inherit_locks.
+ **
+ ** NOTE: some lock providers record locks for locknull resources using
+ ** a different key than for regular resources. this will shift
+ ** the lock information between the two key types.
+ */
+ (void)(*lockdb->hooks->remove_locknull_state)(lockdb, resource);
+
+ /*
+ ** There are resources under this one, which are new. We must
+ ** propagate the locks down to the new resources.
+ */
+ if (depth > 0 &&
+ (err = dav_inherit_locks(r, lockdb, resource, 0)) != NULL) {
+ /* ### add a higher level desc? */
+ return err;
+ }
+ }
+ else if (resource_state == DAV_RESOURCE_NULL) {
+
+ /* ### should pass depth to dav_inherit_locks so that it can
+ ** ### optimize for the depth==0 case.
+ */
+
+ /* this resource should inherit locks from its parent */
+ if ((err = dav_inherit_locks(r, lockdb, resource, 1)) != NULL) {
+
+ err = dav_push_error(r->pool, err->status, 0,
+ "The resource was created successfully, but "
+ "there was a problem inheriting locks from "
+ "the parent resource.",
+ err);
+ return err;
+ }
+ }
+ /* else the resource already exists and its locks are correct. */
+
+ return NULL;
+}
diff --git a/modules/echo/.cvsignore b/modules/echo/.cvsignore
new file mode 100644
index 0000000000..f2f7a70d2c
--- /dev/null
+++ b/modules/echo/.cvsignore
@@ -0,0 +1,10 @@
+.deps
+.libs
+*.la
+modules.mk
+Makefile
+*.lo
+*.slo
+*.so
+*.dll
+*.def
diff --git a/modules/echo/Makefile.in b/modules/echo/Makefile.in
new file mode 100644
index 0000000000..167b343d0d
--- /dev/null
+++ b/modules/echo/Makefile.in
@@ -0,0 +1,3 @@
+
+include $(top_srcdir)/build/special.mk
+
diff --git a/modules/echo/config.m4 b/modules/echo/config.m4
new file mode 100644
index 0000000000..822b0bdfb2
--- /dev/null
+++ b/modules/echo/config.m4
@@ -0,0 +1,18 @@
+dnl modules enabled in this directory by default
+
+dnl AC_DEFUN(modulename, modulestructname, defaultonoroff, configmacros)
+dnl XXX - Need to allow --enable-module to fail if optional config fails
+
+AC_DEFUN(APACHE_CHECK_STANDARD_MODULE, [
+ APACHE_MODULE([$1],[$2],,[$3],[$4],[$5])
+])
+
+APACHE_MODPATH_INIT(echo)
+
+APACHE_CHECK_STANDARD_MODULE(echo, ECHO server, , no)
+
+LTFLAGS="$LTFLAGS -export-dynamic"
+
+APACHE_MODPATH_FINISH
+
+APACHE_SUBST(STANDARD_LIBS)
diff --git a/modules/experimental/Makefile.in b/modules/experimental/Makefile.in
new file mode 100644
index 0000000000..7c5c149d85
--- /dev/null
+++ b/modules/experimental/Makefile.in
@@ -0,0 +1,3 @@
+# a modules Makefile has no explicit targets -- they will be defined by
+# whatever modules are enabled. just grab special.mk to deal with this.
+include $(top_srcdir)/build/special.mk
diff --git a/modules/experimental/config.m4 b/modules/experimental/config.m4
new file mode 100644
index 0000000000..665fc5db74
--- /dev/null
+++ b/modules/experimental/config.m4
@@ -0,0 +1,7 @@
+
+APACHE_MODPATH_INIT(experimental)
+
+APACHE_MODULE(mmap_static, memory mapped file caching, , , no)
+APACHE_MODULE(charset_lite, character set translation, , , no)
+
+APACHE_MODPATH_FINISH
diff --git a/modules/experimental/mod_cache.c b/modules/experimental/mod_cache.c
new file mode 100644
index 0000000000..003156dc26
--- /dev/null
+++ b/modules/experimental/mod_cache.c
@@ -0,0 +1,130 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "apr_strings.h"
+#include "ap_config.h"
+#include "util_filter.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_script.h"
+#include "http_core.h"
+#include "mod_cache.h"
+#include "ap_hooks.h"
+
+module MODULE_VAR_EXPORT cache_module;
+
+AP_HOOK_STRUCT(
+ AP_HOOK_LINK(serve_cache)
+ AP_HOOK_LINK(store_cache)
+)
+
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,serve_cache,(request_rec *r),(r),DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,store_cache,(request_rec *r, ap_bucket_brigade *bb, void **cf),
+ (r, bb, cf),DECLINED)
+
+static int cache_handler(request_rec *r)
+{
+ /* I am sure there is common error checking that belongs in this function,
+ * but I'm not sure what it is.
+ */
+ return ap_run_serve_cache(r);
+}
+
+typedef struct cache_struct {
+ void *cf;
+} cache_struct;
+
+static int cache_filter(ap_filter_t *f, ap_bucket_brigade *bb)
+{
+ cache_struct *ctx = f->ctx;
+
+ if (ctx == NULL) {
+ f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+ }
+
+ ap_run_store_cache(f->r, bb, &ctx->cf);
+ ap_pass_brigade(f->next, bb);
+ return APR_SUCCESS;
+}
+
+static void cache_register_hook(void)
+{
+ ap_register_output_filter("CACHE", cache_filter, AP_FTYPE_HTTP_HEADER);
+}
+
+static const handler_rec cache_handlers[] =
+{
+ {"*/*", cache_handler},
+ {NULL}
+};
+
+module MODULE_VAR_EXPORT cache_module = {
+ STANDARD20_MODULE_STUFF,
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ NULL, /* command apr_table_t */
+ cache_handlers, /* handlers */
+ cache_register_hook /* register hooks */
+};
diff --git a/modules/experimental/mod_cache.h b/modules/experimental/mod_cache.h
new file mode 100644
index 0000000000..81a4d37512
--- /dev/null
+++ b/modules/experimental/mod_cache.h
@@ -0,0 +1,67 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "ap_buckets.h"
+#include "ap_hooks.h"
+#include "httpd.h"
+
+typedef struct cache_funcs cache_funcs;
+
+AP_DECLARE_HOOK(int,serve_cache,(request_rec *r));
+AP_DECLARE_HOOK(int,store_cache,(request_rec *r, ap_bucket_brigade *bb, void **cf));
+
diff --git a/modules/experimental/mod_case_filter.c b/modules/experimental/mod_case_filter.c
new file mode 100644
index 0000000000..b035594b71
--- /dev/null
+++ b/modules/experimental/mod_case_filter.c
@@ -0,0 +1,113 @@
+// Ben messing around...
+
+#include "httpd.h"
+#include "http_config.h"
+#include "apr_general.h"
+#include "util_filter.h"
+#include "ap_buckets.h"
+#include "http_request.h"
+
+static const char s_szCaseFilterName[]="CaseFilter";
+module case_filter_module;
+
+typedef struct
+ {
+ int bEnabled;
+ } CaseFilterConfig;
+
+static void *CaseFilterCreateServerConfig(apr_pool_t *p,server_rec *s)
+ {
+ CaseFilterConfig *pConfig=apr_pcalloc(p,sizeof *pConfig);
+
+ pConfig->bEnabled=0;
+
+ return pConfig;
+ }
+
+static void CaseFilterInsertFilter(request_rec *r)
+ {
+ CaseFilterConfig *pConfig=ap_get_module_config(r->server->module_config,
+ &case_filter_module);
+
+ if(!pConfig->bEnabled)
+ return;
+
+ ap_add_output_filter(s_szCaseFilterName,NULL,r,r->connection);
+ }
+
+static apr_status_t CaseFilterOutFilter(ap_filter_t *f,
+ ap_bucket_brigade *pbbIn)
+ {
+ ap_bucket *pbktIn;
+ ap_bucket_brigade *pbbOut;
+
+ // XXX: is this the most appropriate pool?
+ pbbOut=ap_brigade_create(f->r->pool);
+ AP_BRIGADE_FOREACH(pbktIn,pbbIn)
+ {
+ const char *data;
+ apr_size_t len;
+ char *buf;
+ apr_size_t n;
+ ap_bucket *pbktOut;
+
+ if(AP_BUCKET_IS_EOS(pbktIn))
+ {
+ // XXX: why can't I reuse pbktIn???
+ ap_bucket *pbktEOS=ap_bucket_create_eos();
+ AP_BRIGADE_INSERT_TAIL(pbbOut,pbktEOS);
+ break;
+ }
+
+ // read
+ ap_bucket_read(pbktIn,&data,&len,1);
+
+ // write
+ buf=apr_palloc(f->r->pool,len);
+ for(n=0 ; n < len ; ++n)
+ buf[n]=toupper(data[n]);
+
+ // XXX: should we use a heap bucket instead? Or a transient (in
+ // which case we need a separate brigade for each bucket)?
+ pbktOut=ap_bucket_create_pool(buf,len,f->r->pool);
+ AP_BRIGADE_INSERT_TAIL(pbbOut,pbktOut);
+ }
+
+ // XXX: is there any advantage to passing a brigade for each bucket?
+ return ap_pass_brigade(f->next,pbbOut);
+ }
+
+static const char *CaseFilterEnable(cmd_parms *cmd, void *dummy, int arg)
+ {
+ CaseFilterConfig *pConfig=ap_get_module_config(cmd->server->module_config,
+ &case_filter_module);
+ pConfig->bEnabled=arg;
+
+ return NULL;
+ }
+
+static const command_rec CaseFilterCmds[] =
+ {
+ AP_INIT_FLAG("CaseFilter", CaseFilterEnable, NULL, RSRC_CONF,
+ "Run a case filter on this host"),
+ { NULL }
+ };
+
+static void CaseFilterRegisterHooks(void)
+ {
+ ap_hook_insert_filter(CaseFilterInsertFilter,NULL,NULL,AP_HOOK_MIDDLE);
+ ap_register_output_filter(s_szCaseFilterName,CaseFilterOutFilter,
+ AP_FTYPE_CONTENT);
+ }
+
+module case_filter_module =
+{
+ STANDARD20_MODULE_STUFF,
+ NULL,
+ NULL,
+ CaseFilterCreateServerConfig,
+ NULL,
+ CaseFilterCmds,
+ NULL,
+ CaseFilterRegisterHooks
+};
diff --git a/modules/experimental/mod_charset_lite.c b/modules/experimental/mod_charset_lite.c
new file mode 100644
index 0000000000..03eb6d6939
--- /dev/null
+++ b/modules/experimental/mod_charset_lite.c
@@ -0,0 +1,296 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/*
+ * simple hokey charset recoding configuration module
+ *
+ * See mod_ebcdic and mod_charset for more thought-out examples. This
+ * one is just so Jeff can learn how a module works and experiment with
+ * basic character set recoding configuration.
+ *
+ * !!!This is an extremely cheap ripoff of mod_charset.c from Russian Apache!!!
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "util_charset.h"
+
+#ifndef APACHE_XLATE
+#error mod_charset_lite cannot work without APACHE_XLATE enabled
+#endif
+
+typedef struct charset_dir_t {
+ enum {NO_DEBUG = 1, DEBUG} debug; /* whether or not verbose logging is enabled; 0
+ means uninitialized */
+ const char *charset_source; /* source encoding */
+ const char *charset_default; /* how to ship on wire */
+} charset_dir_t;
+
+module charset_lite_module;
+
+static void *create_charset_dir_conf(ap_pool_t *p,char *dummy)
+{
+ return ap_pcalloc(p,sizeof(charset_dir_t));
+}
+
+static void *merge_charset_dir_conf(ap_pool_t *p, void *basev, void *overridesv)
+{
+ charset_dir_t *a = (charset_dir_t *)ap_pcalloc (p, sizeof(charset_dir_t));
+ charset_dir_t *base = (charset_dir_t *)basev,
+ *over = (charset_dir_t *)overridesv;
+
+ /* If it is defined in the current container, use it. Otherwise, use the one
+ * from the enclosing container.
+ */
+
+ a->debug =
+ over->debug ? over->debug : base->debug;
+ a->charset_default =
+ over->charset_default ? over->charset_default : base->charset_default;
+ a->charset_source =
+ over->charset_source ? over->charset_source : base->charset_source;
+ return a;
+}
+
+/* CharsetSourceEnc charset
+ */
+static const char *add_charset_source(cmd_parms *cmd, charset_dir_t *dc,
+ char *name)
+{
+ dc->charset_source = name;
+ return NULL;
+}
+
+/* CharsetDefault charset
+ */
+static const char *add_charset_default(cmd_parms *cmd, charset_dir_t *dc,
+ char *name)
+{
+ dc->charset_default = name;
+ return NULL;
+}
+
+/* CharsetDefault charset
+ */
+static const char *add_charset_debug(cmd_parms *cmd, charset_dir_t *dc, int arg)
+{
+ if (arg) {
+ dc->debug = DEBUG;
+ }
+ else {
+ dc->debug = NO_DEBUG;
+ }
+
+ return NULL;
+}
+
+/* find_code_page() is a fixup hook that decides if translation should be
+ * enabled
+ */
+static int find_code_page(request_rec *r)
+{
+ charset_dir_t *dc = ap_get_module_config(r->per_dir_config, &charset_lite_module);
+ ap_status_t rv;
+ ap_xlate_t *xlate;
+ const char *mime_type;
+ int debug = dc->debug == DEBUG;
+
+ mime_type = r->content_type ? r->content_type : ap_default_type(r);
+
+ if (debug) {
+ ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
+ "Entering handler, URI: %s FILENAME: %s ARGS: %s PATH_INFO: %s "
+ "MIMETYPE: %s FLAGS: %d SUBREQ: %s, REDIR: %s, PROXY: %s",
+ r->uri, r->filename, r->args, r->path_info, mime_type,
+ r->rrx ? 1 : 0,
+ r->main?"YES":"NO",r->prev?"YES":"NO",
+ r->proxyreq ? "YES" : "NO");
+ }
+
+ /* catch proxy requests */
+ if (r->proxyreq) return DECLINED;
+ /* mod_rewrite indicators */
+ if (!strncmp(r->filename, "redirect:", 9)) return DECLINED;
+ if (!strncmp(r->filename, "gone:", 5)) return DECLINED;
+ if (!strncmp(r->filename, "passthrough:", 12)) return DECLINED;
+ if (!strncmp(r->filename, "forbidden:", 10)) return DECLINED;
+
+ /* If we don't have a full directory configuration, bail out.
+ */
+ if (!dc->charset_source || !dc->charset_default) {
+ if (debug) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
+ "incomplete configuration: src %s, dst %s",
+ dc->charset_source ? dc->charset_source : "unspecified",
+ dc->charset_default ? dc->charset_default : "unspecified");
+ }
+ return DECLINED;
+ }
+
+ /* If this is a subrequest, bail out. We don't want to be setting up
+ * translation just because something like mod_autoindex wants to find the
+ * mime type for directory objects.
+ * (I won't swear that there aren't cases where we need to process
+ * subrequests :) ).
+ */
+ if (r->main) {
+ if (debug) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
+ "skipping subrequest");
+ }
+ return DECLINED;
+ }
+
+ /* If mime type isn't text or message, bail out.
+ */
+ if (strncasecmp(mime_type, "text/", 5) &&
+ strncasecmp(mime_type, "message/", 8)) {
+ if (debug) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
+ "mime type is %s; no translation selected",
+ mime_type);
+ }
+ return DECLINED;
+ }
+
+ if (debug) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
+ "dc: %X charset_source: %s charset_default: %s",
+ (unsigned)dc,
+ dc && dc->charset_source ? dc->charset_source : "(none)",
+ dc && dc->charset_default ? dc->charset_default : "(none)");
+ }
+
+ rv = ap_xlate_open(&xlate, dc->charset_default, dc->charset_source, r->pool);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
+ "can't open translation %s->%s, error %d\n",
+ dc->charset_source, dc->charset_default, rv);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ rv = ap_set_content_xlate(r, 1, xlate);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
+ "can't set content translation, error %d\n", rv);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ rv = ap_bsetopt(r->connection->client, BO_WXLATE, &r->rrx->to_net);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
+ "can't set translation; BO_WXLATE->%d\n", rv);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ return DECLINED;
+}
+
+static const command_rec cmds[] =
+{
+ {
+ "CharsetSourceEnc",
+ add_charset_source,
+ NULL,
+ OR_FILEINFO,
+ TAKE1,
+ "source (html,cgi,ssi) file charset"
+ },
+ {
+ "CharsetDefault",
+ add_charset_default,
+ NULL,
+ OR_FILEINFO,
+ TAKE1,
+ "name of default charset"
+ },
+ {
+ "CharsetDebug",
+ add_charset_debug,
+ NULL,
+ OR_FILEINFO,
+ FLAG,
+ "mod_charset_lite debug flag"
+ },
+ {NULL}
+};
+
+static void register_hooks(void)
+{
+ ap_hook_fixups(find_code_page, NULL, NULL, AP_HOOK_MIDDLE);
+}
+
+module charset_lite_module =
+{
+ STANDARD20_MODULE_STUFF,
+ create_charset_dir_conf,
+ merge_charset_dir_conf,
+ NULL,
+ NULL,
+ cmds,
+ NULL,
+ register_hooks,
+};
+
diff --git a/modules/experimental/mod_disk_cache.c b/modules/experimental/mod_disk_cache.c
new file mode 100644
index 0000000000..f0c916530e
--- /dev/null
+++ b/modules/experimental/mod_disk_cache.c
@@ -0,0 +1,171 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "mod_cache.h"
+#include "apr_file_io.h"
+#include "apr_strings.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "util_filter.h"
+
+module MODULE_VAR_EXPORT disk_cache_module;
+
+static int disk_serve(request_rec *r)
+{
+ ap_bucket *e;
+ ap_bucket_brigade *bb = ap_brigade_create(r->pool);
+ const char *filename;
+ apr_file_t *fd = NULL;
+ apr_status_t rv;
+ ap_filter_t *f;
+ char str[256];
+ apr_off_t offset = 0;
+
+ filename = ap_server_root_relative(r->pool,
+ apr_pstrcat(r->pool, "proxy", r->uri, NULL));
+ if ((rv = apr_open(&fd, filename, APR_READ,
+ APR_UREAD, r->connection->pool)) != APR_SUCCESS) {
+ return DECLINED;
+ }
+
+ /* skip the cached headers. */
+ do {
+ apr_fgets(str, 256, fd);
+ offset += strlen(str);
+ } while (strcmp(str, CRLF));
+
+ /* If we are serving from the cache, we don't want to try to cache it
+ * again.
+ */
+ for ((f = r->output_filters); (f = f->next);) {
+ if (!strcmp(f->frec->name, "CACHE")) {
+ ap_remove_output_filter(f);
+ }
+ }
+
+ e = ap_bucket_create_file(fd, offset, r->finfo.size);
+
+ AP_BRIGADE_INSERT_HEAD(bb, e);
+ e = ap_bucket_create_eos();
+ AP_BRIGADE_INSERT_TAIL(bb, e);
+
+ ap_pass_brigade(r->output_filters, bb);
+ return OK;
+}
+
+typedef struct cache_struct {
+ const char *filename;
+ apr_file_t *fd;
+ int state;
+} cache_struct;
+
+static int disk_cache(request_rec *r, ap_bucket_brigade *bb, void **cf)
+{
+ cache_struct *ctx = *cf;
+ ap_bucket *e;
+
+ if (ctx == NULL) {
+ *cf = ctx = apr_pcalloc(r->pool, sizeof(*ctx));
+ }
+ if (ctx->filename == NULL) {
+ apr_status_t rv;
+ apr_make_dir(ap_server_root_relative(r->pool, "proxy"), APR_UREAD | APR_UWRITE | APR_UEXECUTE | APR_GREAD | APR_GWRITE, r->pool);
+
+ /* currently, we are using the uri as the cache key. This is
+ * probably wrong, but it is much better than a hard-coded filename.
+ */
+ ctx->filename = ap_server_root_relative(r->pool,
+ apr_pstrcat(r->pool, "proxy", r->uri, NULL));
+ if ((rv = apr_open(&ctx->fd, ctx->filename,
+ APR_WRITE | APR_CREATE | APR_TRUNCATE | APR_BUFFERED,
+ APR_UREAD | APR_UWRITE, r->pool)) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ "Could not create cache file");
+ *cf = NULL;
+ return DECLINED;
+ }
+ }
+ AP_BRIGADE_FOREACH(e, bb) {
+ const char *str;
+ apr_ssize_t length;
+
+ ap_bucket_read(e, &str, &length, 0);
+ apr_write(ctx->fd, str, &length);
+ }
+ if (AP_BUCKET_IS_EOS(AP_BRIGADE_LAST(bb))) {
+ apr_close(ctx->fd);
+ }
+ return OK;
+}
+
+static void disk_cache_register_hook(void)
+{
+ ap_hook_store_cache(disk_cache, NULL, NULL, AP_HOOK_MIDDLE);
+ ap_hook_serve_cache(disk_serve, NULL, NULL, AP_HOOK_MIDDLE);
+}
+
+module MODULE_VAR_EXPORT disk_cache_module = {
+ STANDARD20_MODULE_STUFF,
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ NULL, /* command apr_table_t */
+ NULL, /* handlers */
+ disk_cache_register_hook /* register hooks */
+};
diff --git a/modules/filters/.cvsignore b/modules/filters/.cvsignore
new file mode 100644
index 0000000000..f2f7a70d2c
--- /dev/null
+++ b/modules/filters/.cvsignore
@@ -0,0 +1,10 @@
+.deps
+.libs
+*.la
+modules.mk
+Makefile
+*.lo
+*.slo
+*.so
+*.dll
+*.def
diff --git a/modules/filters/Makefile.in b/modules/filters/Makefile.in
new file mode 100644
index 0000000000..167b343d0d
--- /dev/null
+++ b/modules/filters/Makefile.in
@@ -0,0 +1,3 @@
+
+include $(top_srcdir)/build/special.mk
+
diff --git a/modules/filters/config.m4 b/modules/filters/config.m4
new file mode 100644
index 0000000000..2b8589e3e5
--- /dev/null
+++ b/modules/filters/config.m4
@@ -0,0 +1,18 @@
+dnl modules enabled in this directory by default
+
+dnl AC_DEFUN(modulename, modulestructname, defaultonoroff, configmacros)
+dnl XXX - Need to allow --enable-module to fail if optional config fails
+
+AC_DEFUN(APACHE_CHECK_STANDARD_MODULE, [
+ APACHE_MODULE([$1],[$2],,[$3],[$4],[$5])
+])
+
+APACHE_MODPATH_INIT(filters)
+
+APACHE_CHECK_STANDARD_MODULE(include, Server Side Includes, includes, yes)
+
+LTFLAGS="$LTFLAGS -export-dynamic"
+
+APACHE_MODPATH_FINISH
+
+APACHE_SUBST(STANDARD_LIBS)
diff --git a/modules/filters/mod_include.h b/modules/filters/mod_include.h
new file mode 100644
index 0000000000..554d9998bb
--- /dev/null
+++ b/modules/filters/mod_include.h
@@ -0,0 +1,206 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef _MOD_INCLUDE_H
+#define _MOD_INCLUDE_H 1
+
+
+
+#define STARTING_SEQUENCE "<!--#"
+#define ENDING_SEQUENCE "-->"
+
+#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
+#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
+#define SIZEFMT_BYTES 0
+#define SIZEFMT_KMG 1
+#define TMP_BUF_SIZE 1024
+#ifdef CHARSET_EBCDIC
+#define RAW_ASCII_CHAR(ch) apr_xlate_conv_byte(ap_hdrs_from_ascii, (unsigned char)ch)
+#else /*CHARSET_EBCDIC*/
+#define RAW_ASCII_CHAR(ch) (ch)
+#endif /*CHARSET_EBCDIC*/
+
+module AP_MODULE_DECLARE_DATA includes_module;
+
+/* just need some arbitrary non-NULL pointer which can't also be a request_rec */
+#define NESTED_INCLUDE_MAGIC (&includes_module)
+
+/* TODO: changing directory should be handled by CreateProcess */
+#define ap_chdir_file(x) do {} while(0)
+
+
+/****************************************************************************
+ * Used to keep context information during parsing of a request for SSI tags.
+ * This is especially useful if the tag stretches across multiple buckets or
+ * brigades. This keeps track of which buckets need to be replaced with the
+ * content generated by the SSI tag.
+ *
+ * state: PRE_HEAD - State prior to finding the first character of the
+ * STARTING_SEQUENCE. Next state is PARSE_HEAD.
+ * PARSE_HEAD - State entered once the first character of the
+ * STARTING_SEQUENCE is found and exited when the
+ * the full STARTING_SEQUENCE has been matched or
+ * a match failure occurs. Next state is PRE_HEAD
+ * or PARSE_TAG.
+ * PARSE_TAG - State entered once the STARTING sequence has been
+ * matched. It is exited when the first character in
+ * ENDING_SEQUENCE is found. Next state is PARSE_TAIL.
+ * PARSE_TAIL - State entered from PARSE_TAG state when the first
+ * character in ENDING_SEQUENCE is encountered. This
+ * state is exited when the ENDING_SEQUENCE has been
+ * completely matched, or when a match failure occurs.
+ * Next state is PARSE_TAG or PARSED.
+ * PARSED - State entered from PARSE_TAIL once the complete
+ * ENDING_SEQUENCE has been matched. The SSI tag is
+ * processed and the SSI buckets are replaced with the
+ * SSI content during this state.
+ * parse_pos: Current matched position within the STARTING_SEQUENCE or
+ * ENDING_SEQUENCE during the PARSE_HEAD and PARSE_TAIL states.
+ * This is especially useful when the sequence spans brigades.
+ * X_start_bucket: These point to the buckets containing the first character
+ * of the STARTING_SEQUENCE, the first non-whitespace
+ * character of the tag, and the first character in the
+ * ENDING_SEQUENCE (head_, tag_, and tail_ respectively).
+ * The buckets are kept intact until the PARSED state is
+ * reached, at which time the tag is consolidated and the
+ * buckets are released. The buckets that these point to
+ * have all been set aside in the ssi_tag_brigade (along
+ * with all of the intervening buckets).
+ * X_start_index: The index points within the specified bucket contents
+ * where the first character of the STARTING_SEQUENCE,
+ * the first non-whitespace character of the tag, and the
+ * first character in the ENDING_SEQUENCE can be found
+ * (head_, tag_, and tail_ respectively).
+ * combined_tag: Once the PARSED state is reached the tag is collected from
+ * the bucket(s) in the ssi_tag_brigade into this contiguous
+ * buffer. The buckets in the ssi_tag_brigade are released
+ * and the tag is processed.
+ * curr_tag_pos: Ptr to the combined_tag buffer indicating the current
+ * parse position.
+ * tag_length: The number of bytes in the actual tag (excluding the
+ * STARTING_SEQUENCE, leading and trailing whitespace,
+ * and ENDING_SEQUENCE). This length is computed as the
+ * buckets are parsed and set aside during the PARSE_TAG state.
+ * ssi_tag_brigade: The temporary brigade used by this filter to set aside
+ * the buckets containing parts of the ssi tag and headers.
+ */
+typedef enum {PRE_HEAD, PARSE_HEAD, PARSE_TAG, PARSE_TAIL, PARSED} states;
+typedef struct include_filter_ctx {
+ states state;
+ long flags; /* See the FLAG_XXXXX definitions. */
+ int if_nesting_level;
+ apr_ssize_t parse_pos;
+
+ ap_bucket *head_start_bucket;
+ apr_ssize_t head_start_index;
+
+ ap_bucket *tag_start_bucket;
+ apr_ssize_t tag_start_index;
+
+ ap_bucket *tail_start_bucket;
+ apr_ssize_t tail_start_index;
+
+ char *combined_tag;
+ char *curr_tag_pos;
+ apr_ssize_t tag_length;
+
+ apr_ssize_t error_length;
+ char error_str[MAX_STRING_LEN];
+ char time_str[MAX_STRING_LEN];
+
+ ap_bucket_brigade *ssi_tag_brigade;
+} include_ctx_t;
+
+/* These flags are used to set flag bits. */
+#define FLAG_PRINTING 0x00000001 /* Printing conditional lines. */
+#define FLAG_COND_TRUE 0x00000002 /* Conditional eval'd to true. */
+#define FLAG_SIZE_IN_BYTES 0x00000004 /* Sizes displayed in bytes. */
+#define FLAG_NO_EXEC 0x00000008 /* No Exec in current context. */
+
+/* These flags are used to clear flag bits. */
+#define FLAG_SIZE_ABBREV 0xFFFFFFFB /* Reset SIZE_IN_BYTES bit. */
+#define FLAG_CLEAR_PRINT_COND 0xFFFFFFFC /* Reset PRINTING and COND_TRUE*/
+#define FLAG_CLEAR_PRINTING 0xFFFFFFFE /* Reset just PRINTING bit. */
+
+typedef enum {TOK_UNKNOWN, TOK_IF, TOK_SET, TOK_ECHO, TOK_ELIF, TOK_ELSE,
+ TOK_EXEC, TOK_PERL, TOK_ENDIF, TOK_FSIZE, TOK_CONFIG,
+ TOK_INCLUDE, TOK_FLASTMOD, TOK_PRINTENV} dir_token_id;
+
+#define CREATE_ERROR_BUCKET(cntx, t_buck, h_ptr, ins_head) \
+{ \
+ apr_ssize_t e_wrt; \
+ t_buck = ap_bucket_create_heap(cntx->error_str, \
+ ctx->error_length, 1, &e_wrt); \
+ AP_BUCKET_INSERT_BEFORE(h_ptr, t_buck); \
+ \
+ if (ins_head == NULL) { \
+ ins_head = t_buck; \
+ } \
+}
+
+#define SPLIT_AND_PASS_PRETAG_BUCKETS(brgd, cntxt) \
+if ((AP_BRIGADE_EMPTY(cntxt->ssi_tag_brigade)) && \
+ (cntxt->head_start_bucket != NULL)) { \
+ ap_bucket_brigade *tag_plus; \
+ \
+ tag_plus = ap_brigade_split(brgd, cntxt->head_start_bucket); \
+ ap_pass_brigade(f->next, brgd); \
+ brgd = tag_plus; \
+}
+
+#endif /* MOD_INCLUDE */
diff --git a/modules/generators/.cvsignore b/modules/generators/.cvsignore
new file mode 100644
index 0000000000..f2f7a70d2c
--- /dev/null
+++ b/modules/generators/.cvsignore
@@ -0,0 +1,10 @@
+.deps
+.libs
+*.la
+modules.mk
+Makefile
+*.lo
+*.slo
+*.so
+*.dll
+*.def
diff --git a/modules/generators/Makefile.in b/modules/generators/Makefile.in
new file mode 100644
index 0000000000..167b343d0d
--- /dev/null
+++ b/modules/generators/Makefile.in
@@ -0,0 +1,3 @@
+
+include $(top_srcdir)/build/special.mk
+
diff --git a/modules/generators/config5.m4 b/modules/generators/config5.m4
new file mode 100644
index 0000000000..2a1ed8ff27
--- /dev/null
+++ b/modules/generators/config5.m4
@@ -0,0 +1,28 @@
+dnl modules enabled in this directory by default
+
+dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]])
+
+APACHE_MODPATH_INIT(generators)
+
+APACHE_MODULE(status, process/thread monitoring, , , no)
+APACHE_MODULE(autoindex, directory listing, , , yes)
+APACHE_MODULE(asis, as-is filetypes, , , yes)
+APACHE_MODULE(info, server information, , , no)
+APACHE_MODULE(suexec, set uid and gid for spawned processes, , , no)
+
+LTFLAGS="$LTFLAGS -export-dynamic"
+
+if test "$apache_cv_mpm" = "mpmt_pthread" -o "$apache_cv_mpm" = "dexter"; then
+# if we are using a threaded MPM, we will get better performance with
+# mod_cgid, so make it the default.
+ APACHE_MODULE(cgid, CGI scripts, , , yes)
+ APACHE_MODULE(cgi, CGI scripts, , , no)
+else
+# if we are using a non-threaded MPM, it makes little sense to use
+# mod_cgid, and it just opens up holes we don't need. Make mod_cgi the
+# default
+ APACHE_MODULE(cgi, CGI scripts, , , yes)
+ APACHE_MODULE(cgid, CGI scripts, , , no)
+fi
+
+APACHE_MODPATH_FINISH
diff --git a/modules/generators/mod_suexec.c b/modules/generators/mod_suexec.c
new file mode 100644
index 0000000000..bf3a70feb6
--- /dev/null
+++ b/modules/generators/mod_suexec.c
@@ -0,0 +1,160 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_request.h"
+#include "apr_strings.h"
+#include "suexec.h"
+#include "unixd.h"
+
+module MODULE_VAR_EXPORT suexec_module;
+
+typedef struct {
+ ap_unix_identity_t ugid;
+ int active;
+} suexec_config_t;
+
+/*
+ * Create a configuration specific to this module for a server or directory
+ * location, and fill it with the default settings.
+ */
+static void *mkconfig(apr_pool_t *p)
+{
+ suexec_config_t *cfg = apr_palloc(p, sizeof(suexec_config_t));
+
+ cfg->active = 0;
+ return cfg;
+}
+
+/*
+ * Respond to a callback to create configuration record for a server or
+ * vhost environment.
+ */
+static void *create_mconfig_for_server(apr_pool_t *p, server_rec *s)
+{
+ return mkconfig(p);
+}
+
+/*
+ * Respond to a callback to create a config record for a specific directory.
+ */
+static void *create_mconfig_for_directory(apr_pool_t *p, char *dir)
+{
+ return mkconfig(p);
+}
+
+static const char *set_suexec_ugid(cmd_parms *cmd, void *mconfig,
+ char *uid, char *gid)
+{
+ suexec_config_t *cfg = (suexec_config_t *) mconfig;
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+
+ if (err != NULL) {
+ return err;
+ }
+ if (unixd_config.suexec_enabled) {
+ cfg->ugid.uid = ap_uname2id(uid);
+ cfg->ugid.gid = ap_gname2id(gid);
+ cfg->active = 1;
+ }
+ else {
+ fprintf(stderr,
+ "Warning: SuexecUserGroup directive requires SUEXEC wrapper.\n");
+ }
+ return NULL;
+}
+
+static ap_unix_identity_t *get_suexec_id_doer(const request_rec *r)
+{
+ suexec_config_t *cfg =
+ (suexec_config_t *) ap_get_module_config(r->per_dir_config, &suexec_module);
+
+ return cfg->active ? &cfg->ugid : NULL;
+}
+
+/*
+ * Define the directives specific to this module. This structure is referenced
+ * later by the 'module' structure.
+ */
+static const command_rec suexec_cmds[] =
+{
+ /* XXX - Another important reason not to allow this in .htaccess is that
+ * the ap_[ug]name2id() is not thread-safe */
+ AP_INIT_TAKE2("SuexecUserGroup", set_suexec_ugid, NULL, RSRC_CONF,
+ "User and group for spawned processes"),
+ { NULL }
+};
+
+static void suexec_hooks(void)
+{
+ ap_hook_get_suexec_identity(get_suexec_id_doer,NULL,NULL,AP_HOOK_MIDDLE);
+}
+
+module MODULE_VAR_EXPORT suexec_module =
+{
+ STANDARD20_MODULE_STUFF,
+ create_mconfig_for_directory, /* create per-dir config */
+ NULL, /* merge per-dir config */
+ create_mconfig_for_server, /* server config */
+ NULL, /* merge server config */
+ suexec_cmds, /* command table */
+ NULL, /* handlers */
+ suexec_hooks /* register hooks */
+};
diff --git a/modules/generators/mod_suexec.h b/modules/generators/mod_suexec.h
new file mode 100644
index 0000000000..e8b13ac7f2
--- /dev/null
+++ b/modules/generators/mod_suexec.h
@@ -0,0 +1,64 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+#include "unixd.h"
+
+typedef struct {
+ ap_unix_identity_t ugid;
+ int active;
+} suexec_config_t;
+
diff --git a/modules/http/.cvsignore b/modules/http/.cvsignore
new file mode 100644
index 0000000000..f2f7a70d2c
--- /dev/null
+++ b/modules/http/.cvsignore
@@ -0,0 +1,10 @@
+.deps
+.libs
+*.la
+modules.mk
+Makefile
+*.lo
+*.slo
+*.so
+*.dll
+*.def
diff --git a/modules/http/Makefile.in b/modules/http/Makefile.in
new file mode 100644
index 0000000000..167b343d0d
--- /dev/null
+++ b/modules/http/Makefile.in
@@ -0,0 +1,3 @@
+
+include $(top_srcdir)/build/special.mk
+
diff --git a/modules/http/config2.m4 b/modules/http/config2.m4
new file mode 100644
index 0000000000..5fec30cc47
--- /dev/null
+++ b/modules/http/config2.m4
@@ -0,0 +1,10 @@
+dnl modules enabled in this directory by default
+
+APACHE_MODPATH_INIT(http)
+
+http_objects="http_core.lo http_protocol.lo http_request.lo"
+
+APACHE_MODULE(http, HTTP protocol handling, $http_objects, , yes)
+APACHE_MODULE(mime, mapping of file-extension to MIME, , , yes)
+
+APACHE_MODPATH_FINISH
diff --git a/modules/http/mod_core.h b/modules/http/mod_core.h
new file mode 100644
index 0000000000..91c35e081b
--- /dev/null
+++ b/modules/http/mod_core.h
@@ -0,0 +1,92 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+#ifndef MOD_CORE_H
+#define MOD_CORE_H
+
+#include "apr.h"
+#include "apr_buckets.h"
+
+#include "httpd.h"
+#include "util_filter.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @package mod_core private header file
+ */
+
+/*
+ * These (output) filters are internal to the mod_core operation.
+ */
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, apr_bucket_brigade *b);
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, apr_bucket_brigade *b);
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *,
+ apr_bucket_brigade *);
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_old_write_filter(ap_filter_t *f, apr_bucket_brigade *b);
+
+/*
+ * These (input) filters are internal to the mod_core operation.
+ */
+apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode);
+apr_status_t ap_dechunk_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !MOD_CORE_H */
diff --git a/modules/loggers/.cvsignore b/modules/loggers/.cvsignore
new file mode 100644
index 0000000000..f2f7a70d2c
--- /dev/null
+++ b/modules/loggers/.cvsignore
@@ -0,0 +1,10 @@
+.deps
+.libs
+*.la
+modules.mk
+Makefile
+*.lo
+*.slo
+*.so
+*.dll
+*.def
diff --git a/modules/loggers/Makefile.in b/modules/loggers/Makefile.in
new file mode 100644
index 0000000000..167b343d0d
--- /dev/null
+++ b/modules/loggers/Makefile.in
@@ -0,0 +1,3 @@
+
+include $(top_srcdir)/build/special.mk
+
diff --git a/modules/loggers/config.m4 b/modules/loggers/config.m4
new file mode 100644
index 0000000000..b205d4da2d
--- /dev/null
+++ b/modules/loggers/config.m4
@@ -0,0 +1,10 @@
+dnl modules enabled in this directory by default
+APACHE_MODPATH_INIT(standard)
+
+dnl APACHE_MODULE(vhost_alias,blabla)
+
+APACHE_MODULE(log_config, logging configuration, , config_log, yes)
+
+APACHE_MODPATH_FINISH
+
+APACHE_SUBST(STANDARD_LIBS)
diff --git a/modules/loggers/mod_log_config.h b/modules/loggers/mod_log_config.h
new file mode 100644
index 0000000000..d30409f804
--- /dev/null
+++ b/modules/loggers/mod_log_config.h
@@ -0,0 +1,75 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "apr_optional.h"
+#include "httpd.h"
+
+#ifndef _MOD_LOG_CONFIG_H
+#define _MOD_LOG_CONFIG_H 1
+
+typedef const char *ap_log_handler_fn_t(request_rec *r, char *a);
+
+typedef struct ap_log_handler {
+ ap_log_handler_fn_t *func;
+ int want_orig_default;
+} ap_log_handler;
+
+APR_DECLARE_OPTIONAL_FN(void, ap_register_log_handler,
+ (apr_pool_t *p, char *tag, ap_log_handler_fn_t *func, int def));
+
+#endif /* MOD_LOG_CONFIG */
diff --git a/modules/mappers/.cvsignore b/modules/mappers/.cvsignore
new file mode 100644
index 0000000000..f2f7a70d2c
--- /dev/null
+++ b/modules/mappers/.cvsignore
@@ -0,0 +1,10 @@
+.deps
+.libs
+*.la
+modules.mk
+Makefile
+*.lo
+*.slo
+*.so
+*.dll
+*.def
diff --git a/modules/mappers/Makefile.in b/modules/mappers/Makefile.in
new file mode 100644
index 0000000000..167b343d0d
--- /dev/null
+++ b/modules/mappers/Makefile.in
@@ -0,0 +1,3 @@
+
+include $(top_srcdir)/build/special.mk
+
diff --git a/modules/mappers/config9.m4 b/modules/mappers/config9.m4
new file mode 100644
index 0000000000..e60dfd7e4f
--- /dev/null
+++ b/modules/mappers/config9.m4
@@ -0,0 +1,33 @@
+dnl modules enabled in this directory by default
+
+dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]])
+
+APACHE_MODPATH_INIT(mappers)
+
+APACHE_MODULE(vhost_alias, mass hosting module)
+APACHE_MODULE(negotiation, content negoatiation, , , yes)
+APACHE_MODULE(dir, directory request handling, , , yes)
+APACHE_MODULE(imap, internal imagemaps, , , yes)
+APACHE_MODULE(actions, Action triggering on requests, , , yes)
+APACHE_MODULE(speling, correct common URL misspellings)
+APACHE_MODULE(userdir, mapping of user requests, , , yes)
+APACHE_MODULE(alias, translation of requests, , , yes)
+
+APACHE_MODULE(rewrite, regex URL translation, , , most, [
+ EXTRA_CFLAGS="$EXTRA_CFLAGS -DNO_DBM_REWRITEMAP"
+])
+
+dnl ### this isn't going to work quite right because of ordering issues
+dnl ### among the config.m4 files. it is possible that a *later* module
+dnl ### is marked as shared (thus setting sharedobjs), so we won't see
+dnl ### it here. we need to shift *this* config.m4 to be "last" or we
+dnl ### need to find a different way to set up this default and module spec.
+if test "$sharedobjs" = "yes"; then
+ APACHE_MODULE(so, DSO capability, , , yes)
+else
+ APACHE_MODULE(so, DSO capability, , , no)
+fi
+dnl ### why save the cache?
+AC_CACHE_SAVE
+
+APACHE_MODPATH_FINISH
diff --git a/modules/metadata/.cvsignore b/modules/metadata/.cvsignore
new file mode 100644
index 0000000000..f2f7a70d2c
--- /dev/null
+++ b/modules/metadata/.cvsignore
@@ -0,0 +1,10 @@
+.deps
+.libs
+*.la
+modules.mk
+Makefile
+*.lo
+*.slo
+*.so
+*.dll
+*.def
diff --git a/modules/metadata/Makefile.in b/modules/metadata/Makefile.in
new file mode 100644
index 0000000000..167b343d0d
--- /dev/null
+++ b/modules/metadata/Makefile.in
@@ -0,0 +1,3 @@
+
+include $(top_srcdir)/build/special.mk
+
diff --git a/modules/metadata/config.m4 b/modules/metadata/config.m4
new file mode 100644
index 0000000000..4e33d75e2e
--- /dev/null
+++ b/modules/metadata/config.m4
@@ -0,0 +1,30 @@
+dnl modules enabled in this directory by default
+
+dnl AC_DEFUN(modulename, modulestructname, defaultonoroff, configmacros)
+dnl XXX - Need to allow --enable-module to fail if optional config fails
+
+AC_DEFUN(APACHE_CHECK_METADATA_MODULE, [
+ APACHE_MODULE([$1],[$2],,[$3],[$4],[$5])
+])
+
+APACHE_MODPATH_INIT(metadata)
+
+APACHE_CHECK_METADATA_MODULE(env, clearing/setting of ENV vars, , yes)
+APACHE_CHECK_METADATA_MODULE(mime_magic, automagically determining MIME type, , no)
+APACHE_CHECK_METADATA_MODULE(cern_meta, CERN-type meta files, , no)
+APACHE_CHECK_METADATA_MODULE(expires, Expires header control, , no)
+APACHE_CHECK_METADATA_MODULE(headers, HTTP header control, , no)
+
+APACHE_CHECK_METADATA_MODULE(usertrack, user-session tracking, , no, [
+ AC_CHECK_HEADERS(sys/times.h)
+ AC_CHECK_FUNCS(times)
+])
+
+APACHE_CHECK_METADATA_MODULE(unique_id, per-request unique ids, , no)
+APACHE_CHECK_METADATA_MODULE(setenvif, basing ENV vars on headers, , yes)
+
+LTFLAGS="$LTFLAGS -export-dynamic"
+
+APACHE_MODPATH_FINISH
+
+APACHE_SUBST(STANDARD_LIBS)
diff --git a/modules/proxy/Makefile.in b/modules/proxy/Makefile.in
new file mode 100644
index 0000000000..b6a0fc991f
--- /dev/null
+++ b/modules/proxy/Makefile.in
@@ -0,0 +1,5 @@
+
+LTLIBRARY_NAME = libapachemod_proxy.la
+LTLIBRARY_SOURCES = mod_proxy.lo proxy_connect.lo proxy_ftp.lo proxy_http.lo proxy_util.lo
+
+include $(top_srcdir)/build/ltlib.mk
diff --git a/modules/proxy/config.m4 b/modules/proxy/config.m4
new file mode 100644
index 0000000000..f6d5efdf71
--- /dev/null
+++ b/modules/proxy/config.m4
@@ -0,0 +1,32 @@
+dnl modules enabled in this directory by default
+
+dnl AC_DEFUN(modulename, modulestructname, defaultonoroff, configmacros)
+dnl XXX - Need to add help text to --enable-module flags
+dnl XXX - Need to allow --enable-module to fail if optional config fails
+
+AC_DEFUN(APACHE_CHECK_PROXY_MODULE, [
+ APACHE_MODULE($1,,,$2,$3,$4)
+])
+
+APACHE_MODPATH_INIT(proxy)
+
+APACHE_CHECK_PROXY_MODULE(proxy, , yes)
+
+dnl APACHE_CHECK_STANDARD_MODULE(auth_db, , no, [
+dnl AC_CHECK_HEADERS(db.h)
+dnl AC_CHECK_LIB(db,main)
+dnl ])
+
+dnl APACHE_CHECK_STANDARD_MODULE(usertrack, , no, [
+dnl AC_CHECK_HEADERS(sys/times.h)
+dnl AC_CHECK_FUNCS(times)
+dnl ])
+
+APACHE_MODPATH_FINISH
+
+if test "$sharedobjs" = "yes"; then
+ LIBS="$LIBS -ldl"
+ LTFLAGS="$LTFLAGS -export-dynamic"
+fi
+
+APACHE_SUBST(STANDARD_LIBS)
diff --git a/modules/ssl/.cvsignore b/modules/ssl/.cvsignore
new file mode 100644
index 0000000000..f2f7a70d2c
--- /dev/null
+++ b/modules/ssl/.cvsignore
@@ -0,0 +1,10 @@
+.deps
+.libs
+*.la
+modules.mk
+Makefile
+*.lo
+*.slo
+*.so
+*.dll
+*.def
diff --git a/modules/ssl/README b/modules/ssl/README
new file mode 100644
index 0000000000..ca9e225bf8
--- /dev/null
+++ b/modules/ssl/README
@@ -0,0 +1,163 @@
+ _ _
+ _ __ ___ ___ __| | ___ ___| |
+ | '_ ` _ \ / _ \ / _` | / __/ __| |
+ | | | | | | (_) | (_| | \__ \__ \ | ``mod_ssl combines the flexibility of
+ |_| |_| |_|\___/ \__,_|___|___/___/_| Apache with the security of OpenSSL.''
+ |_____|
+ mod_ssl ``Ralf Engelschall has released an
+ Apache Interface to OpenSSL excellent module that integrates
+ http://www.modssl.org/ Apache and SSLeay.''
+ Version 2.8 -- Tim J. Hudson
+
+ SYNOPSIS
+
+ This Apache module provides strong cryptography for the Apache 1.3 webserver
+ via the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS
+ v1) protocols by the help of the SSL/TLS implementation library OpenSSL which
+ is based on SSLeay from Eric A. Young and Tim J. Hudson. The mod_ssl package
+ was created in April 1998 by Ralf S. Engelschall and was originally derived
+ from software developed by Ben Laurie for use in the Apache-SSL HTTP server
+ project.
+
+ SOURCES
+
+ Here is a short overview of the source files:
+
+ Makefile.libdir ......... dummy for Apache config mechanism
+ Makefile.tmpl ........... Makefile template for Unix platform
+ Makefile.win32 .......... Makefile template for Win32 platform
+ libssl.module ........... stub called from the Apache config mechanism
+ libssl.version .......... file containing the mod_ssl version information
+ mod_ssl.c ............... main source file containing API structures
+ mod_ssl.h ............... common header file of mod_ssl
+ ssl_engine_compat.c ..... backward compatibility support
+ ssl_engine_config.c ..... module configuration handling
+ ssl_engine_dh.c ......... DSA/DH support
+ ssl_engine_ds.c ......... data structures
+ ssl_engine_ext.c ........ Extensions to other Apache parts
+ ssl_engine_init.c ....... module initialization
+ ssl_engine_io.c ......... I/O support
+ ssl_engine_kernel.c ..... SSL engine kernel
+ ssl_engine_log.c ........ logfile support
+ ssl_engine_mutex.c ...... mutual exclusion support
+ ssl_engine_pphrase.c .... pass-phrase handling
+ ssl_engine_rand.c ....... PRNG support
+ ssl_engine_vars.c ....... Variable Expansion support
+ ssl_expr.c .............. expression handling main source
+ ssl_expr.h .............. expression handling common header
+ ssl_expr_scan.c ......... expression scanner automaton (pre-generated)
+ ssl_expr_scan.l ......... expression scanner source
+ ssl_expr_parse.c ........ expression parser automaton (pre-generated)
+ ssl_expr_parse.h ........ expression parser header (pre-generated)
+ ssl_expr_parse.y ........ expression parser source
+ ssl_expr_eval.c ......... expression machine evaluation
+ ssl_scache.c ............ session cache abstraction layer
+ ssl_scache_dbm.c ........ session cache via DBM file
+ ssl_scache_shmcb.c ...... session cache via shared memory cyclic buffer
+ ssl_scache_shmht.c ...... session cache via shared memory hash table
+ ssl_util.c .............. utility functions
+ ssl_util_ssl.c .......... the OpenSSL companion source
+ ssl_util_ssl.h .......... the OpenSSL companion header
+ ssl_util_sdbm.c ......... the SDBM library source
+ ssl_util_sdbm.h ......... the SDBM library header
+ ssl_util_table.c ........ the hash table library source
+ ssl_util_table.h ........ the hash table library header
+
+ The source files are written in clean ANSI C and pass the ``gcc -O -g
+ -ggdb3 -Wall -Wshadow -Wpointer-arith -Wcast-align -Wmissing-prototypes
+ -Wmissing-declarations -Wnested-externs -Winline'' compiler test
+ (assuming `gcc' is GCC 2.95.2 or newer) without any complains. When
+ you make changes or additions make sure the source still passes this
+ compiler test.
+
+ FUNCTIONS
+
+ Inside the source code you will be confronted with the following types of
+ functions which can be identified by their prefixes:
+
+ ap_xxxx() ............... Apache API function
+ ssl_xxxx() .............. mod_ssl function
+ SSL_xxxx() .............. OpenSSL function (SSL library)
+ OpenSSL_xxxx() .......... OpenSSL function (SSL library)
+ X509_xxxx() ............. OpenSSL function (Crypto library)
+ PEM_xxxx() .............. OpenSSL function (Crypto library)
+ EVP_xxxx() .............. OpenSSL function (Crypto library)
+ RSA_xxxx() .............. OpenSSL function (Crypto library)
+
+ DATA STRUCTURES
+
+ Inside the source code you will be confronted with the following
+ data structures:
+
+ ap_ctx .................. Apache EAPI Context
+ server_rec .............. Apache (Virtual) Server
+ conn_rec ................ Apache Connection
+ BUFF .................... Apache Connection Buffer
+ request_rec ............. Apache Request
+ SSLModConfig ............ mod_ssl (Global) Module Configuration
+ SSLSrvConfig ............ mod_ssl (Virtual) Server Configuration
+ SSLDirConfig ............ mod_ssl Directory Configuration
+ SSL_CTX ................. OpenSSL Context
+ SSL_METHOD .............. OpenSSL Protocol Method
+ SSL_CIPHER .............. OpenSSL Cipher
+ SSL_SESSION ............. OpenSSL Session
+ SSL ..................... OpenSSL Connection
+ BIO ..................... OpenSSL Connection Buffer
+
+ For an overview how these are related and chained together have a look at the
+ page in README.dsov.{fig,ps}. It contains overview diagrams for those data
+ structures. It's designed for DIN A4 paper size, but you can easily generate
+ a smaller version inside XFig by specifing a magnification on the Export
+ panel.
+
+ EXPERIMENTAL CODE
+
+ Experimental code is always encapsulated as following:
+
+ | #ifdef SSL_EXPERIMENTAL_xxxx
+ | ...
+ | #endif
+
+ This way it is only compiled in when this define is enabled with
+ the APACI --enable-rule=SSL_EXPERIMENTAL option and as long as the
+ C pre-processor variable SSL_EXPERIMENTAL_xxxx_IGNORE is _NOT_
+ defined (via CFLAGS). Or in other words: SSL_EXPERIMENTAL enables all
+ SSL_EXPERIMENTAL_xxxx variables, except if SSL_EXPERIMENTAL_xxxx_IGNORE
+ is already defined. Currently the following features are experimental:
+
+ o SSL_EXPERIMENTAL_PERDIRCA
+ The ability to use SSLCACertificateFile and SSLCACertificatePath
+ in a per-directory context (.htaccess). This is provided by some nasty
+ reconfiguration hacks until OpenSSL has better support for this. It
+ should work on non-multithreaded platforms (all but Win32).
+
+ o SSL_EXPERIMENTAL_PROXY
+ The ability to use various additional SSLProxyXXX directives in
+ oder to control extended client functionality in the HTTPS proxy
+ code.
+
+ o SSL_EXPERIMENTAL_ENGINE
+ The ability to support the new forthcoming OpenSSL ENGINE stuff.
+ Until this development branch of OpenSSL is merged into the main
+ stream, you have to use openssl-engine-0.9.x.tar.gz for this.
+ mod_ssl automatically recognizes this OpenSSL variant and then can
+ activate external crypto devices through SSLCryptoDevice directive.
+
+ VENDOR EXTENSIONS
+
+ Inside the mod_ssl sources you can enable various EAPI vendor hooks
+ (`ap::mod_ssl::vendor::xxxx') by using the APACI --enable-rule=SSL_VENDOR
+ option. These hooks can be used to change or extend mod_ssl by a vendor
+ without patching the source code. Grep for `ap::mod_ssl::vendor::'.
+ Additionally vendors can add their own source code to files named
+ ssl_vendor.c, ssl_vendor_XXX.c, etc. The libssl.module script automatically
+ picks these up under configuration time and mod_ssl under run-time calls the
+ functions `void ssl_vendor_register(void)' and `void
+ ssl_vendor_unregister(void)' inside these objects to bootstrap them.
+
+ An ssl_vendor.c should at least contain the following contents:
+
+ | #include "mod_ssl.h"
+ | void ssl_vendor_register(void) { return; }
+ | void ssl_vendor_unregister(void) { return; }
+
diff --git a/modules/ssl/README.dsov.fig b/modules/ssl/README.dsov.fig
new file mode 100644
index 0000000000..d8d03db247
--- /dev/null
+++ b/modules/ssl/README.dsov.fig
@@ -0,0 +1,346 @@
+#FIG 3.2
+Landscape
+Center
+Metric
+Letter
+100.00
+Single
+-2
+1200 2
+0 32 #616561
+0 33 #b6b2b6
+0 34 #f7f3f7
+0 35 #cfcfcf
+0 36 #ffffff
+6 6345 2835 7155 3150
+6 6345 2970 7110 3150
+4 0 0 200 0 20 8 0.0000 4 120 585 6345 3105 "ssl_module")\001
+-6
+4 0 0 200 0 20 8 0.0000 4 120 660 6345 2970 ap_ctx_get(...,\001
+-6
+6 10800 2610 12240 3060
+4 0 0 200 0 20 8 0.0000 4 120 1170 10800 2745 ap_get_module_config(...\001
+4 0 0 200 0 20 8 0.0000 4 120 795 10800 2880 ->per_dir_config,\001
+4 0 0 200 0 20 8 0.0000 4 120 585 10800 3015 &ssl_module)\001
+-6
+6 7920 4770 9135 4995
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 9135 4995 7920 4995 7920 4770 9135 4770 9135 4995
+4 0 0 100 0 18 12 0.0000 4 180 1065 8010 4950 request_rec\001
+-6
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 6975 3330 7425 2520
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 7200 4230 9450 2520
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 7875 4905 7200 5220
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 6750 5130 6750 4545
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 6705 5445 7155 6120
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 7875 4815 7200 4590
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 9585 2565 11475 4230
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 10170 5130 11835 4545
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 7920 6075 9855 5400
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 9990 5445 10935 5625
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 10215 5310 10935 5310
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 11925 4590 11925 5085
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 9810 5490 9810 6840
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 9945 5445 10935 6030
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 8865 4725 10800 2565
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 675 6075 5850 6075
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 675 6525 675 6075
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 5850 6075 5850 6525
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 900 5625 5625 5625
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 1125 5175 5400 5175
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 1350 4725 5175 4725
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 1575 4275 4950 4275
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 1800 3825 4725 3825
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 2025 3375 4500 3375
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 2250 2925 4275 2925
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 2475 2475 4050 2475
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 2700 2025 3825 2025
+2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2
+ 2925 1575 3600 1575
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 900 6075 900 5625
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 1125 6525 1125 5175
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 1350 5175 1350 4725
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 1575 4725 1575 4275
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 1800 6525 1800 3825
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2025 3825 2025 3375
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2250 3375 2250 2925
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2475 2925 2475 2475
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 5625 5625 5625 6075
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 5400 5175 5400 6525
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 5175 4725 5175 5175
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 4950 4275 4950 4725
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 4725 3825 4725 6525
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 4500 3375 4500 3825
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 4275 2925 4275 3375
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 4050 2475 4050 2925
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2700 6525 2700 2025
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 3825 2025 3825 6525
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 0 1.00 60.00 120.00
+ 3600 1575 3600 2025
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2925 2025 2925 1575
+2 1 0 4 0 0 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 4.00 60.00 120.00
+ 540 6525 6300 6525
+2 3 0 1 7 7 800 0 20 0.000 0 0 -1 0 0 9
+ 675 6525 5850 6525 5850 6075 5625 6075 5625 5625 900 5625
+ 900 6075 675 6075 675 6525
+2 3 0 1 34 34 700 0 20 0.000 0 0 -1 0 0 13
+ 1125 6525 5355 6525 5400 5175 5175 5175 5175 4725 4950 4725
+ 4950 4275 1575 4275 1575 4725 1350 4725 1350 5175 1125 5175
+ 1125 6525
+2 3 0 1 35 35 500 0 20 0.000 0 0 -1 0 0 17
+ 1800 6525 4725 6525 4725 3825 4500 3825 4500 3375 4275 3375
+ 4275 2925 4050 2925 4050 2475 2475 2475 2475 2925 2250 2925
+ 2250 3375 2025 3375 2025 3825 1800 3825 1800 6525
+2 3 0 1 33 33 400 0 20 0.000 0 0 -1 0 0 9
+ 2700 6525 3825 6525 3825 2025 3600 2025 3600 1575 2925 1575
+ 2925 2025 2700 2025 2700 6525
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+ 2 0 1.00 60.00 120.00
+ 2 0 1.00 60.00 120.00
+ 2700 6750 3825 6750
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+ 2 0 1.00 60.00 120.00
+ 2 0 1.00 60.00 120.00
+ 1125 7200 5400 7200
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+ 2 0 1.00 60.00 120.00
+ 2 0 1.00 60.00 120.00
+ 1800 6975 4725 6975
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2
+ 2 0 1.00 60.00 120.00
+ 2 0 1.00 60.00 120.00
+ 675 7425 5850 7425
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 675 6570 675 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 1125 6570 1125 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 1800 6570 1800 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 2700 6570 2700 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 3825 6570 3825 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 4725 6570 4725 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 5400 6570 5400 7650
+2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2
+ 5850 6570 5850 7650
+2 4 0 2 0 7 100 0 -1 0.000 0 0 20 0 0 5
+ 12600 8550 450 8550 450 225 12600 225 12600 8550
+2 4 0 1 0 34 200 0 20 0.000 0 0 20 0 0 5
+ 12600 1350 450 1350 450 225 12600 225 12600 1350
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 10170 2475 8775 2475 8775 2250 10170 2250 10170 2475
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 11925 2475 10575 2475 10575 2250 11925 2250 11925 2475
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 12375 4500 11430 4500 11430 4275 12375 4275 12375 4500
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 12375 5400 10980 5400 10980 5175 12375 5175 12375 5400
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 10170 5400 9675 5400 9675 5175 10170 5175 10170 5400
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 7875 6300 7200 6300 7200 6075 7875 6075 7875 6300
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 8190 2475 6750 2475 6750 2250 8190 2250 8190 2475
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 7605 3600 6300 3600 6300 3375 7605 3375 7605 3600
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 7335 4500 6300 4500 6300 4275 7335 4275 7335 4500
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 7200 5400 6300 5400 6300 5175 7200 5175 7200 5400
+2 1 0 6 7 7 600 0 -1 0.000 0 0 -1 0 0 2
+ 9450 4500 6075 1935
+2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2
+ 9450 4500 12465 2205
+2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2
+ 9450 4500 9450 7785
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 9630 5310 7245 5310
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 11385 4365 7380 4365
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 12240 5805 10980 5805 10980 5580 12240 5580 12240 5805
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 12375 6210 10980 6210 10980 5985 12375 5985 12375 6210
+2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 11205 6885 9900 5445
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 12285 7155 10530 7155 10530 6930 12285 6930 12285 7155
+2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5
+ 10170 7155 9630 7155 9630 6930 10170 6930 10170 7155
+2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2
+ 12510 6435 9450 6435
+2 1 0 1 0 34 300 0 20 0.000 0 0 7 1 0 4
+ 1 1 1.00 60.00 120.00
+ 12375 4455 12510 4635 12510 6210 11970 6885
+2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 9850 5143 9175 4918
+3 1 0 1 34 34 800 0 20 0.000 0 0 0 41
+ 7380 1710 6390 2115 5535 2115 6075 3015 5670 3465 6165 3915
+ 5715 4410 6030 5040 6030 5310 6480 5715 6390 6255 6975 6300
+ 7065 6975 7965 6750 8100 7560 8955 7290 9360 7740 9720 7560
+ 10755 8145 12060 8280 12375 7650 12420 7200 12510 7065 12330 6660
+ 12510 6390 12420 5940 12375 5400 12510 5220 12510 4725 12600 4275
+ 12375 3645 12105 3240 12150 2745 12375 2700 12330 1980 11790 1575
+ 11250 1935 10125 1485 8955 2070 7785 1620 7695 1575
+ 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+ 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+ 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+ 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+ 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000
+ 1.000
+4 0 0 100 0 0 12 0.0000 4 180 1440 10575 675 Ralf S. Engelschall\001
+4 0 0 100 0 18 20 0.0000 4 270 3840 4275 675 Apache+mod_ssl+OpenSSL\001
+4 0 0 100 0 0 10 0.0000 4 135 1320 10575 855 rse@engelschall.com\001
+4 0 0 100 0 0 10 0.0000 4 135 1410 10575 1035 www.engelschall.com\001
+4 0 0 100 0 0 12 0.0000 4 135 870 900 675 Version 1.3\001
+4 0 0 100 0 0 12 0.0000 4 180 1035 900 855 12-Apr-1999\001
+4 0 0 200 0 20 8 0.0000 4 60 390 6210 4680 ->server\001
+4 0 0 200 0 20 8 0.0000 4 120 855 8280 6120 ap_ctx_get(...,"ssl")\001
+4 0 0 200 0 20 8 0.0000 4 120 1170 7740 2700 ap_get_module_config(...\001
+4 0 0 200 0 20 8 0.0000 4 120 810 7740 2835 ->module_config,\001
+4 0 0 200 0 20 8 0.0000 4 120 585 7740 2970 &ssl_module)\001
+4 0 0 100 0 18 20 0.0000 4 270 1200 9000 8100 Chaining\001
+4 0 0 100 0 18 20 0.0000 4 210 1095 2745 8100 Lifetime\001
+4 0 0 100 0 18 12 0.0000 4 180 1215 810 6255 ap_global_ctx\001
+4 0 0 100 0 18 12 0.0000 4 180 1305 990 5805 SSLModConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 840 4050 4455 SSL_CTX\001
+4 0 0 100 0 18 12 0.0000 4 150 975 4455 5355 server_rec\001
+4 0 0 100 0 18 12 0.0000 4 180 1260 3870 4905 SSLSrvConfig\001
+4 0 0 100 0 18 12 0.0000 4 135 480 1845 4005 BUFF\001
+4 0 0 100 0 18 12 0.0000 4 150 810 2070 3555 conn_rec\001
+4 0 0 100 0 18 12 0.0000 4 135 345 2295 3105 BIO\001
+4 0 0 100 0 18 12 0.0000 4 135 375 2565 2655 SSL\001
+4 0 0 100 0 18 12 0.0000 4 180 1185 3645 1620 SSLDirConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 1065 3915 2070 request_rec\001
+4 0 0 200 0 0 8 0.0000 4 120 1440 900 7560 Startup, Runtime, Shutdown\001
+4 0 0 200 0 0 8 0.0000 4 105 975 1350 7335 Configuration Time\001
+4 0 0 200 0 0 8 0.0000 4 90 1050 2025 7110 Connection Duration\001
+4 0 0 200 0 0 8 0.0000 4 120 885 2835 6885 Request Duration\001
+4 0 0 200 0 18 20 0.0000 4 195 90 6345 6795 t\001
+4 0 0 200 0 20 8 0.0000 4 90 345 7110 5985 ->client\001
+4 0 0 100 0 18 12 0.0000 4 180 1305 6795 2430 SSLModConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 1260 8865 2430 SSLSrvConfig\001
+4 0 0 100 0 18 12 0.0000 4 180 1215 6345 3555 ap_global_ctx\001
+4 0 0 100 0 18 12 0.0000 4 150 975 6345 4455 server_rec\001
+4 0 0 100 0 18 12 0.0000 4 150 810 6345 5355 conn_rec\001
+4 0 0 100 0 18 12 0.0000 4 135 375 9720 5355 SSL\001
+4 0 0 100 0 18 12 0.0000 4 180 1185 10665 2430 SSLDirConfig\001
+4 0 0 100 0 18 12 0.0000 4 135 480 7290 6255 BUFF\001
+4 0 0 100 0 18 12 0.0000 4 180 1305 11025 5355 SSL_METHOD\001
+4 0 0 100 0 18 12 0.0000 4 180 840 11475 4455 SSL_CTX\001
+4 0 0 100 0 18 24 0.0000 4 285 4365 3915 1080 Data Structure Overview\001
+4 0 0 200 0 20 8 0.0000 4 90 615 7065 5085 ->connection\001
+4 0 0 200 0 20 8 0.0000 4 60 390 7065 4770 ->server\001
+4 0 0 200 0 20 8 0.0000 4 120 960 8010 5445 SSL_get_app_data()\001
+4 0 0 200 0 20 8 0.0000 4 120 510 10530 4050 ->pSSLCtx\001
+4 0 0 200 0 20 8 0.0000 4 120 1215 7875 4275 SSL_CTX_get_app_data()\001
+4 0 0 200 0 20 8 0.0000 4 120 1155 10305 5535 SSL_get_current_cipher()\001
+4 0 0 100 0 18 12 0.0000 4 180 1170 11025 5760 SSL_CIPHER\001
+4 0 0 100 0 18 12 0.0000 4 180 1350 10980 6165 SSL_SESSION\001
+4 0 0 200 0 20 8 0.0000 4 120 840 10440 5940 SSL_get_session()\001
+4 0 0 100 0 18 12 0.0000 4 180 1665 10575 7110 X509_STORE_CTX\001
+4 0 0 100 0 18 12 0.0000 4 135 345 9720 7110 BIO\001
+4 0 0 200 0 20 8 0.0000 4 120 840 9540 7335 SSL_get_{r,w}bio()\001
+4 0 0 100 0 18 20 0.0000 4 270 1170 8730 3465 mod_ssl\001
+4 0 0 100 0 18 20 0.0000 4 270 1050 8145 6750 Apache\001
+4 0 0 200 0 20 8 0.0000 4 120 945 10125 4680 SSL_get_SSL_CTX()\001
+4 0 0 200 0 20 8 0.0000 4 120 1170 10350 5175 SSL_get_SSL_METHOD()\001
+4 0 0 200 0 20 8 0.0000 4 90 465 11745 4770 ->method\001
+4 0 0 200 0 20 8 0.0000 4 120 1665 9945 6480 X509_STORE_CTX_get_app_data()\001
+4 0 0 200 0 20 8 0.0000 4 120 1215 10980 6705 SSL_CTX_get_cert_store()\001
+4 0 0 200 0 20 8 0.0000 4 120 1020 8280 5130 SSL_get_app_data2()\001
+4 0 0 100 0 18 20 0.0000 4 270 1290 10710 7605 OpenSSL\001
+4 0 0 100 0 18 12 0.0000 4 180 720 10710 7785 [Crypto]\001
+4 0 0 100 0 18 20 0.0000 4 270 1290 10935 3645 OpenSSL\001
+4 0 0 100 0 18 12 0.0000 4 180 495 10935 3825 [SSL]\001
diff --git a/modules/ssl/README.dsov.ps b/modules/ssl/README.dsov.ps
new file mode 100644
index 0000000000..def19dbecf
--- /dev/null
+++ b/modules/ssl/README.dsov.ps
@@ -0,0 +1,1138 @@
+%!PS-Adobe-2.0
+%%Title: README.dsov.ps
+%%Creator: fig2dev Version 3.2 Patchlevel 1
+%%CreationDate: Mon Apr 12 17:09:11 1999
+%%For: rse@en1.engelschall.com (Ralf S. Engelschall)
+%%Orientation: Landscape
+%%BoundingBox: 59 37 553 755
+%%Pages: 1
+%%BeginSetup
+%%IncludeFeature: *PageSize Letter
+%%EndSetup
+%%Magnification: 0.9340
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+/col32 {0.380 0.396 0.380 srgb} bind def
+/col33 {0.714 0.698 0.714 srgb} bind def
+/col34 {0.969 0.953 0.969 srgb} bind def
+/col35 {0.812 0.812 0.812 srgb} bind def
+/col36 {1.000 1.000 1.000 srgb} bind def
+
+end
+save
+48.0 12.0 translate
+ 90 rotate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+ bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+ 4 -2 roll mul srgb} bind def
+/reencdict 12 dict def /ReEncode { reencdict begin
+/newcodesandnames exch def /newfontname exch def /basefontname exch def
+/basefontdict basefontname findfont def /newfont basefontdict maxlength dict def
+basefontdict { exch dup /FID ne { dup /Encoding eq
+{ exch dup length array copy newfont 3 1 roll put }
+{ exch newfont 3 1 roll put } ifelse } { pop pop } ifelse } forall
+newfont /FontName newfontname put newcodesandnames aload pop
+128 1 255 { newfont /Encoding get exch /.notdef put } for
+newcodesandnames length 2 idiv { newfont /Encoding get 3 1 roll put } repeat
+newfontname newfont definefont pop end } def
+/isovec [
+8#200 /grave 8#201 /acute 8#202 /circumflex 8#203 /tilde
+8#204 /macron 8#205 /breve 8#206 /dotaccent 8#207 /dieresis
+8#210 /ring 8#211 /cedilla 8#212 /hungarumlaut 8#213 /ogonek 8#214 /caron
+8#220 /dotlessi 8#230 /oe 8#231 /OE
+8#240 /space 8#241 /exclamdown 8#242 /cent 8#243 /sterling
+8#244 /currency 8#245 /yen 8#246 /brokenbar 8#247 /section 8#250 /dieresis
+8#251 /copyright 8#252 /ordfeminine 8#253 /guillemotleft 8#254 /logicalnot
+8#255 /endash 8#256 /registered 8#257 /macron 8#260 /degree 8#261 /plusminus
+8#262 /twosuperior 8#263 /threesuperior 8#264 /acute 8#265 /mu 8#266 /paragraph
+8#267 /periodcentered 8#270 /cedilla 8#271 /onesuperior 8#272 /ordmasculine
+8#273 /guillemotright 8#274 /onequarter 8#275 /onehalf
+8#276 /threequarters 8#277 /questiondown 8#300 /Agrave 8#301 /Aacute
+8#302 /Acircumflex 8#303 /Atilde 8#304 /Adieresis 8#305 /Aring
+8#306 /AE 8#307 /Ccedilla 8#310 /Egrave 8#311 /Eacute
+8#312 /Ecircumflex 8#313 /Edieresis 8#314 /Igrave 8#315 /Iacute
+8#316 /Icircumflex 8#317 /Idieresis 8#320 /Eth 8#321 /Ntilde 8#322 /Ograve
+8#323 /Oacute 8#324 /Ocircumflex 8#325 /Otilde 8#326 /Odieresis 8#327 /multiply
+8#330 /Oslash 8#331 /Ugrave 8#332 /Uacute 8#333 /Ucircumflex
+8#334 /Udieresis 8#335 /Yacute 8#336 /Thorn 8#337 /germandbls 8#340 /agrave
+8#341 /aacute 8#342 /acircumflex 8#343 /atilde 8#344 /adieresis 8#345 /aring
+8#346 /ae 8#347 /ccedilla 8#350 /egrave 8#351 /eacute
+8#352 /ecircumflex 8#353 /edieresis 8#354 /igrave 8#355 /iacute
+8#356 /icircumflex 8#357 /idieresis 8#360 /eth 8#361 /ntilde 8#362 /ograve
+8#363 /oacute 8#364 /ocircumflex 8#365 /otilde 8#366 /odieresis 8#367 /divide
+8#370 /oslash 8#371 /ugrave 8#372 /uacute 8#373 /ucircumflex
+8#374 /udieresis 8#375 /yacute 8#376 /thorn 8#377 /ydieresis] def
+/Times-Roman /Times-Roman-iso isovec ReEncode
+/Helvetica-Bold /Helvetica-Bold-iso isovec ReEncode
+/Helvetica-Narrow /Helvetica-Narrow-iso isovec ReEncode
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+%%EndProlog
+
+$F2psBegin
+10 setmiterlimit
+n -1000 9572 m -1000 -1000 l 13622 -1000 l 13622 9572 l cp clip
+ 0.05883 0.05883 sc
+%%Page: 1 1
+% Polyline
+7.500 slw
+n 6413 2048 m 6380 2054 l 6348 2061 l 6315 2067 l 6283 2073 l 6250 2079 l
+ 6217 2084 l 6185 2090 l 6152 2095 l 6120 2101 l 6088 2107 l
+ 6057 2113 l 6027 2120 l 5998 2126 l 5970 2134 l 5943 2141 l
+ 5918 2149 l 5894 2158 l 5873 2167 l 5853 2177 l 5835 2187 l
+ 5819 2198 l 5805 2210 l 5793 2222 l 5782 2235 l 5774 2250 l
+ 5768 2265 l 5763 2281 l 5760 2299 l 5759 2318 l 5759 2339 l
+ 5761 2360 l 5764 2383 l 5768 2408 l 5774 2433 l 5780 2460 l
+ 5788 2488 l 5797 2516 l 5806 2546 l 5815 2575 l 5825 2606 l
+ 5836 2636 l 5846 2666 l 5856 2696 l 5866 2726 l 5875 2755 l
+ 5884 2784 l 5892 2812 l 5899 2839 l 5905 2866 l 5910 2891 l
+ 5915 2916 l 5918 2940 l 5919 2968 l 5920 2995 l 5919 3022 l
+ 5916 3048 l 5912 3075 l 5908 3101 l 5902 3127 l 5895 3153 l
+ 5887 3179 l 5880 3205 l 5871 3230 l 5863 3254 l 5855 3278 l
+ 5848 3302 l 5841 3324 l 5834 3346 l 5829 3367 l 5824 3388 l
+ 5821 3408 l 5819 3427 l 5819 3446 l 5820 3465 l 5823 3484 l
+ 5827 3503 l 5833 3522 l 5840 3542 l 5848 3562 l 5858 3582 l
+ 5868 3603 l 5880 3625 l 5891 3647 l 5904 3669 l 5916 3691 l
+ 5929 3713 l 5941 3736 l 5953 3758 l 5964 3779 l 5974 3801 l
+ 5983 3822 l 5991 3843 l 5997 3863 l 6002 3883 l 6006 3903 l
+ 6008 3923 l 6008 3942 l 6006 3962 l 6003 3983 l 5998 4004 l
+ 5992 4025 l 5985 4048 l 5977 4070 l 5968 4094 l 5958 4118 l
+ 5947 4142 l 5936 4167 l 5925 4192 l 5913 4216 l 5902 4241 l
+ 5892 4266 l 5882 4291 l 5872 4315 l 5864 4339 l 5857 4362 l
+ 5851 4386 l 5846 4409 l 5843 4433 l 5840 4456 l 5840 4480 l
+ 5840 4505 l 5842 4530 l 5845 4556 l 5849 4582 l 5854 4609 l
+ 5860 4636 l 5867 4664 l 5875 4692 l 5883 4720 l 5892 4747 l
+ 5901 4774 l 5910 4801 l 5920 4827 l 5929 4852 l 5938 4875 l
+ 5947 4898 l 5955 4920 l 5963 4941 l 5971 4961 l 5978 4980 l
+ 5985 5002 l 5992 5024 l 5999 5046 l 6005 5067 l 6010 5088 l
+ 6016 5109 l 6022 5129 l 6027 5150 l 6033 5170 l 6039 5190 l
+ 6045 5209 l 6052 5228 l 6059 5246 l 6067 5264 l 6075 5281 l
+ 6084 5298 l 6094 5315 l 6105 5333 l 6115 5347 l 6125 5361 l
+ 6137 5376 l 6149 5392 l 6162 5408 l 6176 5425 l 6191 5443 l
+ 6206 5461 l 6221 5480 l 6237 5499 l 6253 5519 l 6269 5539 l
+ 6284 5559 l 6299 5579 l 6313 5599 l 6327 5619 l 6340 5639 l
+ 6352 5659 l 6363 5679 l 6373 5698 l 6382 5718 l 6390 5738 l
+ 6398 5759 l 6404 5782 l 6410 5805 l 6415 5828 l 6420 5852 l
+ 6424 5877 l 6428 5902 l 6431 5927 l 6435 5952 l 6438 5977 l
+ 6442 6001 l 6446 6025 l 6450 6048 l 6455 6069 l 6461 6090 l
+ 6467 6109 l 6474 6127 l 6483 6143 l 6492 6159 l 6503 6173 l
+ 6515 6185 l 6528 6197 l 6543 6209 l 6560 6220 l 6578 6230 l
+ 6598 6240 l 6619 6250 l 6641 6260 l 6663 6270 l 6687 6281 l
+ 6710 6291 l 6733 6302 l 6757 6312 l 6779 6324 l 6801 6335 l
+ 6821 6348 l 6841 6361 l 6859 6374 l 6876 6389 l 6893 6405 l
+ 6906 6421 l 6919 6437 l 6932 6455 l 6944 6475 l 6955 6495 l
+ 6967 6516 l 6979 6538 l 6991 6561 l 7003 6584 l 7015 6608 l
+ 7027 6631 l 7040 6654 l 7053 6677 l 7067 6699 l 7081 6720 l
+ 7096 6739 l 7111 6758 l 7127 6774 l 7144 6789 l 7161 6803 l
+ 7180 6815 l 7200 6825 l 7220 6833 l 7240 6840 l 7263 6845 l
+ 7286 6850 l 7311 6854 l 7338 6857 l 7365 6859 l 7394 6861 l
+ 7424 6862 l 7454 6864 l 7485 6865 l 7516 6866 l 7547 6867 l
+ 7578 6868 l 7609 6870 l 7639 6872 l 7668 6875 l 7696 6879 l
+ 7723 6883 l 7748 6889 l 7773 6895 l 7795 6903 l 7817 6912 l
+ 7838 6923 l 7857 6934 l 7875 6948 l 7892 6963 l 7909 6980 l
+ 7926 6998 l 7941 7017 l 7957 7038 l 7972 7060 l 7987 7083 l
+ 8002 7106 l 8017 7130 l 8031 7154 l 8046 7178 l 8061 7202 l
+ 8075 7225 l 8090 7247 l 8105 7269 l 8120 7289 l 8135 7308 l
+ 8151 7326 l 8167 7342 l 8184 7356 l 8202 7369 l 8220 7380 l
+ 8239 7390 l 8260 7397 l 8282 7404 l 8305 7409 l 8330 7413 l
+ 8356 7416 l 8383 7418 l 8412 7420 l 8441 7420 l 8471 7419 l
+ 8502 7418 l 8534 7417 l 8565 7415 l 8597 7413 l 8629 7411 l
+ 8660 7409 l 8690 7407 l 8720 7405 l 8749 7404 l 8777 7404 l
+ 8804 7404 l 8830 7405 l 8856 7407 l 8880 7410 l 8906 7414 l
+ 8931 7420 l 8956 7427 l 8981 7435 l 9005 7444 l 9029 7455 l
+ 9053 7466 l 9077 7478 l 9100 7491 l 9123 7504 l 9146 7517 l
+ 9168 7531 l 9190 7544 l 9210 7557 l 9230 7570 l 9250 7582 l
+ 9268 7593 l 9286 7604 l 9304 7613 l 9320 7621 l 9336 7629 l
+ 9353 7635 l 9370 7641 l 9388 7645 l 9406 7648 l 9425 7650 l
+ 9444 7652 l 9464 7653 l 9485 7653 l 9508 7653 l 9531 7653 l
+ 9555 7653 l 9579 7653 l 9605 7654 l 9631 7655 l 9658 7656 l
+ 9685 7659 l 9713 7662 l 9742 7666 l 9771 7672 l 9801 7679 l
+ 9833 7688 l 9853 7694 l 9874 7700 l 9895 7708 l 9918 7716 l
+ 9941 7725 l 9966 7734 l 9991 7745 l 10017 7755 l 10045 7767 l
+ 10073 7779 l 10102 7791 l 10132 7804 l 10163 7818 l 10194 7831 l
+ 10227 7845 l 10259 7860 l 10293 7874 l 10326 7889 l 10360 7903 l
+ 10394 7918 l 10429 7932 l 10463 7947 l 10497 7961 l 10531 7974 l
+ 10565 7988 l 10599 8001 l 10633 8013 l 10667 8025 l 10700 8037 l
+ 10733 8049 l 10767 8059 l 10800 8070 l 10834 8080 l 10868 8090 l
+ 10902 8099 l 10937 8108 l 10973 8117 l 11009 8125 l 11045 8133 l
+ 11083 8141 l 11120 8148 l 11158 8155 l 11197 8161 l 11236 8167 l
+ 11275 8172 l 11313 8177 l 11352 8181 l 11391 8184 l 11429 8187 l
+ 11467 8190 l 11504 8191 l 11540 8192 l 11576 8192 l 11610 8192 l
+ 11644 8191 l 11676 8189 l 11707 8187 l 11738 8184 l 11767 8180 l
+ 11794 8176 l 11821 8171 l 11847 8165 l 11871 8159 l 11895 8153 l
+ 11923 8143 l 11950 8133 l 11976 8122 l 12001 8109 l 12025 8096 l
+ 12048 8081 l 12071 8065 l 12092 8048 l 12113 8031 l 12133 8012 l
+ 12153 7992 l 12171 7972 l 12188 7951 l 12205 7930 l 12220 7909 l
+ 12235 7887 l 12248 7865 l 12260 7843 l 12272 7822 l 12282 7800 l
+ 12292 7779 l 12301 7759 l 12309 7739 l 12316 7719 l 12323 7699 l
+ 12330 7680 l 12338 7655 l 12345 7631 l 12352 7607 l 12359 7582 l
+ 12365 7558 l 12371 7533 l 12377 7508 l 12382 7484 l 12388 7460 l
+ 12392 7436 l 12397 7414 l 12401 7391 l 12405 7370 l 12409 7350 l
+ 12412 7331 l 12415 7313 l 12418 7297 l 12421 7281 l 12424 7266 l
+ 12428 7253 l 12432 7234 l 12437 7216 l 12442 7199 l 12446 7183 l
+ 12451 7166 l 12456 7150 l 12460 7134 l 12463 7117 l 12466 7101 l
+ 12468 7086 l 12469 7070 l 12469 7054 l 12467 7037 l 12465 7020 l
+ 12462 7006 l 12459 6991 l 12455 6975 l 12450 6958 l 12445 6940 l
+ 12440 6921 l 12434 6901 l 12428 6880 l 12422 6859 l 12416 6838 l
+ 12411 6817 l 12406 6796 l 12401 6776 l 12397 6756 l 12394 6736 l
+ 12392 6718 l 12390 6700 l 12390 6683 l 12390 6665 l 12392 6649 l
+ 12394 6631 l 12397 6614 l 12401 6597 l 12406 6579 l 12411 6561 l
+ 12416 6542 l 12422 6524 l 12428 6505 l 12434 6487 l 12440 6468 l
+ 12445 6450 l 12450 6432 l 12455 6414 l 12459 6396 l 12462 6378 l
+ 12465 6360 l 12467 6343 l 12468 6326 l 12469 6308 l 12469 6289 l
+ 12468 6269 l 12468 6249 l 12466 6227 l 12464 6205 l 12462 6182 l
+ 12460 6159 l 12457 6135 l 12454 6111 l 12451 6087 l 12447 6063 l
+ 12444 6040 l 12441 6016 l 12437 5993 l 12434 5970 l 12431 5948 l
+ 12428 5925 l 12424 5902 l 12421 5879 l 12419 5855 l 12416 5831 l
+ 12413 5806 l 12411 5781 l 12408 5755 l 12406 5729 l 12404 5702 l
+ 12403 5676 l 12401 5651 l 12400 5625 l 12400 5601 l 12399 5578 l
+ 12399 5555 l 12400 5534 l 12401 5514 l 12402 5495 l 12403 5477 l
+ 12405 5460 l 12408 5440 l 12411 5421 l 12416 5402 l 12420 5384 l
+ 12426 5365 l 12431 5347 l 12437 5329 l 12444 5311 l 12450 5293 l
+ 12456 5275 l 12462 5258 l 12468 5240 l 12474 5222 l 12479 5205 l
+ 12483 5186 l 12488 5168 l 12490 5152 l 12493 5135 l 12496 5117 l
+ 12498 5099 l 12500 5079 l 12502 5058 l 12504 5036 l 12506 5014 l
+ 12507 4990 l 12509 4966 l 12510 4942 l 12512 4918 l 12513 4893 l
+ 12515 4869 l 12516 4845 l 12518 4822 l 12520 4799 l 12521 4776 l
+ 12523 4754 l 12525 4733 l 12527 4713 l 12529 4693 l 12531 4673 l
+ 12534 4653 l 12536 4632 l 12539 4610 l 12541 4588 l 12543 4566 l
+ 12546 4543 l 12548 4520 l 12550 4497 l 12552 4473 l 12553 4450 l
+ 12554 4426 l 12555 4403 l 12555 4380 l 12555 4357 l 12555 4334 l
+ 12554 4312 l 12552 4290 l 12550 4267 l 12548 4245 l 12545 4224 l
+ 12541 4203 l 12537 4181 l 12533 4159 l 12528 4136 l 12523 4112 l
+ 12517 4088 l 12510 4064 l 12503 4038 l 12496 4013 l 12488 3987 l
+ 12479 3961 l 12471 3935 l 12462 3909 l 12452 3884 l 12443 3859 l
+ 12434 3835 l 12424 3811 l 12415 3788 l 12405 3766 l 12396 3744 l
+ 12386 3723 l 12377 3702 l 12368 3683 l 12357 3661 l 12347 3640 l
+ 12336 3619 l 12325 3598 l 12314 3576 l 12303 3555 l 12291 3533 l
+ 12280 3511 l 12269 3489 l 12257 3467 l 12246 3446 l 12235 3424 l
+ 12225 3402 l 12215 3381 l 12206 3360 l 12197 3340 l 12189 3320 l
+ 12181 3301 l 12174 3281 l 12168 3262 l 12162 3244 l 12158 3225 l
+ 12153 3204 l 12149 3183 l 12145 3162 l 12142 3139 l 12140 3117 l
+ 12138 3094 l 12137 3071 l 12137 3047 l 12138 3024 l 12139 3001 l
+ 12141 2978 l 12143 2956 l 12146 2935 l 12150 2915 l 12154 2896 l
+ 12158 2879 l 12163 2862 l 12168 2847 l 12174 2833 l 12180 2820 l
+ 12188 2805 l 12197 2792 l 12206 2779 l 12216 2766 l 12227 2754 l
+ 12238 2742 l 12249 2730 l 12260 2717 l 12272 2704 l 12282 2691 l
+ 12292 2676 l 12302 2661 l 12310 2645 l 12318 2627 l 12324 2608 l
+ 12330 2588 l 12334 2571 l 12336 2553 l 12339 2534 l 12341 2513 l
+ 12342 2491 l 12343 2467 l 12343 2442 l 12342 2416 l 12340 2389 l
+ 12338 2360 l 12335 2332 l 12331 2303 l 12326 2273 l 12320 2244 l
+ 12314 2215 l 12307 2187 l 12299 2159 l 12290 2132 l 12280 2106 l
+ 12270 2081 l 12259 2056 l 12248 2033 l 12236 2011 l 12224 1990 l
+ 12210 1970 l 12196 1949 l 12181 1929 l 12164 1910 l 12147 1890 l
+ 12129 1871 l 12110 1853 l 12090 1835 l 12070 1818 l 12049 1802 l
+ 12027 1787 l 12005 1773 l 11983 1761 l 11961 1749 l 11939 1739 l
+ 11917 1730 l 11895 1722 l 11874 1716 l 11852 1710 l 11831 1707 l
+ 11811 1704 l 11790 1703 l 11769 1702 l 11748 1703 l 11727 1705 l
+ 11706 1708 l 11683 1711 l 11660 1716 l 11636 1721 l 11612 1727 l
+ 11587 1733 l 11560 1740 l 11534 1747 l 11506 1754 l 11479 1761 l
+ 11450 1768 l 11422 1774 l 11393 1780 l 11364 1786 l 11334 1791 l
+ 11305 1795 l 11275 1798 l 11245 1800 l 11215 1801 l 11184 1801 l
+ 11153 1800 l 11128 1798 l 11104 1796 l 11078 1793 l 11052 1790 l
+ 11025 1785 l 10997 1781 l 10968 1776 l 10939 1770 l 10908 1764 l
+ 10877 1758 l 10844 1751 l 10811 1744 l 10778 1737 l 10743 1730 l
+ 10708 1722 l 10673 1715 l 10637 1708 l 10601 1701 l 10565 1695 l
+ 10530 1688 l 10494 1682 l 10458 1677 l 10422 1672 l 10387 1668 l
+ 10352 1664 l 10318 1661 l 10284 1658 l 10250 1657 l 10216 1656 l
+ 10183 1655 l 10150 1656 l 10118 1658 l 10087 1660 l 10055 1663 l
+ 10024 1666 l 9992 1671 l 9960 1676 l 9927 1682 l 9894 1688 l
+ 9861 1695 l 9827 1703 l 9792 1711 l 9757 1720 l 9721 1729 l
+ 9685 1738 l 9649 1748 l 9613 1757 l 9576 1767 l 9539 1778 l
+ 9502 1788 l 9465 1798 l 9429 1807 l 9392 1817 l 9356 1826 l
+ 9320 1835 l 9285 1844 l 9250 1852 l 9216 1860 l 9182 1867 l
+ 9148 1873 l 9115 1879 l 9082 1884 l 9050 1889 l 9018 1892 l
+ 8987 1895 l 8955 1898 l 8919 1899 l 8883 1900 l 8847 1899 l
+ 8811 1898 l 8774 1896 l 8737 1893 l 8699 1889 l 8661 1884 l
+ 8623 1878 l 8585 1872 l 8546 1865 l 8508 1857 l 8470 1849 l
+ 8432 1840 l 8395 1830 l 8358 1821 l 8322 1811 l 8287 1801 l
+ 8254 1790 l 8221 1780 l 8189 1770 l 8159 1760 l 8130 1750 l
+ 8102 1740 l 8076 1730 l 8051 1721 l 8028 1712 l 8006 1703 l
+ 7985 1695 l 7965 1688 l 7931 1674 l 7899 1662 l 7871 1650 l
+ 7844 1640 l 7820 1631 l 7798 1623 l 7778 1617 l 7760 1611 l
+ 7743 1607 l 7728 1603 l 7715 1601 l 7702 1600 l 7691 1600 l
+ 7680 1601 l 7669 1603 l 7658 1605 l 7648 1607 l 7638 1610 l
+ 7627 1613 l 7615 1617 l 7601 1621 l 7587 1626 l 7571 1632 l
+ 7554 1638 l 7536 1645 l 7517 1653 l 7496 1661 l 7474 1670 l
+ 7452 1679 l 7428 1689 l 7403 1699 l 7378 1709 l 7352 1720 l
+ 7325 1731 l 7297 1743 l 7268 1755 l 7247 1763 l 7226 1772 l
+ 7204 1781 l 7182 1790 l 7158 1800 l 7133 1810 l 7108 1820 l
+ 7081 1831 l 7053 1842 l 7025 1853 l 6996 1864 l 6966 1875 l
+ 6935 1886 l 6904 1898 l 6873 1909 l 6841 1921 l 6809 1932 l
+ 6776 1943 l 6744 1954 l 6712 1964 l 6680 1974 l 6649 1984 l
+ 6618 1994 l 6587 2003 l 6557 2011 l 6527 2019 l 6498 2027 l
+ 6469 2034 l 6441 2041 l cp gs col34 1.00 shd ef gr gs col34 s gr
+% Polyline
+n 675 6525 m 5850 6525 l 5850 6075 l 5625 6075 l 5625 5625 l 900 5625 l
+ 900 6075 l 675 6075 l cp gs col7 1.00 shd ef gr gs col7 s gr
+% Polyline
+n 1125 6525 m 5355 6525 l 5400 5175 l 5175 5175 l 5175 4725 l 4950 4725 l
+ 4950 4275 l 1575 4275 l 1575 4725 l 1350 4725 l 1350 5175 l
+ 1125 5175 l cp gs col34 1.00 shd ef gr gs col34 s gr
+% Polyline
+75.000 slw
+n 9450 4500 m 12465 2205 l gs col7 s gr
+% Polyline
+n 9450 4500 m 9450 7785 l gs col7 s gr
+% Polyline
+n 9450 4500 m 6075 1935 l gs col7 s gr
+% Polyline
+n 12510 6435 m 9450 6435 l gs col7 s gr
+% Polyline
+7.500 slw
+n 1800 6525 m 4725 6525 l 4725 3825 l 4500 3825 l 4500 3375 l 4275 3375 l
+ 4275 2925 l 4050 2925 l 4050 2475 l 2475 2475 l 2475 2925 l
+ 2250 2925 l 2250 3375 l 2025 3375 l 2025 3825 l 1800 3825 l
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 2700 6525 m 3825 6525 l 3825 2025 l 3600 2025 l 3600 1575 l 2925 1575 l
+ 2925 2025 l 2700 2025 l cp gs col33 1.00 shd ef gr gs col33 s gr
+% Polyline
+gs clippath
+12068 6810 m 11970 6885 l 12022 6773 l 11937 6878 l 11984 6915 l cp
+clip
+n 12375 4455 m 12510 4635 l 12510 6210 l 11970 6885 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 12068 6810 m 11970 6885 l 12022 6773 l 12045 6791 l 12068 6810 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+7113 6004 m 7155 6120 l 7063 6037 l 7138 6149 l 7188 6116 l cp
+clip
+n 6705 5445 m 7155 6120 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7113 6004 m 7155 6120 l 7063 6037 l 7088 6020 l 7113 6004 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+7304 4656 m 7200 4590 l 7323 4599 l 7195 4557 l 7176 4614 l cp
+clip
+n 7875 4815 m 7200 4590 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7304 4656 m 7200 4590 l 7323 4599 l 7314 4628 l 7304 4656 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+11405 4128 m 11475 4230 l 11365 4173 l 11466 4262 l 11506 4217 l cp
+clip
+n 9585 2565 m 11475 4230 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 11405 4128 m 11475 4230 l 11365 4173 l 11385 4151 l 11405 4128 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+11712 4556 m 11835 4545 l 11732 4613 l 11859 4568 l 11839 4512 l cp
+clip
+n 10170 5130 m 11835 4545 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 11712 4556 m 11835 4545 l 11732 4613 l 11722 4585 l 11712 4556 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+9732 5411 m 9855 5400 l 9752 5468 l 9879 5423 l 9859 5367 l cp
+clip
+n 7920 6075 m 9855 5400 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9732 5411 m 9855 5400 l 9752 5468 l 9742 5440 l 9732 5411 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+10823 5573 m 10935 5625 l 10812 5632 l 10944 5657 l 10955 5598 l cp
+clip
+n 9990 5445 m 10935 5625 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10823 5573 m 10935 5625 l 10812 5632 l 10817 5603 l 10823 5573 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+10815 5280 m 10935 5310 l 10815 5340 l 10950 5340 l 10950 5280 l cp
+clip
+n 10215 5310 m 10935 5310 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10815 5280 m 10935 5310 l 10815 5340 l 10815 5310 l 10815 5280 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+11955 4965 m 11925 5085 l 11895 4965 l 11895 5100 l 11955 5100 l cp
+clip
+n 11925 4590 m 11925 5085 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 11955 4965 m 11925 5085 l 11895 4965 l 11925 4965 l 11955 4965 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+9840 6720 m 9810 6840 l 9780 6720 l 9780 6855 l 9840 6855 l cp
+clip
+n 9810 5490 m 9810 6840 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9840 6720 m 9810 6840 l 9780 6720 l 9810 6720 l 9840 6720 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+10847 5943 m 10935 6030 l 10816 5995 l 10933 6063 l 10963 6012 l cp
+clip
+n 9945 5445 m 10935 6030 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10847 5943 m 10935 6030 l 10816 5995 l 10832 5969 l 10847 5943 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+10698 2634 m 10800 2565 l 10742 2674 l 10832 2574 l 10788 2534 l cp
+clip
+n 8865 4725 m 10800 2565 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 10698 2634 m 10800 2565 l 10742 2674 l 10720 2654 l 10698 2634 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+30.000 slw
+n 675 6075 m 5850 6075 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+7.500 slw
+ [15 15] 15 sd
+gs clippath
+645 6195 m 675 6075 l 705 6195 l 705 6060 l 645 6060 l cp
+clip
+n 675 6525 m 675 6075 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 645 6195 m 675 6075 l 705 6195 l 675 6195 l 645 6195 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+5880 6405 m 5850 6525 l 5820 6405 l 5820 6540 l 5880 6540 l cp
+clip
+n 5850 6075 m 5850 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5880 6405 m 5850 6525 l 5820 6405 l 5850 6405 l 5880 6405 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+30.000 slw
+n 900 5625 m 5625 5625 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 1125 5175 m 5400 5175 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 1350 4725 m 5175 4725 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 1575 4275 m 4950 4275 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 1800 3825 m 4725 3825 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 2025 3375 m 4500 3375 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 2250 2925 m 4275 2925 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 2475 2475 m 4050 2475 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 2700 2025 m 3825 2025 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 2925 1575 m 3600 1575 l gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+7.500 slw
+ [15 15] 15 sd
+gs clippath
+870 5745 m 900 5625 l 930 5745 l 930 5610 l 870 5610 l cp
+clip
+n 900 6075 m 900 5625 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 870 5745 m 900 5625 l 930 5745 l 900 5745 l 870 5745 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+1095 5295 m 1125 5175 l 1155 5295 l 1155 5160 l 1095 5160 l cp
+clip
+n 1125 6525 m 1125 5175 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1095 5295 m 1125 5175 l 1155 5295 l 1125 5295 l 1095 5295 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+1320 4845 m 1350 4725 l 1380 4845 l 1380 4710 l 1320 4710 l cp
+clip
+n 1350 5175 m 1350 4725 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1320 4845 m 1350 4725 l 1380 4845 l 1350 4845 l 1320 4845 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+1545 4395 m 1575 4275 l 1605 4395 l 1605 4260 l 1545 4260 l cp
+clip
+n 1575 4725 m 1575 4275 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1545 4395 m 1575 4275 l 1605 4395 l 1575 4395 l 1545 4395 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+1770 3945 m 1800 3825 l 1830 3945 l 1830 3810 l 1770 3810 l cp
+clip
+n 1800 6525 m 1800 3825 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1770 3945 m 1800 3825 l 1830 3945 l 1800 3945 l 1770 3945 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+1995 3495 m 2025 3375 l 2055 3495 l 2055 3360 l 1995 3360 l cp
+clip
+n 2025 3825 m 2025 3375 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 1995 3495 m 2025 3375 l 2055 3495 l 2025 3495 l 1995 3495 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+2220 3045 m 2250 2925 l 2280 3045 l 2280 2910 l 2220 2910 l cp
+clip
+n 2250 3375 m 2250 2925 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2220 3045 m 2250 2925 l 2280 3045 l 2250 3045 l 2220 3045 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+2445 2595 m 2475 2475 l 2505 2595 l 2505 2460 l 2445 2460 l cp
+clip
+n 2475 2925 m 2475 2475 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2445 2595 m 2475 2475 l 2505 2595 l 2475 2595 l 2445 2595 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+5655 5955 m 5625 6075 l 5595 5955 l 5595 6090 l 5655 6090 l cp
+clip
+n 5625 5625 m 5625 6075 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5655 5955 m 5625 6075 l 5595 5955 l 5625 5955 l 5655 5955 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+5430 6405 m 5400 6525 l 5370 6405 l 5370 6540 l 5430 6540 l cp
+clip
+n 5400 5175 m 5400 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5430 6405 m 5400 6525 l 5370 6405 l 5400 6405 l 5430 6405 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+5205 5055 m 5175 5175 l 5145 5055 l 5145 5190 l 5205 5190 l cp
+clip
+n 5175 4725 m 5175 5175 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 5205 5055 m 5175 5175 l 5145 5055 l 5175 5055 l 5205 5055 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+4980 4605 m 4950 4725 l 4920 4605 l 4920 4740 l 4980 4740 l cp
+clip
+n 4950 4275 m 4950 4725 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4980 4605 m 4950 4725 l 4920 4605 l 4950 4605 l 4980 4605 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+4755 6405 m 4725 6525 l 4695 6405 l 4695 6540 l 4755 6540 l cp
+clip
+n 4725 3825 m 4725 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4755 6405 m 4725 6525 l 4695 6405 l 4725 6405 l 4755 6405 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+4530 3705 m 4500 3825 l 4470 3705 l 4470 3840 l 4530 3840 l cp
+clip
+n 4500 3375 m 4500 3825 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4530 3705 m 4500 3825 l 4470 3705 l 4500 3705 l 4530 3705 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+4305 3255 m 4275 3375 l 4245 3255 l 4245 3390 l 4305 3390 l cp
+clip
+n 4275 2925 m 4275 3375 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4305 3255 m 4275 3375 l 4245 3255 l 4275 3255 l 4305 3255 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+4080 2805 m 4050 2925 l 4020 2805 l 4020 2940 l 4080 2940 l cp
+clip
+n 4050 2475 m 4050 2925 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 4080 2805 m 4050 2925 l 4020 2805 l 4050 2805 l 4080 2805 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+2670 2145 m 2700 2025 l 2730 2145 l 2730 2010 l 2670 2010 l cp
+clip
+n 2700 6525 m 2700 2025 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2670 2145 m 2700 2025 l 2730 2145 l 2700 2145 l 2670 2145 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+3855 6405 m 3825 6525 l 3795 6405 l 3795 6540 l 3855 6540 l cp
+clip
+n 3825 2025 m 3825 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 3855 6405 m 3825 6525 l 3795 6405 l 3825 6405 l 3855 6405 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+3630 1905 m 3600 2025 l 3570 1905 l 3570 2040 l 3630 2040 l cp
+clip
+n 3600 1575 m 3600 2025 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 3630 1905 m 3600 2025 l 3570 1905 l 3600 1905 l 3630 1905 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+2895 1695 m 2925 1575 l 2955 1695 l 2955 1560 l 2895 1560 l cp
+clip
+n 2925 2025 m 2925 1575 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 2895 1695 m 2925 1575 l 2955 1695 l 2925 1695 l 2895 1695 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+45.000 slw
+gs clippath
+6087 6495 m 6207 6525 l 6087 6555 l 6360 6555 l 6360 6495 l cp
+clip
+n 540 6525 m 6300 6525 l gs 0.00 setgray ef gr gs col0 s gr gr
+
+% arrowhead
+n 6087 6495 m 6207 6525 l 6087 6555 l 6087 6525 l 6087 6495 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+7.500 slw
+gs clippath
+3681 6720 m 3825 6750 l 3681 6780 l 3840 6780 l 3840 6720 l cp
+2844 6780 m 2700 6750 l 2844 6720 l 2685 6720 l 2685 6780 l cp
+clip
+n 2700 6750 m 3825 6750 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 2844 6780 m 2700 6750 l 2844 6720 l 2820 6750 l 2844 6780 l cp gs col7 1.00 shd ef gr col0 s
+% arrowhead
+n 3681 6720 m 3825 6750 l 3681 6780 l 3705 6750 l 3681 6720 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+gs clippath
+5256 7170 m 5400 7200 l 5256 7230 l 5415 7230 l 5415 7170 l cp
+1269 7230 m 1125 7200 l 1269 7170 l 1110 7170 l 1110 7230 l cp
+clip
+n 1125 7200 m 5400 7200 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 1269 7230 m 1125 7200 l 1269 7170 l 1245 7200 l 1269 7230 l cp gs col7 1.00 shd ef gr col0 s
+% arrowhead
+n 5256 7170 m 5400 7200 l 5256 7230 l 5280 7200 l 5256 7170 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+gs clippath
+4581 6945 m 4725 6975 l 4581 7005 l 4740 7005 l 4740 6945 l cp
+1944 7005 m 1800 6975 l 1944 6945 l 1785 6945 l 1785 7005 l cp
+clip
+n 1800 6975 m 4725 6975 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 1944 7005 m 1800 6975 l 1944 6945 l 1920 6975 l 1944 7005 l cp gs col7 1.00 shd ef gr col0 s
+% arrowhead
+n 4581 6945 m 4725 6975 l 4581 7005 l 4605 6975 l 4581 6945 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+gs clippath
+5706 7395 m 5850 7425 l 5706 7455 l 5865 7455 l 5865 7395 l cp
+819 7455 m 675 7425 l 819 7395 l 660 7395 l 660 7455 l cp
+clip
+n 675 7425 m 5850 7425 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 819 7455 m 675 7425 l 819 7395 l 795 7425 l 819 7455 l cp gs col7 1.00 shd ef gr col0 s
+% arrowhead
+n 5706 7395 m 5850 7425 l 5706 7455 l 5730 7425 l 5706 7395 l cp gs col7 1.00 shd ef gr col0 s
+% Polyline
+1 slc
+ [15 45] 45 sd
+n 675 6570 m 675 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 1125 6570 m 1125 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 1800 6570 m 1800 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 2700 6570 m 2700 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 3825 6570 m 3825 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 4725 6570 m 4725 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 5400 6570 m 5400 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+ [15 45] 45 sd
+n 5850 6570 m 5850 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd
+% Polyline
+0 slc
+n 750 225 m 450 225 450 1050 300 arcto 4 {pop} repeat
+ 450 1350 12300 1350 300 arcto 4 {pop} repeat
+ 12600 1350 12600 525 300 arcto 4 {pop} repeat
+ 12600 225 750 225 300 arcto 4 {pop} repeat
+ cp gs col34 1.00 shd ef gr gs col0 s gr
+% Polyline
+n 8835 2250 m 8775 2250 8775 2415 60 arcto 4 {pop} repeat
+ 8775 2475 10110 2475 60 arcto 4 {pop} repeat
+ 10170 2475 10170 2310 60 arcto 4 {pop} repeat
+ 10170 2250 8835 2250 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 10635 2250 m 10575 2250 10575 2415 60 arcto 4 {pop} repeat
+ 10575 2475 11865 2475 60 arcto 4 {pop} repeat
+ 11925 2475 11925 2310 60 arcto 4 {pop} repeat
+ 11925 2250 10635 2250 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 11490 4275 m 11430 4275 11430 4440 60 arcto 4 {pop} repeat
+ 11430 4500 12315 4500 60 arcto 4 {pop} repeat
+ 12375 4500 12375 4335 60 arcto 4 {pop} repeat
+ 12375 4275 11490 4275 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 11040 5175 m 10980 5175 10980 5340 60 arcto 4 {pop} repeat
+ 10980 5400 12315 5400 60 arcto 4 {pop} repeat
+ 12375 5400 12375 5235 60 arcto 4 {pop} repeat
+ 12375 5175 11040 5175 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 9735 5175 m 9675 5175 9675 5340 60 arcto 4 {pop} repeat
+ 9675 5400 10110 5400 60 arcto 4 {pop} repeat
+ 10170 5400 10170 5235 60 arcto 4 {pop} repeat
+ 10170 5175 9735 5175 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 7260 6075 m 7200 6075 7200 6240 60 arcto 4 {pop} repeat
+ 7200 6300 7815 6300 60 arcto 4 {pop} repeat
+ 7875 6300 7875 6135 60 arcto 4 {pop} repeat
+ 7875 6075 7260 6075 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 6810 2250 m 6750 2250 6750 2415 60 arcto 4 {pop} repeat
+ 6750 2475 8130 2475 60 arcto 4 {pop} repeat
+ 8190 2475 8190 2310 60 arcto 4 {pop} repeat
+ 8190 2250 6810 2250 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 6360 3375 m 6300 3375 6300 3540 60 arcto 4 {pop} repeat
+ 6300 3600 7545 3600 60 arcto 4 {pop} repeat
+ 7605 3600 7605 3435 60 arcto 4 {pop} repeat
+ 7605 3375 6360 3375 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 6360 4275 m 6300 4275 6300 4440 60 arcto 4 {pop} repeat
+ 6300 4500 7275 4500 60 arcto 4 {pop} repeat
+ 7335 4500 7335 4335 60 arcto 4 {pop} repeat
+ 7335 4275 6360 4275 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 6360 5175 m 6300 5175 6300 5340 60 arcto 4 {pop} repeat
+ 6300 5400 7140 5400 60 arcto 4 {pop} repeat
+ 7200 5400 7200 5235 60 arcto 4 {pop} repeat
+ 7200 5175 6360 5175 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+gs clippath
+7365 5340 m 7245 5310 l 7365 5280 l 7230 5280 l 7230 5340 l cp
+clip
+n 9630 5310 m 7245 5310 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7365 5340 m 7245 5310 l 7365 5280 l 7365 5310 l 7365 5340 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+7500 4395 m 7380 4365 l 7500 4335 l 7365 4335 l 7365 4395 l cp
+clip
+n 11385 4365 m 7380 4365 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7500 4395 m 7380 4365 l 7500 4335 l 7500 4365 l 7500 4395 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+n 11040 5580 m 10980 5580 10980 5745 60 arcto 4 {pop} repeat
+ 10980 5805 12180 5805 60 arcto 4 {pop} repeat
+ 12240 5805 12240 5640 60 arcto 4 {pop} repeat
+ 12240 5580 11040 5580 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 11040 5985 m 10980 5985 10980 6150 60 arcto 4 {pop} repeat
+ 10980 6210 12315 6210 60 arcto 4 {pop} repeat
+ 12375 6210 12375 6045 60 arcto 4 {pop} repeat
+ 12375 5985 11040 5985 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+gs clippath
+9958 5554 m 9900 5445 l 10003 5514 l 9912 5414 l 9868 5454 l cp
+clip
+n 11205 6885 m 9900 5445 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9958 5554 m 9900 5445 l 10003 5514 l 9981 5534 l 9958 5554 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+n 10590 6930 m 10530 6930 10530 7095 60 arcto 4 {pop} repeat
+ 10530 7155 12225 7155 60 arcto 4 {pop} repeat
+ 12285 7155 12285 6990 60 arcto 4 {pop} repeat
+ 12285 6930 10590 6930 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+n 9690 6930 m 9630 6930 9630 7095 60 arcto 4 {pop} repeat
+ 9630 7155 10110 7155 60 arcto 4 {pop} repeat
+ 10170 7155 10170 6990 60 arcto 4 {pop} repeat
+ 10170 6930 9690 6930 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+/Times-Roman-iso ff 120.00 scf sf
+900 7560 m
+gs 1 -1 sc (Startup, Runtime, Shutdown) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+6345 2970 m
+gs 1 -1 sc (ap_ctx_get\(...,) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10800 2745 m
+gs 1 -1 sc (ap_get_module_config\(...) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10800 2880 m
+gs 1 -1 sc (->per_dir_config,) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10800 3015 m
+gs 1 -1 sc (&ssl_module\)) col0 sh gr
+% Polyline
+n 7980 4770 m 7920 4770 7920 4935 60 arcto 4 {pop} repeat
+ 7920 4995 9075 4995 60 arcto 4 {pop} repeat
+ 9135 4995 9135 4830 60 arcto 4 {pop} repeat
+ 9135 4770 7980 4770 60 arcto 4 {pop} repeat
+ cp gs col35 1.00 shd ef gr gs col35 s gr
+% Polyline
+gs clippath
+7340 2610 m 7425 2520 l 7393 2639 l 7459 2521 l 7406 2492 l cp
+clip
+n 6975 3330 m 7425 2520 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7340 2610 m 7425 2520 l 7393 2639 l 7367 2625 l 7340 2610 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+9336 2569 m 9450 2520 l 9373 2616 l 9480 2535 l 9444 2487 l cp
+clip
+n 7200 4230 m 9450 2520 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 9336 2569 m 9450 2520 l 9373 2616 l 9354 2593 l 9336 2569 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+7321 5196 m 7200 5220 l 7296 5142 l 7174 5199 l 7199 5254 l cp
+clip
+n 7875 4905 m 7200 5220 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 7321 5196 m 7200 5220 l 7296 5142 l 7309 5169 l 7321 5196 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+gs clippath
+6720 4665 m 6750 4545 l 6780 4665 l 6780 4530 l 6720 4530 l cp
+clip
+n 6750 5130 m 6750 4545 l gs col34 1.00 shd ef gr gs col0 s gr gr
+
+% arrowhead
+n 6720 4665 m 6750 4545 l 6780 4665 l 6750 4665 l 6720 4665 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+ [15 15] 15 sd
+gs clippath
+9279 4984 m 9175 4918 l 9298 4927 l 9170 4885 l 9151 4942 l cp
+clip
+n 9850 5143 m 9175 4918 l gs col34 1.00 shd ef gr gs col0 s gr gr
+ [] 0 sd
+% arrowhead
+n 9279 4984 m 9175 4918 l 9298 4927 l 9289 4956 l 9279 4984 l cp gs 0.00 setgray ef gr col0 s
+/Helvetica-Narrow-iso ff 120.00 scf sf
+6210 4680 m
+gs 1 -1 sc (->server) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+8280 6120 m
+gs 1 -1 sc (ap_ctx_get\(...,"ssl"\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7740 2700 m
+gs 1 -1 sc (ap_get_module_config\(...) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7740 2835 m
+gs 1 -1 sc (->module_config,) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7740 2970 m
+gs 1 -1 sc (&ssl_module\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+6345 3105 m
+gs 1 -1 sc ("ssl_module"\)) col0 sh gr
+/Times-Roman-iso ff 120.00 scf sf
+1350 7335 m
+gs 1 -1 sc (Configuration Time) col0 sh gr
+/Times-Roman-iso ff 120.00 scf sf
+2025 7110 m
+gs 1 -1 sc (Connection Duration) col0 sh gr
+/Times-Roman-iso ff 120.00 scf sf
+2835 6885 m
+gs 1 -1 sc (Request Duration) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+6345 6795 m
+gs 1 -1 sc (t) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7110 5985 m
+gs 1 -1 sc (->client) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7065 5085 m
+gs 1 -1 sc (->connection) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7065 4770 m
+gs 1 -1 sc (->server) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+8010 5445 m
+gs 1 -1 sc (SSL_get_app_data\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10530 4050 m
+gs 1 -1 sc (->pSSLCtx) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+7875 4275 m
+gs 1 -1 sc (SSL_CTX_get_app_data\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10305 5535 m
+gs 1 -1 sc (SSL_get_current_cipher\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10440 5940 m
+gs 1 -1 sc (SSL_get_session\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+9540 7335 m
+gs 1 -1 sc (SSL_get_{r,w}bio\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10125 4680 m
+gs 1 -1 sc (SSL_get_SSL_CTX\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10350 5175 m
+gs 1 -1 sc (SSL_get_SSL_METHOD\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+11745 4770 m
+gs 1 -1 sc (->method) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+9945 6480 m
+gs 1 -1 sc (X509_STORE_CTX_get_app_data\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+10980 6705 m
+gs 1 -1 sc (SSL_CTX_get_cert_store\(\)) col0 sh gr
+/Helvetica-Narrow-iso ff 120.00 scf sf
+8280 5130 m
+gs 1 -1 sc (SSL_get_app_data2\(\)) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+3645 1620 m
+gs 1 -1 sc (SSLDirConfig) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+10935 3645 m
+gs 1 -1 sc (OpenSSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10935 3825 m
+gs 1 -1 sc ([SSL]) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+11025 5760 m
+gs 1 -1 sc (SSL_CIPHER) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10980 6165 m
+gs 1 -1 sc (SSL_SESSION) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+10710 7605 m
+gs 1 -1 sc (OpenSSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10575 7110 m
+gs 1 -1 sc (X509_STORE_CTX) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6795 2430 m
+gs 1 -1 sc (SSLModConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+8865 2430 m
+gs 1 -1 sc (SSLSrvConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6345 3555 m
+gs 1 -1 sc (ap_global_ctx) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6345 4455 m
+gs 1 -1 sc (server_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+6345 5355 m
+gs 1 -1 sc (conn_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+9720 5355 m
+gs 1 -1 sc (SSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10665 2430 m
+gs 1 -1 sc (SSLDirConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+7290 6255 m
+gs 1 -1 sc (BUFF) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+11025 5355 m
+gs 1 -1 sc (SSL_METHOD) col0 sh gr
+% Polyline
+15.000 slw
+n 750 225 m 450 225 450 8250 300 arcto 4 {pop} repeat
+ 450 8550 12300 8550 300 arcto 4 {pop} repeat
+ 12600 8550 12600 525 300 arcto 4 {pop} repeat
+ 12600 225 750 225 300 arcto 4 {pop} repeat
+ cp gs col0 s gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+11475 4455 m
+gs 1 -1 sc (SSL_CTX) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+8010 4950 m
+gs 1 -1 sc (request_rec) col0 sh gr
+/Times-Roman-iso ff 180.00 scf sf
+10575 675 m
+gs 1 -1 sc (Ralf S. Engelschall) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+4275 675 m
+gs 1 -1 sc (Apache+mod_ssl+OpenSSL) col0 sh gr
+/Times-Roman-iso ff 150.00 scf sf
+10575 855 m
+gs 1 -1 sc (rse@engelschall.com) col0 sh gr
+/Times-Roman-iso ff 150.00 scf sf
+10575 1035 m
+gs 1 -1 sc (www.engelschall.com) col0 sh gr
+/Times-Roman-iso ff 180.00 scf sf
+900 675 m
+gs 1 -1 sc (Version 1.3) col0 sh gr
+/Times-Roman-iso ff 180.00 scf sf
+900 855 m
+gs 1 -1 sc (12-Apr-1999) col0 sh gr
+/Helvetica-Bold-iso ff 360.00 scf sf
+3915 1080 m
+gs 1 -1 sc (Data Structure Overview) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+9720 7110 m
+gs 1 -1 sc (BIO) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+10710 7785 m
+gs 1 -1 sc ([Crypto]) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+8730 3465 m
+gs 1 -1 sc (mod_ssl) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+8145 6750 m
+gs 1 -1 sc (Apache) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+9000 8100 m
+gs 1 -1 sc (Chaining) col0 sh gr
+/Helvetica-Bold-iso ff 300.00 scf sf
+2745 8100 m
+gs 1 -1 sc (Lifetime) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+810 6255 m
+gs 1 -1 sc (ap_global_ctx) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+990 5805 m
+gs 1 -1 sc (SSLModConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+4050 4455 m
+gs 1 -1 sc (SSL_CTX) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+4455 5355 m
+gs 1 -1 sc (server_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+3870 4905 m
+gs 1 -1 sc (SSLSrvConfig) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+1845 4005 m
+gs 1 -1 sc (BUFF) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+2070 3555 m
+gs 1 -1 sc (conn_rec) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+2295 3105 m
+gs 1 -1 sc (BIO) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+2565 2655 m
+gs 1 -1 sc (SSL) col0 sh gr
+/Helvetica-Bold-iso ff 180.00 scf sf
+3915 2070 m
+gs 1 -1 sc (request_rec) col0 sh gr
+$F2psEnd
+rs
+showpage
diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
new file mode 100644
index 0000000000..2d1b5e1b0a
--- /dev/null
+++ b/modules/ssl/mod_ssl.c
@@ -0,0 +1,248 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** mod_ssl.c
+** Apache API interface structures
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``I'll be surprised if
+ others think that what you
+ are doing is honourable.''
+ -- Ben Laurie, Apache-SSL author */
+#include "mod_ssl.h"
+
+/* _________________________________________________________________
+**
+** Apache API glue structures
+** _________________________________________________________________
+*/
+
+/*
+ * identify the module to SCCS `what' and RCS `ident' commands
+ */
+static char const sccsid[] = "@(#) mod_ssl/" MOD_SSL_VERSION " >";
+static char const rcsid[] = "$Id: mod_ssl.c,v 1.1 2001/05/04 21:54:42 rse Exp $";
+
+/*
+ * the table of configuration directives we provide
+ */
+static command_rec ssl_config_cmds[] = {
+ /*
+ * Global (main-server) context configuration directives
+ */
+ AP_SRV_CMD(Mutex, TAKE1,
+ "SSL lock for handling internal mutual exclusions "
+ "(`none', `file:/path/to/file')")
+ AP_SRV_CMD(PassPhraseDialog, TAKE1,
+ "SSL dialog mechanism for the pass phrase query "
+ "(`builtin', `exec:/path/to/program')")
+ AP_SRV_CMD(SessionCache, TAKE1,
+ "SSL Session Cache storage "
+ "(`none', `dbm:/path/to/file')")
+#ifdef SSL_EXPERIMENTAL_ENGINE
+ AP_SRV_CMD(CryptoDevice, TAKE1,
+ "SSL external Crypto Device usage "
+ "(`builtin', `...')")
+#endif
+ AP_SRV_CMD(RandomSeed, TAKE23,
+ "SSL Pseudo Random Number Generator (PRNG) seeding source "
+ "(`startup|connect builtin|file:/path|exec:/path [bytes]')")
+
+ /*
+ * Per-server context configuration directives
+ */
+ AP_SRV_CMD(Engine, FLAG,
+ "SSL switch for the protocol engine "
+ "(`on', `off')")
+ AP_ALL_CMD(CipherSuite, TAKE1,
+ "Colon-delimited list of permitted SSL Ciphers "
+ "(`XXX:...:XXX' - see manual)")
+ AP_SRV_CMD(CertificateFile, TAKE1,
+ "SSL Server Certificate file "
+ "(`/path/to/file' - PEM or DER encoded)")
+ AP_SRV_CMD(CertificateKeyFile, TAKE1,
+ "SSL Server Private Key file "
+ "(`/path/to/file' - PEM or DER encoded)")
+ AP_SRV_CMD(CertificateChainFile, TAKE1,
+ "SSL Server CA Certificate Chain file "
+ "(`/path/to/file' - PEM encoded)")
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+ AP_ALL_CMD(CACertificatePath, TAKE1,
+ "SSL CA Certificate path "
+ "(`/path/to/dir' - contains PEM encoded files)")
+ AP_ALL_CMD(CACertificateFile, TAKE1,
+ "SSL CA Certificate file "
+ "(`/path/to/file' - PEM encoded)")
+#else
+ AP_SRV_CMD(CACertificatePath, TAKE1,
+ "SSL CA Certificate path "
+ "(`/path/to/dir' - contains PEM encoded files)")
+ AP_SRV_CMD(CACertificateFile, TAKE1,
+ "SSL CA Certificate file "
+ "(`/path/to/file' - PEM encoded)")
+#endif
+ AP_SRV_CMD(CARevocationPath, TAKE1,
+ "SSL CA Certificate Revocation List (CRL) path "
+ "(`/path/to/dir' - contains PEM encoded files)")
+ AP_SRV_CMD(CARevocationFile, TAKE1,
+ "SSL CA Certificate Revocation List (CRL) file "
+ "(`/path/to/file' - PEM encoded)")
+ AP_ALL_CMD(VerifyClient, TAKE1,
+ "SSL Client verify type "
+ "(`none', `optional', `require', `optional_no_ca')")
+ AP_ALL_CMD(VerifyDepth, TAKE1,
+ "SSL Client verify depth "
+ "(`N' - number of intermediate certificates)")
+ AP_SRV_CMD(SessionCacheTimeout, TAKE1,
+ "SSL Session Cache object lifetime "
+ "(`N' - number of seconds)")
+ AP_SRV_CMD(Log, TAKE1,
+ "SSL logfile for SSL-related messages "
+ "(`/path/to/file', `|/path/to/program')")
+ AP_SRV_CMD(LogLevel, TAKE1,
+ "SSL logfile verbosity level "
+ "(`none', `error', `warn', `info', `debug')")
+ AP_SRV_CMD(Protocol, RAW_ARGS,
+ "Enable or disable various SSL protocols"
+ "(`[+-][SSLv2|SSLv3|TLSv1] ...' - see manual)")
+
+#ifdef SSL_EXPERIMENTAL_PROXY
+ /*
+ * Proxy configuration for remote SSL connections
+ */
+ AP_SRV_CMD(ProxyProtocol, RAW_ARGS,
+ "SSL Proxy: enable or disable SSL protocol flavors "
+ "(`[+-][SSLv2|SSLv3|TLSv1] ...' - see manual)")
+ AP_SRV_CMD(ProxyCipherSuite, TAKE1,
+ "SSL Proxy: colon-delimited list of permitted SSL ciphers "
+ "(`XXX:...:XXX' - see manual)")
+ AP_SRV_CMD(ProxyVerify, FLAG,
+ "SSL Proxy: whether to verify the remote certificate "
+ "(`on' or `off')")
+ AP_SRV_CMD(ProxyVerifyDepth, TAKE1,
+ "SSL Proxy: maximum certificate verification depth "
+ "(`N' - number of intermediate certificates)")
+ AP_SRV_CMD(ProxyCACertificateFile, TAKE1,
+ "SSL Proxy: file containing server certificates "
+ "(`/path/to/file' - PEM encoded certificates)")
+ AP_SRV_CMD(ProxyCACertificatePath, TAKE1,
+ "SSL Proxy: directory containing server certificates "
+ "(`/path/to/dir' - contains PEM encoded certificates)")
+ AP_SRV_CMD(ProxyMachineCertificateFile, TAKE1,
+ "SSL Proxy: file containing client certificates "
+ "(`/path/to/file' - PEM encoded certificates)")
+ AP_SRV_CMD(ProxyMachineCertificatePath, TAKE1,
+ "SSL Proxy: directory containing client certificates "
+ "(`/path/to/dir' - contains PEM encoded certificates)")
+#endif
+
+ /*
+ * Per-directory context configuration directives
+ */
+ AP_DIR_CMD(Options, OPTIONS, RAW_ARGS,
+ "Set one of more options to configure the SSL engine"
+ "(`[+-]option[=value] ...' - see manual)")
+ AP_DIR_CMD(RequireSSL, AUTHCFG, NO_ARGS,
+ "Require the SSL protocol for the per-directory context "
+ "(no arguments)")
+ AP_DIR_CMD(Require, AUTHCFG, RAW_ARGS,
+ "Require a boolean expresion to evaluate to true for granting access"
+ "(arbitrary complex boolean expression - see manual)")
+
+ AP_END_CMD
+};
+
+static const handler_rec ssl_config_handler[] = {
+ { "mod_ssl:content-handler", ssl_hook_Handler },
+ { NULL, NULL }
+};
+
+/*
+ * the main Apache API config structure
+ */
+module MODULE_VAR_EXPORT ssl_module = {
+ STANDARD_MODULE_STUFF,
+
+ /* Standard API (always present) */
+
+ ssl_init_Module, /* module initializer */
+ ssl_config_perdir_create, /* create per-dir config structures */
+ ssl_config_perdir_merge, /* merge per-dir config structures */
+ ssl_config_server_create, /* create per-server config structures */
+ ssl_config_server_merge, /* merge per-server config structures */
+ ssl_config_cmds, /* table of config file commands */
+ ssl_config_handler, /* [#8] MIME-typed-dispatched handlers */
+ ssl_hook_Translate, /* [#1] URI to filename translation */
+ ssl_hook_Auth, /* [#4] validate user id from request */
+ ssl_hook_UserCheck, /* [#5] check if the user is ok _here_ */
+ ssl_hook_Access, /* [#3] check access by host address */
+ NULL, /* [#6] determine MIME type */
+ ssl_hook_Fixup, /* [#7] pre-run fixups */
+ NULL, /* [#9] log a transaction */
+ NULL, /* [#2] header parser */
+ ssl_init_Child, /* child_init */
+ NULL, /* child_exit */
+ ssl_hook_ReadReq, /* [#0] post read-request */
+
+ /* Extended API (forced to be enabled with mod_ssl) */
+
+ ssl_hook_AddModule, /* after modules was added to core */
+ ssl_hook_RemoveModule, /* before module is removed from core */
+ ssl_hook_RewriteCommand, /* configuration command rewriting */
+ ssl_hook_NewConnection, /* socket connection open */
+ ssl_hook_CloseConnection /* socket connection close */
+};
+
diff --git a/modules/ssl/mod_ssl.h b/modules/ssl/mod_ssl.h
new file mode 100644
index 0000000000..8731ef9de4
--- /dev/null
+++ b/modules/ssl/mod_ssl.h
@@ -0,0 +1,854 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** mod_ssl.h
+** Global header
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``The Apache Group: a collection
+ of talented individuals who are
+ trying to perfect the art of
+ never finishing something.''
+ -- Rob Hartill */
+#ifndef MOD_SSL_H
+#define MOD_SSL_H 1
+
+/*
+ * Check whether Extended API (EAPI) is enabled
+ */
+#ifndef EAPI
+#error "mod_ssl requires Extended API (EAPI)"
+#endif
+
+/*
+ * Optionally enable the experimental stuff, but allow the user to
+ * override the decision which experimental parts are included by using
+ * CFLAGS="-DSSL_EXPERIMENTAL_xxxx_IGNORE".
+ */
+#ifdef SSL_EXPERIMENTAL
+#ifndef SSL_EXPERIMENTAL_PERDIRCA_IGNORE
+#define SSL_EXPERIMENTAL_PERDIRCA
+#endif
+#ifndef SSL_EXPERIMENTAL_PROXY_IGNORE
+#define SSL_EXPERIMENTAL_PROXY
+#endif
+#ifdef SSL_ENGINE
+#ifndef SSL_EXPERIMENTAL_ENGINE_IGNORE
+#define SSL_EXPERIMENTAL_ENGINE
+#endif
+#endif
+#endif /* SSL_EXPERIMENTAL */
+
+/*
+ * Power up our brain...
+ */
+
+/* OS headers */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+#ifdef WIN32
+#include <wincrypt.h>
+#include <winsock2.h>
+#endif
+
+/* OpenSSL headers */
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pem.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#ifdef SSL_EXPERIMENTAL_ENGINE
+#include <openssl/engine.h>
+#endif
+
+/* Apache headers */
+#define CORE_PRIVATE
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_conf_globals.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "http_main.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "scoreboard.h"
+#include "util_md5.h"
+#include "fnmatch.h"
+#undef CORE_PRIVATE
+
+/* mod_ssl headers */
+#include "ssl_expr.h"
+#include "ssl_util_ssl.h"
+#include "ssl_util_table.h"
+
+/*
+ * Provide reasonable default for some defines
+ */
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+#ifndef PFALSE
+#define PFALSE ((void *)FALSE)
+#endif
+#ifndef PTRUE
+#define PTRUE ((void *)TRUE)
+#endif
+#ifndef UNSET
+#define UNSET (-1)
+#endif
+#ifndef NUL
+#define NUL '\0'
+#endif
+#ifndef RAND_MAX
+#include <limits.h>
+#define RAND_MAX INT_MAX
+#endif
+
+/*
+ * Provide reasonable defines for some types
+ */
+#ifndef BOOL
+#define BOOL unsigned int
+#endif
+#ifndef UCHAR
+#define UCHAR unsigned char
+#endif
+
+/*
+ * Provide useful shorthands
+ */
+#define strEQ(s1,s2) (strcmp(s1,s2) == 0)
+#define strNE(s1,s2) (strcmp(s1,s2) != 0)
+#define strEQn(s1,s2,n) (strncmp(s1,s2,n) == 0)
+#define strNEn(s1,s2,n) (strncmp(s1,s2,n) != 0)
+
+#define strcEQ(s1,s2) (strcasecmp(s1,s2) == 0)
+#define strcNE(s1,s2) (strcasecmp(s1,s2) != 0)
+#define strcEQn(s1,s2,n) (strncasecmp(s1,s2,n) == 0)
+#define strcNEn(s1,s2,n) (strncasecmp(s1,s2,n) != 0)
+
+#define strIsEmpty(s) (s == NULL || s[0] == NUL)
+
+#define cfgMerge(el,unset) new->el = add->el == unset ? base->el : add->el
+#define cfgMergeArray(el) new->el = ap_append_arrays(p, add->el, base->el)
+#define cfgMergeTable(el) new->el = ap_overlay_tables(p, add->el, base->el)
+#define cfgMergeCtx(el) new->el = ap_ctx_overlay(p, add->el, base->el)
+#define cfgMergeString(el) cfgMerge(el, NULL)
+#define cfgMergeBool(el) cfgMerge(el, UNSET)
+#define cfgMergeInt(el) cfgMerge(el, UNSET)
+
+#define myModConfig() (SSLModConfigRec *)ap_ctx_get(ap_global_ctx, "ssl_module")
+#define mySrvConfig(srv) (SSLSrvConfigRec *)ap_get_module_config(srv->module_config, &ssl_module)
+#define myDirConfig(req) (SSLDirConfigRec *)ap_get_module_config(req->per_dir_config, &ssl_module)
+
+#define myCtxVarSet(mc,num,val) mc->rCtx.pV##num = val
+#define myCtxVarGet(mc,num,type) (type)(mc->rCtx.pV##num)
+
+#define AP_ALL_CMD(name, args, desc) \
+ { "SSL"#name, ssl_cmd_SSL##name, NULL, RSRC_CONF|OR_AUTHCFG, args, desc },
+#define AP_SRV_CMD(name, args, desc) \
+ { "SSL"#name, ssl_cmd_SSL##name, NULL, RSRC_CONF, args, desc },
+#define AP_DIR_CMD(name, type, args, desc) \
+ { "SSL"#name, ssl_cmd_SSL##name, NULL, OR_##type, args, desc },
+#define AP_END_CMD \
+ { NULL }
+
+/*
+ * SSL Logging
+ */
+#define SSL_LOG_NONE (1<<0)
+#define SSL_LOG_ERROR (1<<1)
+#define SSL_LOG_WARN (1<<2)
+#define SSL_LOG_INFO (1<<3)
+#define SSL_LOG_TRACE (1<<4)
+#define SSL_LOG_DEBUG (1<<5)
+#define SSL_LOG_MASK (SSL_LOG_ERROR|SSL_LOG_WARN|SSL_LOG_INFO|SSL_LOG_TRACE|SSL_LOG_DEBUG)
+
+#define SSL_ADD_NONE (1<<8)
+#define SSL_ADD_ERRNO (1<<9)
+#define SSL_ADD_SSLERR (1<<10)
+#define SSL_NO_TIMESTAMP (1<<11)
+#define SSL_NO_LEVELID (1<<12)
+#define SSL_NO_NEWLINE (1<<13)
+
+/*
+ * Defaults for the configuration
+ */
+#ifndef SSL_SESSION_CACHE_TIMEOUT
+#define SSL_SESSION_CACHE_TIMEOUT 300
+#endif
+
+/*
+ * Support for file locking: Try to determine whether we should use fcntl() or
+ * flock(). Would be better ap_config.h could provide this... :-(
+ */
+#if defined(USE_FCNTL_SERIALIZED_ACCEPT)
+#define SSL_USE_FCNTL 1
+#include <fcntl.h>
+#endif
+#if defined(USE_FLOCK_SERIALIZED_ACCEPT)
+#define SSL_USE_FLOCK 1
+#include <sys/file.h>
+#endif
+#if !defined(SSL_USE_FCNTL) && !defined(SSL_USE_FLOCK)
+#define SSL_USE_FLOCK 1
+#if !defined(MPE) && !defined(WIN32)
+#include <sys/file.h>
+#endif
+#ifndef LOCK_UN
+#undef SSL_USE_FLOCK
+#define SSL_USE_FCNTL 1
+#include <fcntl.h>
+#endif
+#endif
+#ifdef AIX
+#undef SSL_USE_FLOCK
+#define SSL_USE_FCNTL 1
+#include <fcntl.h>
+#endif
+
+/*
+ * Support for Mutex
+ */
+#ifndef WIN32
+#define SSL_MUTEX_LOCK_MODE ( S_IRUSR|S_IWUSR )
+#else
+#define SSL_MUTEX_LOCK_MODE (_S_IREAD|_S_IWRITE )
+#endif
+#if defined(USE_SYSVSEM_SERIALIZED_ACCEPT) ||\
+ (defined(__FreeBSD__) && defined(__FreeBSD_version) &&\
+ __FreeBSD_version >= 300000) ||\
+ (defined(LINUX) && defined(__GLIBC__) && defined(__GLIBC_MINOR__) &&\
+ LINUX >= 2 && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) ||\
+ defined(SOLARIS2) || defined(__hpux) ||\
+ (defined (__digital__) && defined (__unix__))
+#define SSL_CAN_USE_SEM
+#define SSL_HAVE_IPCSEM
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+/*
+ * Some platforms have a `union semun' pre-defined but Single Unix
+ * Specification (SUSv2) says in semctl(2): `If required, it is of
+ * type union semun, which the application program must explicitly
+ * declare'. So we define it always ourself to avoid problems (but under
+ * a different name to avoid a namespace clash).
+ */
+union ssl_ipc_semun {
+ long val;
+ struct semid_ds *buf;
+ unsigned short int *array;
+};
+#endif
+#ifdef WIN32
+#define SSL_CAN_USE_SEM
+#define SSL_HAVE_W32SEM
+#include "multithread.h"
+#include <process.h>
+#endif
+
+/*
+ * Support for MM library
+ */
+#ifndef WIN32
+#define SSL_MM_FILE_MODE ( S_IRUSR|S_IWUSR )
+#else
+#define SSL_MM_FILE_MODE ( _S_IREAD|_S_IWRITE )
+#endif
+
+/*
+ * Support for DBM library
+ */
+#ifndef WIN32
+#define SSL_DBM_FILE_MODE ( S_IRUSR|S_IWUSR )
+#else
+#define SSL_USE_SDBM
+#define SSL_DBM_FILE_MODE ( _S_IREAD|_S_IWRITE )
+#endif
+
+#ifdef SSL_USE_SDBM
+#include "ssl_util_sdbm.h"
+#define ssl_dbm_open sdbm_open
+#define ssl_dbm_close sdbm_close
+#define ssl_dbm_store sdbm_store
+#define ssl_dbm_fetch sdbm_fetch
+#define ssl_dbm_delete sdbm_delete
+#define ssl_dbm_firstkey sdbm_firstkey
+#define ssl_dbm_nextkey sdbm_nextkey
+#define SSL_DBM_FILE_SUFFIX_DIR ".dir"
+#define SSL_DBM_FILE_SUFFIX_PAG ".pag"
+#else /* !SSL_USE_SDBM */
+#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) \
+ && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+#include <db1/ndbm.h>
+#else
+#include <ndbm.h>
+#endif
+#define ssl_dbm_open dbm_open
+#define ssl_dbm_close dbm_close
+#define ssl_dbm_store dbm_store
+#define ssl_dbm_fetch dbm_fetch
+#define ssl_dbm_delete dbm_delete
+#define ssl_dbm_firstkey dbm_firstkey
+#define ssl_dbm_nextkey dbm_nextkey
+#if !defined(SSL_DBM_FILE_SUFFIX_DIR) && !defined(SSL_DBM_FILE_SUFFIX_PAG)
+#if defined(DBM_SUFFIX)
+#define SSL_DBM_FILE_SUFFIX_DIR DBM_SUFFIX
+#define SSL_DBM_FILE_SUFFIX_PAG DBM_SUFFIX
+#elif defined(__FreeBSD__) || (defined(DB_LOCK) && defined(DB_SHMEM))
+#define SSL_DBM_FILE_SUFFIX_DIR ".db"
+#define SSL_DBM_FILE_SUFFIX_PAG ".db"
+#else
+#define SSL_DBM_FILE_SUFFIX_DIR ".dir"
+#define SSL_DBM_FILE_SUFFIX_PAG ".pag"
+#endif
+#endif
+#endif /* !SSL_USE_SDBM */
+
+/*
+ * Check for OpenSSL version
+ */
+#if SSL_LIBRARY_VERSION < 0x00903100
+#error "mod_ssl requires OpenSSL 0.9.3 or higher"
+#endif
+
+/*
+ * The own data structures
+ */
+typedef struct {
+ pool *pPool;
+ pool *pSubPool;
+ array_header *aData;
+} ssl_ds_array;
+
+typedef struct {
+ pool *pPool;
+ pool *pSubPool;
+ array_header *aKey;
+ array_header *aData;
+} ssl_ds_table;
+
+/*
+ * Define the certificate algorithm types
+ */
+
+typedef int ssl_algo_t;
+
+#define SSL_ALGO_UNKNOWN (0)
+#define SSL_ALGO_RSA (1<<0)
+#define SSL_ALGO_DSA (1<<1)
+#define SSL_ALGO_ALL (SSL_ALGO_RSA|SSL_ALGO_DSA)
+
+#define SSL_AIDX_RSA (0)
+#define SSL_AIDX_DSA (1)
+#define SSL_AIDX_MAX (2)
+
+/*
+ * Define IDs for the temporary RSA keys and DH params
+ */
+
+#define SSL_TKP_GEN (0)
+#define SSL_TKP_ALLOC (1)
+#define SSL_TKP_FREE (2)
+
+#define SSL_TKPIDX_RSA512 (0)
+#define SSL_TKPIDX_RSA1024 (1)
+#define SSL_TKPIDX_DH512 (2)
+#define SSL_TKPIDX_DH1024 (3)
+#define SSL_TKPIDX_MAX (4)
+
+/*
+ * Define the SSL options
+ */
+#define SSL_OPT_NONE (0)
+#define SSL_OPT_RELSET (1<<0)
+#define SSL_OPT_STDENVVARS (1<<1)
+#define SSL_OPT_COMPATENVVARS (1<<2)
+#define SSL_OPT_EXPORTCERTDATA (1<<3)
+#define SSL_OPT_FAKEBASICAUTH (1<<4)
+#define SSL_OPT_STRICTREQUIRE (1<<5)
+#define SSL_OPT_OPTRENEGOTIATE (1<<6)
+#define SSL_OPT_ALL (SSL_OPT_STDENVVARS|SSL_OPT_COMPATENVVAR|SSL_OPT_EXPORTCERTDATA|SSL_OPT_FAKEBASICAUTH|SSL_OPT_STRICTREQUIRE|SSL_OPT_OPTRENEGOTIATE)
+typedef int ssl_opt_t;
+
+/*
+ * Define the SSL Protocol options
+ */
+#define SSL_PROTOCOL_NONE (0)
+#define SSL_PROTOCOL_SSLV2 (1<<0)
+#define SSL_PROTOCOL_SSLV3 (1<<1)
+#define SSL_PROTOCOL_TLSV1 (1<<2)
+#define SSL_PROTOCOL_ALL (SSL_PROTOCOL_SSLV2|SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1)
+typedef int ssl_proto_t;
+
+/*
+ * Define the SSL verify levels
+ */
+typedef enum {
+ SSL_CVERIFY_UNSET = UNSET,
+ SSL_CVERIFY_NONE = 0,
+ SSL_CVERIFY_OPTIONAL = 1,
+ SSL_CVERIFY_REQUIRE = 2,
+ SSL_CVERIFY_OPTIONAL_NO_CA = 3
+} ssl_verify_t;
+
+/*
+ * Define the SSL pass phrase dialog types
+ */
+typedef enum {
+ SSL_PPTYPE_UNSET = UNSET,
+ SSL_PPTYPE_BUILTIN = 0,
+ SSL_PPTYPE_FILTER = 1
+} ssl_pphrase_t;
+
+/*
+ * Define the Path Checking modes
+ */
+#define SSL_PCM_EXISTS 1
+#define SSL_PCM_ISREG 2
+#define SSL_PCM_ISDIR 4
+#define SSL_PCM_ISNONZERO 8
+typedef unsigned int ssl_pathcheck_t;
+
+/*
+ * Define the SSL session cache modes and structures
+ */
+typedef enum {
+ SSL_SCMODE_UNSET = UNSET,
+ SSL_SCMODE_NONE = 0,
+ SSL_SCMODE_DBM = 1,
+ SSL_SCMODE_SHMHT = 2,
+ SSL_SCMODE_SHMCB = 3
+} ssl_scmode_t;
+
+/*
+ * Define the SSL mutex modes
+ */
+typedef enum {
+ SSL_MUTEXMODE_UNSET = UNSET,
+ SSL_MUTEXMODE_NONE = 0,
+ SSL_MUTEXMODE_FILE = 1,
+ SSL_MUTEXMODE_SEM = 2
+} ssl_mutexmode_t;
+
+/*
+ * Define the SSL requirement structure
+ */
+typedef struct {
+ char *cpExpr;
+ ssl_expr *mpExpr;
+} ssl_require_t;
+
+/*
+ * Define the SSL random number generator seeding source
+ */
+typedef enum {
+ SSL_RSCTX_STARTUP = 1,
+ SSL_RSCTX_CONNECT = 2
+} ssl_rsctx_t;
+typedef enum {
+ SSL_RSSRC_BUILTIN = 1,
+ SSL_RSSRC_FILE = 2,
+ SSL_RSSRC_EXEC = 3
+#if SSL_LIBRARY_VERSION >= 0x00905100
+ ,SSL_RSSRC_EGD = 4
+#endif
+} ssl_rssrc_t;
+typedef struct {
+ ssl_rsctx_t nCtx;
+ ssl_rssrc_t nSrc;
+ char *cpPath;
+ int nBytes;
+} ssl_randseed_t;
+
+/*
+ * Define the structure of an ASN.1 anything
+ */
+typedef struct {
+ long int nData;
+ unsigned char *cpData;
+} ssl_asn1_t;
+
+/*
+ * Define the mod_ssl per-module configuration structure
+ * (i.e. the global configuration for each httpd process)
+ */
+
+typedef struct {
+ pool *pPool;
+ BOOL bFixed;
+ int nInitCount;
+ int nSessionCacheMode;
+ char *szSessionCacheDataFile;
+ int nSessionCacheDataSize;
+ AP_MM *pSessionCacheDataMM;
+ table_t *tSessionCacheDataTable;
+ ssl_mutexmode_t nMutexMode;
+ char *szMutexFile;
+ int nMutexFD;
+ int nMutexSEMID;
+ array_header *aRandSeed;
+ ssl_ds_table *tTmpKeys;
+ void *pTmpKeys[SSL_TKPIDX_MAX];
+ ssl_ds_table *tPublicCert;
+ ssl_ds_table *tPrivateKey;
+#ifdef SSL_EXPERIMENTAL_ENGINE
+ char *szCryptoDevice;
+#endif
+ struct {
+ void *pV1, *pV2, *pV3, *pV4, *pV5, *pV6, *pV7, *pV8, *pV9, *pV10;
+ } rCtx;
+#ifdef SSL_VENDOR
+ ap_ctx *ctx;
+#endif
+} SSLModConfigRec;
+
+/*
+ * Define the mod_ssl per-server configuration structure
+ * (i.e. the configuration for the main server
+ * and all <VirtualHost> contexts)
+ */
+typedef struct {
+ BOOL bEnabled;
+ char *szPublicCertFile[SSL_AIDX_MAX];
+ char *szPrivateKeyFile[SSL_AIDX_MAX];
+ char *szCertificateChain;
+ char *szCACertificatePath;
+ char *szCACertificateFile;
+ char *szLogFile;
+ char *szCipherSuite;
+ FILE *fileLogFile;
+ int nLogLevel;
+ int nVerifyDepth;
+ ssl_verify_t nVerifyClient;
+ X509 *pPublicCert[SSL_AIDX_MAX];
+ EVP_PKEY *pPrivateKey[SSL_AIDX_MAX];
+ SSL_CTX *pSSLCtx;
+ int nSessionCacheTimeout;
+ int nPassPhraseDialogType;
+ char *szPassPhraseDialogPath;
+ ssl_proto_t nProtocol;
+ char *szCARevocationPath;
+ char *szCARevocationFile;
+ X509_STORE *pRevocationStore;
+#ifdef SSL_EXPERIMENTAL_PROXY
+ /* Configuration details for proxy operation */
+ ssl_proto_t nProxyProtocol;
+ int bProxyVerify;
+ int nProxyVerifyDepth;
+ char *szProxyCACertificatePath;
+ char *szProxyCACertificateFile;
+ char *szProxyClientCertificateFile;
+ char *szProxyClientCertificatePath;
+ char *szProxyCipherSuite;
+ SSL_CTX *pSSLProxyCtx;
+ STACK_OF(X509_INFO) *skProxyClientCerts;
+#endif
+#ifdef SSL_VENDOR
+ ap_ctx *ctx;
+#endif
+} SSLSrvConfigRec;
+
+/*
+ * Define the mod_ssl per-directory configuration structure
+ * (i.e. the local configuration for all <Directory>
+ * and .htaccess contexts)
+ */
+typedef struct {
+ BOOL bSSLRequired;
+ array_header *aRequirement;
+ ssl_opt_t nOptions;
+ ssl_opt_t nOptionsAdd;
+ ssl_opt_t nOptionsDel;
+ char *szCipherSuite;
+ ssl_verify_t nVerifyClient;
+ int nVerifyDepth;
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+ char *szCACertificatePath;
+ char *szCACertificateFile;
+#endif
+#ifdef SSL_VENDOR
+ ap_ctx *ctx;
+#endif
+} SSLDirConfigRec;
+
+/*
+ * function prototypes
+ */
+
+/* API glue structures */
+extern module MODULE_VAR_EXPORT ssl_module;
+
+/* configuration handling */
+void ssl_config_global_create(void);
+void ssl_config_global_fix(void);
+BOOL ssl_config_global_isfixed(void);
+void *ssl_config_server_create(pool *, server_rec *);
+void *ssl_config_server_merge(pool *, void *, void *);
+void *ssl_config_perdir_create(pool *, char *);
+void *ssl_config_perdir_merge(pool *, void *, void *);
+const char *ssl_cmd_SSLMutex(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLPassPhraseDialog(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLCryptoDevice(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLRandomSeed(cmd_parms *, char *, char *, char *, char *);
+const char *ssl_cmd_SSLEngine(cmd_parms *, char *, int);
+const char *ssl_cmd_SSLCipherSuite(cmd_parms *, SSLDirConfigRec *, char *);
+const char *ssl_cmd_SSLCertificateFile(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLCertificateChainFile(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLCACertificatePath(cmd_parms *, SSLDirConfigRec *, char *);
+const char *ssl_cmd_SSLCACertificateFile(cmd_parms *, SSLDirConfigRec *, char *);
+const char *ssl_cmd_SSLCARevocationPath(cmd_parms *, SSLDirConfigRec *, char *);
+const char *ssl_cmd_SSLCARevocationFile(cmd_parms *, SSLDirConfigRec *, char *);
+const char *ssl_cmd_SSLVerifyClient(cmd_parms *, SSLDirConfigRec *, char *);
+const char *ssl_cmd_SSLVerifyDepth(cmd_parms *, SSLDirConfigRec *, char *);
+const char *ssl_cmd_SSLSessionCache(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLSessionCacheTimeout(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLLog(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLLogLevel(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLProtocol(cmd_parms *, char *, const char *);
+const char *ssl_cmd_SSLOptions(cmd_parms *, SSLDirConfigRec *, const char *);
+const char *ssl_cmd_SSLRequireSSL(cmd_parms *, SSLDirConfigRec *, char *);
+const char *ssl_cmd_SSLRequire(cmd_parms *, SSLDirConfigRec *, char *);
+#ifdef SSL_EXPERIMENTAL_PROXY
+const char *ssl_cmd_SSLProxyProtocol(cmd_parms *, char *, const char *);
+const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLProxyVerify(cmd_parms *, char *, int);
+const char *ssl_cmd_SSLProxyVerifyDepth(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLProxyCACertificatePath(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLProxyCACertificateFile(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *, char *, char *);
+const char *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *, char *, char *);
+#endif
+
+/* module initialization */
+void ssl_init_Module(server_rec *, pool *);
+void ssl_init_SSLLibrary(void);
+void ssl_init_Engine(server_rec *, pool *);
+void ssl_init_TmpKeysHandle(int, server_rec *, pool *);
+void ssl_init_ConfigureServer(server_rec *, pool *, SSLSrvConfigRec *);
+void ssl_init_CheckServers(server_rec *, pool *);
+STACK_OF(X509_NAME)
+ *ssl_init_FindCAList(server_rec *, pool *, char *, char *);
+void ssl_init_Child(server_rec *, pool *);
+void ssl_init_ChildKill(void *);
+void ssl_init_ModuleKill(void *);
+
+/* Apache API hooks */
+void ssl_hook_AddModule(module *);
+void ssl_hook_RemoveModule(module *);
+char *ssl_hook_RewriteCommand(cmd_parms *, void *, const char *);
+void ssl_hook_NewConnection(conn_rec *);
+void ssl_hook_TimeoutConnection(int);
+void ssl_hook_CloseConnection(conn_rec *);
+int ssl_hook_Translate(request_rec *);
+int ssl_hook_Auth(request_rec *);
+int ssl_hook_UserCheck(request_rec *);
+int ssl_hook_Access(request_rec *);
+int ssl_hook_Fixup(request_rec *);
+int ssl_hook_ReadReq(request_rec *);
+int ssl_hook_Handler(request_rec *);
+
+/* OpenSSL callbacks */
+RSA *ssl_callback_TmpRSA(SSL *, int, int);
+DH *ssl_callback_TmpDH(SSL *, int, int);
+int ssl_callback_SSLVerify(int, X509_STORE_CTX *);
+int ssl_callback_SSLVerify_CRL(int, X509_STORE_CTX *, server_rec *);
+int ssl_callback_NewSessionCacheEntry(SSL *, SSL_SESSION *);
+SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *, unsigned char *, int, int *);
+void ssl_callback_DelSessionCacheEntry(SSL_CTX *, SSL_SESSION *);
+void ssl_callback_LogTracingState(SSL *, int, int);
+
+/* Session Cache Support */
+void ssl_scache_init(server_rec *, pool *);
+void ssl_scache_kill(server_rec *);
+BOOL ssl_scache_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *);
+SSL_SESSION *ssl_scache_retrieve(server_rec *, UCHAR *, int);
+void ssl_scache_remove(server_rec *, UCHAR *, int);
+void ssl_scache_expire(server_rec *);
+void ssl_scache_status(server_rec *, pool *, void (*)(char *, void *), void *);
+char *ssl_scache_id2sz(UCHAR *, int);
+void ssl_scache_dbm_init(server_rec *, pool *);
+void ssl_scache_dbm_kill(server_rec *);
+BOOL ssl_scache_dbm_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *);
+SSL_SESSION *ssl_scache_dbm_retrieve(server_rec *, UCHAR *, int);
+void ssl_scache_dbm_remove(server_rec *, UCHAR *, int);
+void ssl_scache_dbm_expire(server_rec *);
+void ssl_scache_dbm_status(server_rec *, pool *, void (*)(char *, void *), void *);
+void ssl_scache_shmht_init(server_rec *, pool *);
+void ssl_scache_shmht_kill(server_rec *);
+BOOL ssl_scache_shmht_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *);
+SSL_SESSION *ssl_scache_shmht_retrieve(server_rec *, UCHAR *, int);
+void ssl_scache_shmht_remove(server_rec *, UCHAR *, int);
+void ssl_scache_shmht_expire(server_rec *);
+void ssl_scache_shmht_status(server_rec *, pool *, void (*)(char *, void *), void *);
+void ssl_scache_shmcb_init(server_rec *, pool *);
+void ssl_scache_shmcb_kill(server_rec *);
+BOOL ssl_scache_shmcb_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *);
+SSL_SESSION *ssl_scache_shmcb_retrieve(server_rec *, UCHAR *, int);
+void ssl_scache_shmcb_remove(server_rec *, UCHAR *, int);
+void ssl_scache_shmcb_expire(server_rec *);
+void ssl_scache_shmcb_status(server_rec *, pool *, void (*)(char *, void *), void *);
+
+/* Pass Phrase Support */
+void ssl_pphrase_Handle(server_rec *, pool *);
+int ssl_pphrase_Handle_CB(char *, int, int);
+
+/* Diffie-Hellman Parameter Support */
+DH *ssl_dh_GetTmpParam(int);
+DH *ssl_dh_GetParamFromFile(char *);
+
+/* Data Structures */
+ssl_ds_array *ssl_ds_array_make(pool *, int);
+BOOL ssl_ds_array_isempty(ssl_ds_array *);
+void *ssl_ds_array_push(ssl_ds_array *);
+void *ssl_ds_array_get(ssl_ds_array *, int);
+void ssl_ds_array_wipeout(ssl_ds_array *);
+void ssl_ds_array_kill(ssl_ds_array *);
+ssl_ds_table *ssl_ds_table_make(pool *, int);
+BOOL ssl_ds_table_isempty(ssl_ds_table *);
+void *ssl_ds_table_push(ssl_ds_table *, char *);
+void *ssl_ds_table_get(ssl_ds_table *, char *);
+void ssl_ds_table_wipeout(ssl_ds_table *);
+void ssl_ds_table_kill(ssl_ds_table *);
+
+/* Mutex Support */
+void ssl_mutex_init(server_rec *, pool *);
+void ssl_mutex_reinit(server_rec *, pool *);
+void ssl_mutex_on(server_rec *);
+void ssl_mutex_off(server_rec *);
+void ssl_mutex_kill(server_rec *s);
+void ssl_mutex_file_create(server_rec *, pool *);
+void ssl_mutex_file_open(server_rec *, pool *);
+void ssl_mutex_file_remove(void *);
+BOOL ssl_mutex_file_acquire(void);
+BOOL ssl_mutex_file_release(void);
+void ssl_mutex_sem_create(server_rec *, pool *);
+void ssl_mutex_sem_open(server_rec *, pool *);
+void ssl_mutex_sem_remove(void *);
+BOOL ssl_mutex_sem_acquire(void);
+BOOL ssl_mutex_sem_release(void);
+
+/* Logfile Support */
+void ssl_log_open(server_rec *, server_rec *, pool *);
+BOOL ssl_log_applies(server_rec *, int);
+void ssl_log(server_rec *, int, const char *, ...);
+void ssl_die(void);
+
+/* Variables */
+void ssl_var_register(void);
+void ssl_var_unregister(void);
+char *ssl_var_lookup(pool *, server_rec *, conn_rec *, request_rec *, char *);
+
+/* I/O */
+void ssl_io_register(void);
+void ssl_io_unregister(void);
+long ssl_io_data_cb(BIO *, int, const char *, int, long, long);
+#ifndef SSL_CONSERVATIVE
+void ssl_io_suck(request_rec *, SSL *);
+#endif
+
+/* PRNG */
+int ssl_rand_seed(server_rec *, pool *, ssl_rsctx_t, char *);
+
+/* Extensions */
+void ssl_ext_register(void);
+void ssl_ext_unregister(void);
+
+/* Compatibility */
+#ifdef SSL_COMPAT
+char *ssl_compat_directive(server_rec *, pool *, const char *);
+void ssl_compat_variables(request_rec *);
+#endif
+
+/* Utility Functions */
+char *ssl_util_server_root_relative(pool *, char *, char *);
+char *ssl_util_vhostid(pool *, server_rec *);
+void ssl_util_strupper(char *);
+void ssl_util_uuencode(char *, const char *, BOOL);
+void ssl_util_uuencode_binary(unsigned char *, const unsigned char *, int, BOOL);
+FILE *ssl_util_ppopen(server_rec *, pool *, char *);
+int ssl_util_ppopen_child(void *, child_info *);
+void ssl_util_ppclose(server_rec *, pool *, FILE *);
+char *ssl_util_readfilter(server_rec *, pool *, char *);
+BOOL ssl_util_path_check(ssl_pathcheck_t, char *);
+ssl_algo_t ssl_util_algotypeof(X509 *, EVP_PKEY *);
+char *ssl_util_algotypestr(ssl_algo_t);
+char *ssl_util_ptxtsub(pool *, const char *, const char *, char *);
+void ssl_util_thread_setup(void);
+
+/* Vendor extension support */
+#if defined(SSL_VENDOR) && defined(SSL_VENDOR_OBJS)
+void ssl_vendor_register(void);
+void ssl_vendor_unregister(void);
+#endif
+
+#endif /* MOD_SSL_H */
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
new file mode 100644
index 0000000000..c47340b223
--- /dev/null
+++ b/modules/ssl/ssl_engine_config.c
@@ -0,0 +1,1093 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_engine_config.c
+** Apache Configuration Directives
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+ /* ``Damned if you do,
+ damned if you don't.''
+ -- Unknown */
+#include "mod_ssl.h"
+
+
+/* _________________________________________________________________
+**
+** Support for Global Configuration
+** _________________________________________________________________
+*/
+
+void ssl_hook_AddModule(module *m)
+{
+ if (m == &ssl_module) {
+ /*
+ * Announce us for the configuration files
+ */
+ ap_add_config_define("MOD_SSL");
+
+ /*
+ * Link ourself into the Apache kernel
+ */
+ ssl_var_register();
+ ssl_ext_register();
+ ssl_io_register();
+#if defined(SSL_VENDOR) && defined(SSL_VENDOR_OBJS)
+ ssl_vendor_register();
+#endif
+ }
+ return;
+}
+
+void ssl_hook_RemoveModule(module *m)
+{
+ if (m == &ssl_module) {
+ /*
+ * Unlink ourself from the Apache kernel
+ */
+ ssl_var_unregister();
+ ssl_ext_unregister();
+ ssl_io_unregister();
+#if defined(SSL_VENDOR) && defined(SSL_VENDOR_OBJS)
+ ssl_vendor_unregister();
+#endif
+ }
+ return;
+}
+
+void ssl_config_global_create(void)
+{
+ pool *pPool;
+ SSLModConfigRec *mc;
+
+ mc = ap_ctx_get(ap_global_ctx, "ssl_module");
+ if (mc == NULL) {
+ /*
+ * allocate an own subpool which survives server restarts
+ */
+ pPool = ap_make_sub_pool(NULL);
+ mc = (SSLModConfigRec *)ap_palloc(pPool, sizeof(SSLModConfigRec));
+ mc->pPool = pPool;
+ mc->bFixed = FALSE;
+
+ /*
+ * initialize per-module configuration
+ */
+ mc->nInitCount = 0;
+ mc->nSessionCacheMode = SSL_SCMODE_UNSET;
+ mc->szSessionCacheDataFile = NULL;
+ mc->nSessionCacheDataSize = 0;
+ mc->pSessionCacheDataMM = NULL;
+ mc->tSessionCacheDataTable = NULL;
+ mc->nMutexMode = SSL_MUTEXMODE_UNSET;
+ mc->szMutexFile = NULL;
+ mc->nMutexFD = -1;
+ mc->nMutexSEMID = -1;
+ mc->aRandSeed = ap_make_array(pPool, 4, sizeof(ssl_randseed_t));
+ mc->tPrivateKey = ssl_ds_table_make(pPool, sizeof(ssl_asn1_t));
+ mc->tPublicCert = ssl_ds_table_make(pPool, sizeof(ssl_asn1_t));
+ mc->tTmpKeys = ssl_ds_table_make(pPool, sizeof(ssl_asn1_t));
+#ifdef SSL_EXPERIMENTAL_ENGINE
+ mc->szCryptoDevice = NULL;
+#endif
+
+ (void)memset(mc->pTmpKeys, 0, SSL_TKPIDX_MAX*sizeof(void *));
+
+#ifdef SSL_VENDOR
+ mc->ctx = ap_ctx_new(pPool);
+ ap_hook_use("ap::mod_ssl::vendor::config_global_create",
+ AP_HOOK_SIG2(void,ptr), AP_HOOK_MODE_ALL, mc);
+#endif
+
+ /*
+ * And push it into Apache's global context
+ */
+ ap_ctx_set(ap_global_ctx, "ssl_module", mc);
+ }
+ return;
+}
+
+void ssl_config_global_fix(void)
+{
+ SSLModConfigRec *mc = myModConfig();
+ mc->bFixed = TRUE;
+ return;
+}
+
+BOOL ssl_config_global_isfixed(void)
+{
+ SSLModConfigRec *mc = myModConfig();
+ return (mc->bFixed);
+}
+
+
+/* _________________________________________________________________
+**
+** Configuration handling
+** _________________________________________________________________
+*/
+
+/*
+ * Create per-server SSL configuration
+ */
+void *ssl_config_server_create(pool *p, server_rec *s)
+{
+ SSLSrvConfigRec *sc;
+
+ ssl_config_global_create();
+
+ sc = ap_palloc(p, sizeof(SSLSrvConfigRec));
+ sc->bEnabled = UNSET;
+ sc->szCACertificatePath = NULL;
+ sc->szCACertificateFile = NULL;
+ sc->szCertificateChain = NULL;
+ sc->szLogFile = NULL;
+ sc->szCipherSuite = NULL;
+ sc->nLogLevel = SSL_LOG_NONE;
+ sc->nVerifyDepth = UNSET;
+ sc->nVerifyClient = SSL_CVERIFY_UNSET;
+ sc->nSessionCacheTimeout = UNSET;
+ sc->nPassPhraseDialogType = SSL_PPTYPE_UNSET;
+ sc->szPassPhraseDialogPath = NULL;
+ sc->nProtocol = SSL_PROTOCOL_ALL;
+ sc->fileLogFile = NULL;
+ sc->pSSLCtx = NULL;
+ sc->szCARevocationPath = NULL;
+ sc->szCARevocationFile = NULL;
+ sc->pRevocationStore = NULL;
+
+#ifdef SSL_EXPERIMENTAL_PROXY
+ sc->nProxyVerifyDepth = UNSET;
+ sc->szProxyCACertificatePath = NULL;
+ sc->szProxyCACertificateFile = NULL;
+ sc->szProxyClientCertificateFile = NULL;
+ sc->szProxyClientCertificatePath = NULL;
+ sc->szProxyCipherSuite = NULL;
+ sc->nProxyProtocol = SSL_PROTOCOL_ALL & ~SSL_PROTOCOL_TLSV1;
+ sc->bProxyVerify = UNSET;
+ sc->pSSLProxyCtx = NULL;
+#endif
+
+ (void)memset(sc->szPublicCertFile, 0, SSL_AIDX_MAX*sizeof(char *));
+ (void)memset(sc->szPrivateKeyFile, 0, SSL_AIDX_MAX*sizeof(char *));
+ (void)memset(sc->pPublicCert, 0, SSL_AIDX_MAX*sizeof(X509 *));
+ (void)memset(sc->pPrivateKey, 0, SSL_AIDX_MAX*sizeof(EVP_PKEY *));
+
+#ifdef SSL_VENDOR
+ sc->ctx = ap_ctx_new(p);
+ ap_hook_use("ap::mod_ssl::vendor::config_server_create",
+ AP_HOOK_SIG4(void,ptr,ptr,ptr), AP_HOOK_MODE_ALL,
+ p, s, sc);
+#endif
+
+ return sc;
+}
+
+/*
+ * Merge per-server SSL configurations
+ */
+void *ssl_config_server_merge(pool *p, void *basev, void *addv)
+{
+ SSLSrvConfigRec *base = (SSLSrvConfigRec *)basev;
+ SSLSrvConfigRec *add = (SSLSrvConfigRec *)addv;
+ SSLSrvConfigRec *new = (SSLSrvConfigRec *)ap_palloc(p, sizeof(SSLSrvConfigRec));
+ int i;
+
+ cfgMergeBool(bEnabled);
+ cfgMergeString(szCACertificatePath);
+ cfgMergeString(szCACertificateFile);
+ cfgMergeString(szCertificateChain);
+ cfgMergeString(szLogFile);
+ cfgMergeString(szCipherSuite);
+ cfgMerge(nLogLevel, SSL_LOG_NONE);
+ cfgMergeInt(nVerifyDepth);
+ cfgMerge(nVerifyClient, SSL_CVERIFY_UNSET);
+ cfgMergeInt(nSessionCacheTimeout);
+ cfgMerge(nPassPhraseDialogType, SSL_PPTYPE_UNSET);
+ cfgMergeString(szPassPhraseDialogPath);
+ cfgMerge(nProtocol, SSL_PROTOCOL_ALL);
+ cfgMerge(fileLogFile, NULL);
+ cfgMerge(pSSLCtx, NULL);
+ cfgMerge(szCARevocationPath, NULL);
+ cfgMerge(szCARevocationFile, NULL);
+ cfgMerge(pRevocationStore, NULL);
+
+ for (i = 0; i < SSL_AIDX_MAX; i++) {
+ cfgMergeString(szPublicCertFile[i]);
+ cfgMergeString(szPrivateKeyFile[i]);
+ cfgMerge(pPublicCert[i], NULL);
+ cfgMerge(pPrivateKey[i], NULL);
+ }
+
+#ifdef SSL_VENDOR
+ cfgMergeCtx(ctx);
+ ap_hook_use("ap::mod_ssl::vendor::config_server_merge",
+ AP_HOOK_SIG5(void,ptr,ptr,ptr,ptr), AP_HOOK_MODE_ALL,
+ p, base, add, new);
+#endif
+
+#ifdef SSL_EXPERIMENTAL_PROXY
+ cfgMergeInt(nProxyVerifyDepth);
+ cfgMergeString(szProxyCACertificatePath);
+ cfgMergeString(szProxyCACertificateFile);
+ cfgMergeString(szProxyClientCertificateFile);
+ cfgMergeString(szProxyClientCertificatePath);
+ cfgMergeString(szProxyCipherSuite);
+ cfgMerge(nProxyProtocol, (SSL_PROTOCOL_ALL & ~SSL_PROTOCOL_TLSV1));
+ cfgMergeBool(bProxyVerify);
+ cfgMerge(pSSLProxyCtx, NULL);
+#endif
+
+ return new;
+}
+
+/*
+ * Create per-directory SSL configuration
+ */
+void *ssl_config_perdir_create(pool *p, char *dir)
+{
+ SSLDirConfigRec *dc = ap_palloc(p, sizeof(SSLDirConfigRec));
+
+ dc->bSSLRequired = FALSE;
+ dc->aRequirement = ap_make_array(p, 4, sizeof(ssl_require_t));
+ dc->nOptions = SSL_OPT_NONE|SSL_OPT_RELSET;
+ dc->nOptionsAdd = SSL_OPT_NONE;
+ dc->nOptionsDel = SSL_OPT_NONE;
+
+ dc->szCipherSuite = NULL;
+ dc->nVerifyClient = SSL_CVERIFY_UNSET;
+ dc->nVerifyDepth = UNSET;
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+ dc->szCACertificatePath = NULL;
+ dc->szCACertificateFile = NULL;
+#endif
+
+#ifdef SSL_VENDOR
+ dc->ctx = ap_ctx_new(p);
+ ap_hook_use("ap::mod_ssl::vendor::config_perdir_create",
+ AP_HOOK_SIG4(void,ptr,ptr,ptr), AP_HOOK_MODE_ALL,
+ p, dir, dc);
+#endif
+
+ return dc;
+}
+
+/*
+ * Merge per-directory SSL configurations
+ */
+void *ssl_config_perdir_merge(pool *p, void *basev, void *addv)
+{
+ SSLDirConfigRec *base = (SSLDirConfigRec *)basev;
+ SSLDirConfigRec *add = (SSLDirConfigRec *)addv;
+ SSLDirConfigRec *new = (SSLDirConfigRec *)ap_palloc(p,
+ sizeof(SSLDirConfigRec));
+
+ cfgMerge(bSSLRequired, FALSE);
+ cfgMergeArray(aRequirement);
+
+ if (add->nOptions & SSL_OPT_RELSET) {
+ new->nOptionsAdd = (base->nOptionsAdd & ~(add->nOptionsDel)) | add->nOptionsAdd;
+ new->nOptionsDel = (base->nOptionsDel & ~(add->nOptionsAdd)) | add->nOptionsDel;
+ new->nOptions = (base->nOptions & ~(new->nOptionsDel)) | new->nOptionsAdd;
+ }
+ else {
+ new->nOptions = add->nOptions;
+ new->nOptionsAdd = add->nOptionsAdd;
+ new->nOptionsDel = add->nOptionsDel;
+ }
+
+ cfgMergeString(szCipherSuite);
+ cfgMerge(nVerifyClient, SSL_CVERIFY_UNSET);
+ cfgMergeInt(nVerifyDepth);
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+ cfgMergeString(szCACertificatePath);
+ cfgMergeString(szCACertificateFile);
+#endif
+
+#ifdef SSL_VENDOR
+ cfgMergeCtx(ctx);
+ ap_hook_use("ap::mod_ssl::vendor::config_perdir_merge",
+ AP_HOOK_SIG5(void,ptr,ptr,ptr,ptr), AP_HOOK_MODE_ALL,
+ p, base, add, new);
+#endif
+
+ return new;
+}
+
+/*
+ * Directive Rewriting
+ */
+
+char *ssl_hook_RewriteCommand(cmd_parms *cmd, void *config, const char *cmd_line)
+{
+#ifdef SSL_COMPAT
+ return ssl_compat_directive(cmd->server, cmd->pool, cmd_line);
+#else
+ return NULL;
+#endif
+}
+
+/*
+ * Configuration functions for particular directives
+ */
+
+const char *ssl_cmd_SSLMutex(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ const char *err;
+ SSLModConfigRec *mc = myModConfig();
+
+ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL)
+ return err;
+ if (ssl_config_global_isfixed())
+ return NULL;
+ if (strcEQ(arg, "none")) {
+ mc->nMutexMode = SSL_MUTEXMODE_NONE;
+ }
+ else if (strlen(arg) > 5 && strcEQn(arg, "file:", 5)) {
+#ifndef WIN32
+ mc->nMutexMode = SSL_MUTEXMODE_FILE;
+ mc->szMutexFile = ap_psprintf(mc->pPool, "%s.%lu",
+ ssl_util_server_root_relative(cmd->pool, "mutex", arg+5),
+ (unsigned long)getpid());
+#else
+ return "SSLMutex: Lockfiles not available on this platform";
+#endif
+ }
+ else if (strcEQ(arg, "sem")) {
+#ifdef SSL_CAN_USE_SEM
+ mc->nMutexMode = SSL_MUTEXMODE_SEM;
+#else
+ return "SSLMutex: Semaphores not available on this platform";
+#endif
+ }
+ else
+ return "SSLMutex: Invalid argument";
+ return NULL;
+}
+
+const char *ssl_cmd_SSLPassPhraseDialog(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL)
+ return err;
+ if (strcEQ(arg, "builtin")) {
+ sc->nPassPhraseDialogType = SSL_PPTYPE_BUILTIN;
+ sc->szPassPhraseDialogPath = NULL;
+ }
+ else if (strlen(arg) > 5 && strEQn(arg, "exec:", 5)) {
+ sc->nPassPhraseDialogType = SSL_PPTYPE_FILTER;
+ sc->szPassPhraseDialogPath = ssl_util_server_root_relative(cmd->pool, "dialog", arg+5);
+ if (!ssl_util_path_check(SSL_PCM_EXISTS, sc->szPassPhraseDialogPath))
+ return ap_pstrcat(cmd->pool, "SSLPassPhraseDialog: file '",
+ sc->szPassPhraseDialogPath, "' not exists", NULL);
+ }
+ else
+ return "SSLPassPhraseDialog: Invalid argument";
+ return NULL;
+}
+
+#ifdef SSL_EXPERIMENTAL_ENGINE
+const char *ssl_cmd_SSLCryptoDevice(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLModConfigRec *mc = myModConfig();
+ const char *err;
+ ENGINE *e;
+#if SSL_LIBRARY_VERSION >= 0x00907000
+ static int loaded_engines = FALSE;
+
+ /* early loading to make sure the engines are already
+ available for ENGINE_by_id() above... */
+ if (!loaded_engines) {
+ ENGINE_load_builtin_engines();
+ loaded_engines = TRUE;
+ }
+#endif
+ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL)
+ return err;
+ if (strcEQ(arg, "builtin")) {
+ mc->szCryptoDevice = NULL;
+ }
+ else if ((e = ENGINE_by_id(arg)) != NULL) {
+ mc->szCryptoDevice = arg;
+ ENGINE_free(e);
+ }
+ else
+ return "SSLCryptoDevice: Invalid argument";
+ return NULL;
+}
+#endif
+
+const char *ssl_cmd_SSLRandomSeed(
+ cmd_parms *cmd, char *struct_ptr, char *arg1, char *arg2, char *arg3)
+{
+ SSLModConfigRec *mc = myModConfig();
+ const char *err;
+ ssl_randseed_t *pRS;
+
+ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL)
+ return err;
+ if (ssl_config_global_isfixed())
+ return NULL;
+ pRS = ap_push_array(mc->aRandSeed);
+ if (strcEQ(arg1, "startup"))
+ pRS->nCtx = SSL_RSCTX_STARTUP;
+ else if (strcEQ(arg1, "connect"))
+ pRS->nCtx = SSL_RSCTX_CONNECT;
+ else
+ return ap_pstrcat(cmd->pool, "SSLRandomSeed: "
+ "invalid context: `", arg1, "'");
+ if (strlen(arg2) > 5 && strEQn(arg2, "file:", 5)) {
+ pRS->nSrc = SSL_RSSRC_FILE;
+ pRS->cpPath = ap_pstrdup(mc->pPool, ssl_util_server_root_relative(cmd->pool, "random", arg2+5));
+ }
+ else if (strlen(arg2) > 5 && strEQn(arg2, "exec:", 5)) {
+ pRS->nSrc = SSL_RSSRC_EXEC;
+ pRS->cpPath = ap_pstrdup(mc->pPool, ssl_util_server_root_relative(cmd->pool, "random", arg2+5));
+ }
+#if SSL_LIBRARY_VERSION >= 0x00905100
+ else if (strlen(arg2) > 4 && strEQn(arg2, "egd:", 4)) {
+ pRS->nSrc = SSL_RSSRC_EGD;
+ pRS->cpPath = ap_pstrdup(mc->pPool, ssl_util_server_root_relative(cmd->pool, "random", arg2+4));
+ }
+#endif
+ else if (strcEQ(arg2, "builtin")) {
+ pRS->nSrc = SSL_RSSRC_BUILTIN;
+ pRS->cpPath = NULL;
+ }
+ else {
+ pRS->nSrc = SSL_RSSRC_FILE;
+ pRS->cpPath = ap_pstrdup(mc->pPool, ssl_util_server_root_relative(cmd->pool, "random", arg2));
+ }
+ if (pRS->nSrc != SSL_RSSRC_BUILTIN)
+ if (!ssl_util_path_check(SSL_PCM_EXISTS, pRS->cpPath))
+ return ap_pstrcat(cmd->pool, "SSLRandomSeed: source path '",
+ pRS->cpPath, "' not exists", NULL);
+ if (arg3 == NULL)
+ pRS->nBytes = 0; /* read whole file */
+ else {
+ if (pRS->nSrc == SSL_RSSRC_BUILTIN)
+ return "SSLRandomSeed: byte specification not "
+ "allowed for builtin seed source";
+ pRS->nBytes = atoi(arg3);
+ if (pRS->nBytes < 0)
+ return "SSLRandomSeed: invalid number of bytes specified";
+ }
+ return NULL;
+}
+
+const char *ssl_cmd_SSLEngine(
+ cmd_parms *cmd, char *struct_ptr, int flag)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->bEnabled = (flag ? TRUE : FALSE);
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCipherSuite(
+ cmd_parms *cmd, SSLDirConfigRec *dc, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ if (cmd->path == NULL || dc == NULL)
+ sc->szCipherSuite = arg;
+ else
+ dc->szCipherSuite = arg;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCertificateFile(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ char *cpPath;
+ int i;
+
+ cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+ if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+ return ap_pstrcat(cmd->pool, "SSLCertificateFile: file '",
+ cpPath, "' not exists or empty", NULL);
+ for (i = 0; i < SSL_AIDX_MAX && sc->szPublicCertFile[i] != NULL; i++)
+ ;
+ if (i == SSL_AIDX_MAX)
+ return ap_psprintf(cmd->pool, "SSLCertificateFile: only up to %d "
+ "different certificates per virtual host allowed",
+ SSL_AIDX_MAX);
+ sc->szPublicCertFile[i] = cpPath;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCertificateKeyFile(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ char *cpPath;
+ int i;
+
+ cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+ if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+ return ap_pstrcat(cmd->pool, "SSLCertificateKeyFile: file '",
+ cpPath, "' not exists or empty", NULL);
+ for (i = 0; i < SSL_AIDX_MAX && sc->szPrivateKeyFile[i] != NULL; i++)
+ ;
+ if (i == SSL_AIDX_MAX)
+ return ap_psprintf(cmd->pool, "SSLCertificateKeyFile: only up to %d "
+ "different private keys per virtual host allowed",
+ SSL_AIDX_MAX);
+ sc->szPrivateKeyFile[i] = cpPath;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCertificateChainFile(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ char *cpPath;
+
+ cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+ if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+ return ap_pstrcat(cmd->pool, "SSLCertificateChainFile: file '",
+ cpPath, "' not exists or empty", NULL);
+ sc->szCertificateChain = cpPath;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCACertificatePath(
+ cmd_parms *cmd, SSLDirConfigRec *dc, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ char *cpPath;
+
+ cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+ if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath))
+ return ap_pstrcat(cmd->pool, "SSLCACertificatePath: directory '",
+ cpPath, "' not exists", NULL);
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+ if (cmd->path == NULL || dc == NULL)
+ sc->szCACertificatePath = cpPath;
+ else
+ dc->szCACertificatePath = cpPath;
+#else
+ sc->szCACertificatePath = cpPath;
+#endif
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCACertificateFile(
+ cmd_parms *cmd, SSLDirConfigRec *dc, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ char *cpPath;
+
+ cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+ if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+ return ap_pstrcat(cmd->pool, "SSLCACertificateFile: file '",
+ cpPath, "' not exists or empty", NULL);
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+ if (cmd->path == NULL || dc == NULL)
+ sc->szCACertificateFile = cpPath;
+ else
+ dc->szCACertificateFile = cpPath;
+#else
+ sc->szCACertificateFile = cpPath;
+#endif
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCARevocationPath(
+ cmd_parms *cmd, SSLDirConfigRec *dc, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ char *cpPath;
+
+ cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+ if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath))
+ return ap_pstrcat(cmd->pool, "SSLCARecocationPath: directory '",
+ cpPath, "' not exists", NULL);
+ sc->szCARevocationPath = cpPath;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLCARevocationFile(
+ cmd_parms *cmd, SSLDirConfigRec *dc, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ char *cpPath;
+
+ cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+ if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+ return ap_pstrcat(cmd->pool, "SSLCARevocationFile: file '",
+ cpPath, "' not exists or empty", NULL);
+ sc->szCARevocationFile = cpPath;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLVerifyClient(
+ cmd_parms *cmd, SSLDirConfigRec *dc, char *level)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ ssl_verify_t id;
+
+ if (strEQ(level, "0") || strcEQ(level, "none"))
+ id = SSL_CVERIFY_NONE;
+ else if (strEQ(level, "1") || strcEQ(level, "optional"))
+ id = SSL_CVERIFY_OPTIONAL;
+ else if (strEQ(level, "2") || strcEQ(level, "require"))
+ id = SSL_CVERIFY_REQUIRE;
+ else if (strEQ(level, "3") || strcEQ(level, "optional_no_ca"))
+ id = SSL_CVERIFY_OPTIONAL_NO_CA;
+ else
+ return "SSLVerifyClient: Invalid argument";
+ if (cmd->path == NULL || dc == NULL)
+ sc->nVerifyClient = id;
+ else
+ dc->nVerifyClient = id;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLVerifyDepth(
+ cmd_parms *cmd, SSLDirConfigRec *dc, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ int d;
+
+ d = atoi(arg);
+ if (d < 0)
+ return "SSLVerifyDepth: Invalid argument";
+ if (cmd->path == NULL || dc == NULL)
+ sc->nVerifyDepth = d;
+ else
+ dc->nVerifyDepth = d;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLSessionCache(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ const char *err;
+ SSLModConfigRec *mc = myModConfig();
+ char *cp, *cp2;
+ int maxsize;
+
+ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL)
+ return err;
+ if (ssl_config_global_isfixed())
+ return NULL;
+ if (strcEQ(arg, "none")) {
+ mc->nSessionCacheMode = SSL_SCMODE_NONE;
+ mc->szSessionCacheDataFile = NULL;
+ }
+ else if (strlen(arg) > 4 && strcEQn(arg, "dbm:", 4)) {
+ mc->nSessionCacheMode = SSL_SCMODE_DBM;
+ mc->szSessionCacheDataFile = ap_pstrdup(mc->pPool,
+ ssl_util_server_root_relative(cmd->pool, "scache", arg+4));
+ }
+ else if ( (strlen(arg) > 4 && strcEQn(arg, "shm:", 4))
+ || (strlen(arg) > 6 && strcEQn(arg, "shmht:", 6))) {
+ if (!ap_mm_useable())
+ return "SSLSessionCache: shared memory cache not useable on this platform";
+ mc->nSessionCacheMode = SSL_SCMODE_SHMHT;
+ cp = strchr(arg, ':');
+ mc->szSessionCacheDataFile = ap_pstrdup(mc->pPool,
+ ssl_util_server_root_relative(cmd->pool, "scache", cp+1));
+ mc->tSessionCacheDataTable = NULL;
+ mc->nSessionCacheDataSize = 1024*512; /* 512KB */
+ if ((cp = strchr(mc->szSessionCacheDataFile, '(')) != NULL) {
+ *cp++ = NUL;
+ if ((cp2 = strchr(cp, ')')) == NULL)
+ return "SSLSessionCache: Invalid argument: no closing parenthesis";
+ *cp2 = NUL;
+ mc->nSessionCacheDataSize = atoi(cp);
+ if (mc->nSessionCacheDataSize <= 8192)
+ return "SSLSessionCache: Invalid argument: size has to be >= 8192 bytes";
+ maxsize = ap_mm_core_maxsegsize();
+ if (mc->nSessionCacheDataSize >= maxsize)
+ return ap_psprintf(cmd->pool, "SSLSessionCache: Invalid argument: "
+ "size has to be < %d bytes on this platform", maxsize);
+ }
+ }
+ else if (strlen(arg) > 6 && strcEQn(arg, "shmcb:", 6)) {
+ if (!ap_mm_useable())
+ return "SSLSessionCache: shared memory cache not useable on this platform";
+ mc->nSessionCacheMode = SSL_SCMODE_SHMCB;
+ mc->szSessionCacheDataFile = ap_pstrdup(mc->pPool,
+ ap_server_root_relative(cmd->pool, arg+6));
+ mc->tSessionCacheDataTable = NULL;
+ mc->nSessionCacheDataSize = 1024*512; /* 512KB */
+ if ((cp = strchr(mc->szSessionCacheDataFile, '(')) != NULL) {
+ *cp++ = NUL;
+ if ((cp2 = strchr(cp, ')')) == NULL)
+ return "SSLSessionCache: Invalid argument: no closing parenthesis";
+ *cp2 = NUL;
+ mc->nSessionCacheDataSize = atoi(cp);
+ if (mc->nSessionCacheDataSize <= 8192)
+ return "SSLSessionCache: Invalid argument: size has to be >= 8192 bytes";
+ maxsize = ap_mm_core_maxsegsize();
+ if (mc->nSessionCacheDataSize >= maxsize)
+ return ap_psprintf(cmd->pool, "SSLSessionCache: Invalid argument: "
+ "size has to be < %d bytes on this platform", maxsize);
+ }
+ }
+ else
+#ifdef SSL_VENDOR
+ if (!ap_hook_use("ap::mod_ssl::vendor::cmd_sslsessioncache",
+ AP_HOOK_SIG4(void,ptr,ptr,ptr), AP_HOOK_MODE_ALL,
+ cmd, arg, mc))
+#endif
+ return "SSLSessionCache: Invalid argument";
+ return NULL;
+}
+
+const char *ssl_cmd_SSLSessionCacheTimeout(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->nSessionCacheTimeout = atoi(arg);
+ if (sc->nSessionCacheTimeout < 0)
+ return "SSLSessionCacheTimeout: Invalid argument";
+ return NULL;
+}
+
+const char *ssl_cmd_SSLLog(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_DIRECTORY
+ |NOT_IN_LOCATION|NOT_IN_FILES )) != NULL)
+ return err;
+ sc->szLogFile = arg;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLLogLevel(
+ cmd_parms *cmd, char *struct_ptr, char *level)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+ if ((err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_DIRECTORY
+ |NOT_IN_LOCATION|NOT_IN_FILES )) != NULL)
+ return err;
+ if (strcEQ(level, "none"))
+ sc->nLogLevel = SSL_LOG_NONE;
+ else if (strcEQ(level, "error"))
+ sc->nLogLevel = SSL_LOG_ERROR;
+ else if (strcEQ(level, "warn"))
+ sc->nLogLevel = SSL_LOG_WARN;
+ else if (strcEQ(level, "info"))
+ sc->nLogLevel = SSL_LOG_INFO;
+ else if (strcEQ(level, "trace"))
+ sc->nLogLevel = SSL_LOG_TRACE;
+ else if (strcEQ(level, "debug"))
+ sc->nLogLevel = SSL_LOG_DEBUG;
+ else
+ return "SSLLogLevel: Invalid argument";
+ return NULL;
+}
+
+const char *ssl_cmd_SSLOptions(
+ cmd_parms *cmd, SSLDirConfigRec *dc, const char *cpLine)
+{
+ ssl_opt_t opt;
+ int first;
+ char action;
+ char *w;
+
+ first = TRUE;
+ while (cpLine[0] != NUL) {
+ w = ap_getword_conf(cmd->pool, &cpLine);
+ action = NUL;
+
+ if (*w == '+' || *w == '-') {
+ action = *(w++);
+ }
+ else if (first) {
+ dc->nOptions = SSL_OPT_NONE;
+ first = FALSE;
+ }
+
+ if (strcEQ(w, "StdEnvVars"))
+ opt = SSL_OPT_STDENVVARS;
+ else if (strcEQ(w, "CompatEnvVars"))
+ opt = SSL_OPT_COMPATENVVARS;
+ else if (strcEQ(w, "ExportCertData"))
+ opt = SSL_OPT_EXPORTCERTDATA;
+ else if (strcEQ(w, "FakeBasicAuth"))
+ opt = SSL_OPT_FAKEBASICAUTH;
+ else if (strcEQ(w, "StrictRequire"))
+ opt = SSL_OPT_STRICTREQUIRE;
+ else if (strcEQ(w, "OptRenegotiate"))
+ opt = SSL_OPT_OPTRENEGOTIATE;
+ else
+ return ap_pstrcat(cmd->pool, "SSLOptions: Illegal option '", w, "'", NULL);
+
+ if (action == '-') {
+ dc->nOptionsAdd &= ~opt;
+ dc->nOptionsDel |= opt;
+ dc->nOptions &= ~opt;
+ }
+ else if (action == '+') {
+ dc->nOptionsAdd |= opt;
+ dc->nOptionsDel &= ~opt;
+ dc->nOptions |= opt;
+ }
+ else {
+ dc->nOptions = opt;
+ dc->nOptionsAdd = opt;
+ dc->nOptionsDel = SSL_OPT_NONE;
+ }
+ }
+ return NULL;
+}
+
+const char *ssl_cmd_SSLRequireSSL(
+ cmd_parms *cmd, SSLDirConfigRec *dc, char *cipher)
+{
+ dc->bSSLRequired = TRUE;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLRequire(
+ cmd_parms *cmd, SSLDirConfigRec *dc, char *cpExpr)
+{
+ ssl_expr *mpExpr;
+ ssl_require_t *pReqRec;
+
+ if ((mpExpr = ssl_expr_comp(cmd->pool, cpExpr)) == NULL)
+ return ap_pstrcat(cmd->pool, "SSLRequire: ", ssl_expr_get_error(), NULL);
+ pReqRec = ap_push_array(dc->aRequirement);
+ pReqRec->cpExpr = ap_pstrdup(cmd->pool, cpExpr);
+ pReqRec->mpExpr = mpExpr;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProtocol(
+ cmd_parms *cmd, char *struct_ptr, const char *opt)
+{
+ SSLSrvConfigRec *sc;
+ ssl_proto_t options, thisopt;
+ char action;
+ char *w;
+
+ sc = mySrvConfig(cmd->server);
+ options = SSL_PROTOCOL_NONE;
+ while (opt[0] != NUL) {
+ w = ap_getword_conf(cmd->pool, &opt);
+
+ action = NUL;
+ if (*w == '+' || *w == '-')
+ action = *(w++);
+
+ if (strcEQ(w, "SSLv2"))
+ thisopt = SSL_PROTOCOL_SSLV2;
+ else if (strcEQ(w, "SSLv3"))
+ thisopt = SSL_PROTOCOL_SSLV3;
+ else if (strcEQ(w, "TLSv1"))
+ thisopt = SSL_PROTOCOL_TLSV1;
+ else if (strcEQ(w, "all"))
+ thisopt = SSL_PROTOCOL_ALL;
+ else
+ return ap_pstrcat(cmd->pool, "SSLProtocol: Illegal protocol '", w, "'", NULL);
+
+ if (action == '-')
+ options &= ~thisopt;
+ else if (action == '+')
+ options |= thisopt;
+ else
+ options = thisopt;
+ }
+ sc->nProtocol = options;
+ return NULL;
+}
+
+#ifdef SSL_EXPERIMENTAL_PROXY
+
+const char *ssl_cmd_SSLProxyProtocol(
+ cmd_parms *cmd, char *struct_ptr, const char *opt)
+{
+ SSLSrvConfigRec *sc;
+ ssl_proto_t options, thisopt;
+ char action;
+ char *w;
+
+ sc = mySrvConfig(cmd->server);
+ options = SSL_PROTOCOL_NONE;
+ while (opt[0] != NUL) {
+ w = ap_getword_conf(cmd->pool, &opt);
+
+ action = NUL;
+ if (*w == '+' || *w == '-')
+ action = *(w++);
+
+ if (strcEQ(w, "SSLv2"))
+ thisopt = SSL_PROTOCOL_SSLV2;
+ else if (strcEQ(w, "SSLv3"))
+ thisopt = SSL_PROTOCOL_SSLV3;
+ else if (strcEQ(w, "TLSv1"))
+ thisopt = SSL_PROTOCOL_TLSV1;
+ else if (strcEQ(w, "all"))
+ thisopt = SSL_PROTOCOL_ALL;
+ else
+ return ap_pstrcat(cmd->pool, "SSLProxyProtocol: "
+ "Illegal protocol '", w, "'", NULL);
+ if (action == '-')
+ options &= ~thisopt;
+ else if (action == '+')
+ options |= thisopt;
+ else
+ options = thisopt;
+ }
+ sc->nProxyProtocol = options;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCipherSuite(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->szProxyCipherSuite = arg;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyVerify(
+ cmd_parms *cmd, char *struct_ptr, int flag)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->bProxyVerify = (flag ? TRUE : FALSE);
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyVerifyDepth(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ int d;
+
+ d = atoi(arg);
+ if (d < 0)
+ return "SSLProxyVerifyDepth: Invalid argument";
+ sc->nProxyVerifyDepth = d;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCACertificateFile(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ char *cpPath;
+
+ cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+ if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+ return ap_pstrcat(cmd->pool, "SSLProxyCACertificateFile: file '",
+ cpPath, "' not exists or empty", NULL);
+ sc->szProxyCACertificateFile = cpPath;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyCACertificatePath(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ char *cpPath;
+
+ cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+ if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath))
+ return ap_pstrcat(cmd->pool, "SSLProxyCACertificatePath: directory '",
+ cpPath, "' does not exists", NULL);
+ sc->szProxyCACertificatePath = cpPath;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyMachineCertificateFile(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ char *cpPath;
+
+ cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+ if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath))
+ return ap_pstrcat(cmd->pool, "SSLProxyMachineCertFile: file '",
+ cpPath, "' not exists or empty", NULL);
+ sc->szProxyClientCertificateFile = cpPath;
+ return NULL;
+}
+
+const char *ssl_cmd_SSLProxyMachineCertificatePath(
+ cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ char *cpPath;
+
+ cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg);
+ if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath))
+ return ap_pstrcat(cmd->pool, "SSLProxyMachineCertPath: directory '",
+ cpPath, "' does not exists", NULL);
+ sc->szProxyClientCertificatePath = cpPath;
+ return NULL;
+}
+
+#endif /* SSL_EXPERIMENTAL_PROXY */
+
diff --git a/modules/ssl/ssl_engine_dh.c b/modules/ssl/ssl_engine_dh.c
new file mode 100644
index 0000000000..84f49e6657
--- /dev/null
+++ b/modules/ssl/ssl_engine_dh.c
@@ -0,0 +1,255 @@
+#if 0
+=pod
+#endif
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_engine_dh.c
+** Diffie-Hellman Built-in Temporary Parameters
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include "mod_ssl.h"
+
+/* ----BEGIN GENERATED SECTION-------- */
+
+/*
+** Diffie-Hellman-Parameters: (512 bit)
+** prime:
+** 00:d4:bc:d5:24:06:f6:9b:35:99:4b:88:de:5d:b8:
+** 96:82:c8:15:7f:62:d8:f3:36:33:ee:57:72:f1:1f:
+** 05:ab:22:d6:b5:14:5b:9f:24:1e:5a:cc:31:ff:09:
+** 0a:4b:c7:11:48:97:6f:76:79:50:94:e7:1e:79:03:
+** 52:9f:5a:82:4b
+** generator: 2 (0x2)
+** Diffie-Hellman-Parameters: (1024 bit)
+** prime:
+** 00:e6:96:9d:3d:49:5b:e3:2c:7c:f1:80:c3:bd:d4:
+** 79:8e:91:b7:81:82:51:bb:05:5e:2a:20:64:90:4a:
+** 79:a7:70:fa:15:a2:59:cb:d5:23:a6:a6:ef:09:c4:
+** 30:48:d5:a2:2f:97:1f:3c:20:12:9b:48:00:0e:6e:
+** dd:06:1c:bc:05:3e:37:1d:79:4e:53:27:df:61:1e:
+** bb:be:1b:ac:9b:5c:60:44:cf:02:3d:76:e0:5e:ea:
+** 9b:ad:99:1b:13:a6:3c:97:4e:9e:f1:83:9e:b5:db:
+** 12:51:36:f7:26:2e:56:a8:87:15:38:df:d8:23:c6:
+** 50:50:85:e2:1f:0d:d5:c8:6b
+** generator: 2 (0x2)
+*/
+
+static unsigned char dh512_p[] =
+{
+ 0xD4, 0xBC, 0xD5, 0x24, 0x06, 0xF6, 0x9B, 0x35, 0x99, 0x4B, 0x88, 0xDE,
+ 0x5D, 0xB8, 0x96, 0x82, 0xC8, 0x15, 0x7F, 0x62, 0xD8, 0xF3, 0x36, 0x33,
+ 0xEE, 0x57, 0x72, 0xF1, 0x1F, 0x05, 0xAB, 0x22, 0xD6, 0xB5, 0x14, 0x5B,
+ 0x9F, 0x24, 0x1E, 0x5A, 0xCC, 0x31, 0xFF, 0x09, 0x0A, 0x4B, 0xC7, 0x11,
+ 0x48, 0x97, 0x6F, 0x76, 0x79, 0x50, 0x94, 0xE7, 0x1E, 0x79, 0x03, 0x52,
+ 0x9F, 0x5A, 0x82, 0x4B,
+};
+static unsigned char dh512_g[] =
+{
+ 0x02,
+};
+
+static DH *get_dh512()
+{
+ DH *dh;
+
+ if ((dh = DH_new()) == NULL)
+ return (NULL);
+ dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL);
+ dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL);
+ if ((dh->p == NULL) || (dh->g == NULL))
+ return (NULL);
+ return (dh);
+}
+static unsigned char dh1024_p[] =
+{
+ 0xE6, 0x96, 0x9D, 0x3D, 0x49, 0x5B, 0xE3, 0x2C, 0x7C, 0xF1, 0x80, 0xC3,
+ 0xBD, 0xD4, 0x79, 0x8E, 0x91, 0xB7, 0x81, 0x82, 0x51, 0xBB, 0x05, 0x5E,
+ 0x2A, 0x20, 0x64, 0x90, 0x4A, 0x79, 0xA7, 0x70, 0xFA, 0x15, 0xA2, 0x59,
+ 0xCB, 0xD5, 0x23, 0xA6, 0xA6, 0xEF, 0x09, 0xC4, 0x30, 0x48, 0xD5, 0xA2,
+ 0x2F, 0x97, 0x1F, 0x3C, 0x20, 0x12, 0x9B, 0x48, 0x00, 0x0E, 0x6E, 0xDD,
+ 0x06, 0x1C, 0xBC, 0x05, 0x3E, 0x37, 0x1D, 0x79, 0x4E, 0x53, 0x27, 0xDF,
+ 0x61, 0x1E, 0xBB, 0xBE, 0x1B, 0xAC, 0x9B, 0x5C, 0x60, 0x44, 0xCF, 0x02,
+ 0x3D, 0x76, 0xE0, 0x5E, 0xEA, 0x9B, 0xAD, 0x99, 0x1B, 0x13, 0xA6, 0x3C,
+ 0x97, 0x4E, 0x9E, 0xF1, 0x83, 0x9E, 0xB5, 0xDB, 0x12, 0x51, 0x36, 0xF7,
+ 0x26, 0x2E, 0x56, 0xA8, 0x87, 0x15, 0x38, 0xDF, 0xD8, 0x23, 0xC6, 0x50,
+ 0x50, 0x85, 0xE2, 0x1F, 0x0D, 0xD5, 0xC8, 0x6B,
+};
+static unsigned char dh1024_g[] =
+{
+ 0x02,
+};
+
+static DH *get_dh1024()
+{
+ DH *dh;
+
+ if ((dh = DH_new()) == NULL)
+ return (NULL);
+ dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
+ dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
+ if ((dh->p == NULL) || (dh->g == NULL))
+ return (NULL);
+ return (dh);
+}
+/* ----END GENERATED SECTION---------- */
+
+DH *ssl_dh_GetTmpParam(int nKeyLen)
+{
+ DH *dh;
+
+ if (nKeyLen == 512)
+ dh = get_dh512();
+ else if (nKeyLen == 1024)
+ dh = get_dh1024();
+ else
+ dh = get_dh1024();
+ return dh;
+}
+
+DH *ssl_dh_GetParamFromFile(char *file)
+{
+ DH *dh = NULL;
+ BIO *bio;
+
+ if ((bio = BIO_new_file(file, "r")) == NULL)
+ return NULL;
+#if SSL_LIBRARY_VERSION < 0x00904000
+ dh = PEM_read_bio_DHparams(bio, NULL, NULL);
+#else
+ dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+#endif
+ BIO_free(bio);
+ return (dh);
+}
+
+/*
+=cut
+##
+## Embedded Perl script for generating the temporary DH parameters
+##
+
+require 5.003;
+use strict;
+
+# configuration
+my $file = $0;
+my $begin = '----BEGIN GENERATED SECTION--------';
+my $end = '----END GENERATED SECTION----------';
+
+# read ourself and keep a backup
+open(FP, "<$file") || die;
+my $source = '';
+$source .= $_ while (<FP>);
+close(FP);
+open(FP, ">$file.bak") || die;
+print FP $source;
+close(FP);
+
+# generate the DH parameters
+print "1. Generate 512 and 1024 bit Diffie-Hellman parameters (p, g)\n";
+my $rand = '';
+foreach $file (qw(/var/log/messages /var/adm/messages
+ /kernel /vmunix /vmlinuz /etc/hosts /etc/resolv.conf)) {
+ if (-f $file) {
+ $rand = $file if ($rand eq '');
+ $rand .= ":$file" if ($rand ne '');
+ }
+}
+$rand = "-rand $rand" if ($rand ne '');
+system("openssl gendh $rand -out dh512.pem 512");
+system("openssl gendh $rand -out dh1024.pem 1024");
+
+# generate DH param info
+my $dhinfo = '';
+open(FP, "openssl dh -noout -text -in dh512.pem |") || die;
+$dhinfo .= $_ while (<FP>);
+close(FP);
+open(FP, "openssl dh -noout -text -in dh1024.pem |") || die;
+$dhinfo .= $_ while (<FP>);
+close(FP);
+$dhinfo =~ s|^|** |mg;
+$dhinfo = "\n\/\*\n$dhinfo\*\/\n\n";
+
+# generate C source from DH params
+my $dhsource = '';
+open(FP, "openssl dh -noout -C -in dh512.pem | indent | expand -8 |") || die;
+$dhsource .= $_ while (<FP>);
+close(FP);
+open(FP, "openssl dh -noout -C -in dh1024.pem | indent | expand -8 |") || die;
+$dhsource .= $_ while (<FP>);
+close(FP);
+$dhsource =~ s|(DH\s+\*get_dh)|static $1|sg;
+
+# generate output
+my $o = $dhinfo . $dhsource;
+
+# insert the generated code at the target location
+$source =~ s|(\/\* $begin.+?\n).*\n(.*?\/\* $end)|$1$o$2|s;
+
+# and update the source on disk
+print "Updating file `$file'\n";
+open(FP, ">$file") || die;
+print FP $source;
+close(FP);
+
+# cleanup
+unlink("dh512.pem");
+unlink("dh1024.pem");
+
+=pod
+*/
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
new file mode 100644
index 0000000000..6a27c4af4d
--- /dev/null
+++ b/modules/ssl/ssl_engine_init.c
@@ -0,0 +1,1090 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_engine_init.c
+** Initialization of Servers
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/* ====================================================================
+ * Copyright (c) 1995-1999 Ben Laurie. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by Ben Laurie
+ * for use in the Apache-SSL HTTP server project."
+ *
+ * 4. The name "Apache-SSL Server" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Ben Laurie
+ * for use in the Apache-SSL HTTP server project."
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN LAURIE ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BEN LAURIE OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``Recursive, adj.;
+ see Recursive.''
+ -- Unknown */
+#include "mod_ssl.h"
+
+
+/* _________________________________________________________________
+**
+** Module Initialization
+** _________________________________________________________________
+*/
+
+/*
+ * Per-module initialization
+ */
+void ssl_init_Module(server_rec *s, pool *p)
+{
+ SSLModConfigRec *mc = myModConfig();
+ SSLSrvConfigRec *sc;
+ server_rec *s2;
+ char *cp;
+
+ mc->nInitCount++;
+
+ /*
+ * Let us cleanup on restarts and exists
+ */
+ ap_register_cleanup(p, s, ssl_init_ModuleKill, ssl_init_ChildKill);
+
+ /*
+ * Any init round fixes the global config
+ */
+ ssl_config_global_create(); /* just to avoid problems */
+ ssl_config_global_fix();
+
+ /*
+ * try to fix the configuration and open the dedicated SSL
+ * logfile as early as possible
+ */
+ for (s2 = s; s2 != NULL; s2 = s2->next) {
+ sc = mySrvConfig(s2);
+
+ /* Fix up stuff that may not have been set */
+ if (sc->bEnabled == UNSET)
+ sc->bEnabled = FALSE;
+ if (sc->nVerifyClient == SSL_CVERIFY_UNSET)
+ sc->nVerifyClient = SSL_CVERIFY_NONE;
+ if (sc->nVerifyDepth == UNSET)
+ sc->nVerifyDepth = 1;
+#ifdef SSL_EXPERIMENTAL_PROXY
+ if (sc->nProxyVerifyDepth == UNSET)
+ sc->nProxyVerifyDepth = 1;
+#endif
+ if (sc->nSessionCacheTimeout == UNSET)
+ sc->nSessionCacheTimeout = SSL_SESSION_CACHE_TIMEOUT;
+ if (sc->nPassPhraseDialogType == SSL_PPTYPE_UNSET)
+ sc->nPassPhraseDialogType = SSL_PPTYPE_BUILTIN;
+
+ /* Open the dedicated SSL logfile */
+ ssl_log_open(s, s2, p);
+ }
+
+ /*
+ * Identification
+ */
+ if (mc->nInitCount == 1) {
+ ssl_log(s, SSL_LOG_INFO, "Server: %s, Interface: %s, Library: %s",
+ SERVER_BASEVERSION,
+ ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_INTERFACE"),
+ ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_LIBRARY"));
+#ifdef WIN32
+ ssl_log(s, SSL_LOG_WARN, "You are using mod_ssl under Win32. "
+ "This combination is *NOT* officially supported. "
+ "Use it at your own risk!");
+#endif
+ }
+
+ /*
+ * Initialization round information
+ */
+ if (mc->nInitCount == 1)
+ ssl_log(s, SSL_LOG_INFO, "Init: 1st startup round (still not detached)");
+ else if (mc->nInitCount == 2)
+ ssl_log(s, SSL_LOG_INFO, "Init: 2nd startup round (already detached)");
+ else
+ ssl_log(s, SSL_LOG_INFO, "Init: %d%s restart round (already detached)",
+ mc->nInitCount-2, (mc->nInitCount-2) == 1 ? "st" : "nd");
+
+#ifdef SSL_VENDOR
+ ap_hook_use("ap::mod_ssl::vendor::init_module",
+ AP_HOOK_SIG3(void,ptr,ptr), AP_HOOK_ALL, s, p);
+#endif
+
+ /*
+ * The initialization phase inside the Apache API is totally bogus.
+ * We actually have three non-trivial problems:
+ *
+ * 1. Under Unix the API does a 2-round initialization of modules while
+ * under Win32 it doesn't. This means we have to make sure that at
+ * least the pass phrase dialog doesn't occur twice. We overcome this
+ * problem by using a counter (mc->nInitCount) which has to
+ * survive the init rounds.
+ *
+ * 2. Between the first and the second round Apache detaches from
+ * the terminal under Unix. This means that our pass phrase dialog
+ * _has_ to be done in the first round and _cannot_ be done in the
+ * second round.
+ *
+ * 3. When Dynamic Shared Object (DSO) mechanism is used under Unix the
+ * module segment (code & data) gets unloaded and re-loaded between
+ * the first and the second round. This means no global data survives
+ * between first and the second init round. We overcome this by using
+ * an entry ("ssl_module") inside the ap_global_ctx.
+ *
+ * The situation as a table:
+ *
+ * Unix/static Unix/DSO Win32 Action Required
+ * (-DSHARED_MODULE) (-DWIN32)
+ * ----------- ----------------- --------- -----------------------------------
+ * - load module - -
+ * init init init SSL library init, Pass Phrase Dialog
+ * detach detach - -
+ * - reload module - -
+ * init init - SSL library init, mod_ssl init
+ *
+ * Ok, now try to solve this totally ugly situation...
+ */
+
+#ifdef SHARED_MODULE
+ ssl_log(s, SSL_LOG_INFO, "Init: %snitializing %s library",
+ mc->nInitCount == 1 ? "I" : "Rei", SSL_LIBRARY_NAME);
+ ssl_init_SSLLibrary();
+#else
+ if (mc->nInitCount <= 2) {
+ ssl_log(s, SSL_LOG_INFO, "Init: %snitializing %s library",
+ mc->nInitCount == 1 ? "I" : "Rei", SSL_LIBRARY_NAME);
+ ssl_init_SSLLibrary();
+ }
+#endif
+ if (mc->nInitCount == 1) {
+ ssl_pphrase_Handle(s, p);
+ ssl_init_TmpKeysHandle(SSL_TKP_GEN, s, p);
+#ifndef WIN32
+ return;
+#endif
+ }
+
+ /*
+ * SSL external crypto device ("engine") support
+ */
+#ifdef SSL_EXPERIMENTAL_ENGINE
+ ssl_init_Engine(s, p);
+#endif
+
+ /*
+ * Warn the user that he should use the session cache.
+ * But we can operate without it, of course.
+ */
+ if (mc->nSessionCacheMode == SSL_SCMODE_UNSET) {
+ ssl_log(s, SSL_LOG_WARN,
+ "Init: Session Cache is not configured [hint: SSLSessionCache]");
+ mc->nSessionCacheMode = SSL_SCMODE_NONE;
+ }
+
+ /*
+ * initialize the mutex handling and session caching
+ */
+ ssl_mutex_init(s, p);
+ ssl_scache_init(s, p);
+
+ /*
+ * Seed the Pseudo Random Number Generator (PRNG)
+ */
+ ssl_rand_seed(s, p, SSL_RSCTX_STARTUP, "Init: ");
+
+ /*
+ * allocate the temporary RSA keys and DH params
+ */
+ ssl_init_TmpKeysHandle(SSL_TKP_ALLOC, s, p);
+
+ /*
+ * initialize servers
+ */
+ ssl_log(s, SSL_LOG_INFO, "Init: Initializing (virtual) servers for SSL");
+ for (s2 = s; s2 != NULL; s2 = s2->next) {
+ sc = mySrvConfig(s2);
+ /*
+ * Either now skip this server when SSL is disabled for
+ * it or give out some information about what we're
+ * configuring.
+ */
+ if (!sc->bEnabled)
+ continue;
+ ssl_log(s2, SSL_LOG_INFO,
+ "Init: Configuring server %s for SSL protocol",
+ ssl_util_vhostid(p, s2));
+
+ /*
+ * Read the server certificate and key
+ */
+ ssl_init_ConfigureServer(s2, p, sc);
+ }
+
+ /*
+ * Configuration consistency checks
+ */
+ ssl_init_CheckServers(s, p);
+
+ /*
+ * Announce mod_ssl and SSL library in HTTP Server field
+ * as ``mod_ssl/X.X.X OpenSSL/X.X.X''
+ */
+ if ((cp = ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_PRODUCT")) != NULL && cp[0] != NUL)
+ ap_add_version_component(cp);
+ ap_add_version_component(ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_INTERFACE"));
+ ap_add_version_component(ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_LIBRARY"));
+
+ return;
+}
+
+/*
+ * Initialize SSL library (also already needed for the pass phrase dialog)
+ */
+void ssl_init_SSLLibrary(void)
+{
+#ifdef WIN32
+ CRYPTO_malloc_init();
+#endif
+ SSL_load_error_strings();
+ SSL_library_init();
+ ssl_util_thread_setup();
+ X509V3_add_standard_extensions();
+ return;
+}
+
+/*
+ * Support for external a Crypto Device ("engine"), usually
+ * a hardware accellerator card for crypto operations.
+ */
+#ifdef SSL_EXPERIMENTAL_ENGINE
+void ssl_init_Engine(server_rec *s, pool *p)
+{
+ SSLModConfigRec *mc = myModConfig();
+ ENGINE *e;
+
+ if (mc->szCryptoDevice != NULL) {
+ if ((e = ENGINE_by_id(mc->szCryptoDevice)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load Crypto Device API `%s'",
+ mc->szCryptoDevice);
+ ssl_die();
+ }
+ if (strEQ(mc->szCryptoDevice, "chil"))
+ ENGINE_ctrl(e, ENGINE_CTRL_CHIL_SET_FORKCHECK, 1, 0, 0);
+ if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
+ ssl_log(s, SSL_LOG_ERROR, "Init: Failed to enable Crypto Device API `%s'",
+ mc->szCryptoDevice);
+ ssl_die();
+ }
+ ENGINE_free(e);
+ }
+ return;
+}
+#endif
+
+/*
+ * Handle the Temporary RSA Keys and DH Params
+ */
+void ssl_init_TmpKeysHandle(int action, server_rec *s, pool *p)
+{
+ SSLModConfigRec *mc = myModConfig();
+ ssl_asn1_t *asn1;
+ unsigned char *ucp;
+ RSA *rsa;
+ DH *dh;
+
+ /* Generate Keys and Params */
+ if (action == SSL_TKP_GEN) {
+
+ /* seed PRNG */
+ ssl_rand_seed(s, p, SSL_RSCTX_STARTUP, "Init: ");
+
+ /* generate 512 bit RSA key */
+ ssl_log(s, SSL_LOG_INFO, "Init: Generating temporary RSA private keys (512/1024 bits)");
+ if ((rsa = RSA_generate_key(512, RSA_F4, NULL, NULL)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: Failed to generate temporary 512 bit RSA private key");
+ ssl_die();
+ }
+ asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tTmpKeys, "RSA:512");
+ asn1->nData = i2d_RSAPrivateKey(rsa, NULL);
+ asn1->cpData = ap_palloc(mc->pPool, asn1->nData);
+ ucp = asn1->cpData; i2d_RSAPrivateKey(rsa, &ucp); /* 2nd arg increments */
+ RSA_free(rsa);
+
+ /* generate 1024 bit RSA key */
+ if ((rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: Failed to generate temporary 1024 bit RSA private key");
+ ssl_die();
+ }
+ asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tTmpKeys, "RSA:1024");
+ asn1->nData = i2d_RSAPrivateKey(rsa, NULL);
+ asn1->cpData = ap_palloc(mc->pPool, asn1->nData);
+ ucp = asn1->cpData; i2d_RSAPrivateKey(rsa, &ucp); /* 2nd arg increments */
+ RSA_free(rsa);
+
+ ssl_log(s, SSL_LOG_INFO, "Init: Configuring temporary DH parameters (512/1024 bits)");
+
+ /* import 512 bit DH param */
+ if ((dh = ssl_dh_GetTmpParam(512)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR, "Init: Failed to import temporary 512 bit DH parameters");
+ ssl_die();
+ }
+ asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tTmpKeys, "DH:512");
+ asn1->nData = i2d_DHparams(dh, NULL);
+ asn1->cpData = ap_palloc(mc->pPool, asn1->nData);
+ ucp = asn1->cpData; i2d_DHparams(dh, &ucp); /* 2nd arg increments */
+ /* no need to free dh, it's static */
+
+ /* import 1024 bit DH param */
+ if ((dh = ssl_dh_GetTmpParam(1024)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR, "Init: Failed to import temporary 1024 bit DH parameters");
+ ssl_die();
+ }
+ asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tTmpKeys, "DH:1024");
+ asn1->nData = i2d_DHparams(dh, NULL);
+ asn1->cpData = ap_palloc(mc->pPool, asn1->nData);
+ ucp = asn1->cpData; i2d_DHparams(dh, &ucp); /* 2nd arg increments */
+ /* no need to free dh, it's static */
+ }
+
+ /* Allocate Keys and Params */
+ else if (action == SSL_TKP_ALLOC) {
+
+ ssl_log(s, SSL_LOG_INFO, "Init: Configuring temporary RSA private keys (512/1024 bits)");
+
+ /* allocate 512 bit RSA key */
+ if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tTmpKeys, "RSA:512")) != NULL) {
+ ucp = asn1->cpData;
+ if ((mc->pTmpKeys[SSL_TKPIDX_RSA512] =
+#if SSL_LIBRARY_VERSION >= 0x00907000
+ (void *)d2i_RSAPrivateKey(NULL, (const unsigned char **)&ucp, asn1->nData)) == NULL) {
+#else
+ (void *)d2i_RSAPrivateKey(NULL, &ucp, asn1->nData)) == NULL) {
+#endif
+ ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load temporary 512 bit RSA private key");
+ ssl_die();
+ }
+ }
+
+ /* allocate 1024 bit RSA key */
+ if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tTmpKeys, "RSA:1024")) != NULL) {
+ ucp = asn1->cpData;
+ if ((mc->pTmpKeys[SSL_TKPIDX_RSA1024] =
+#if SSL_LIBRARY_VERSION >= 0x00907000
+ (void *)d2i_RSAPrivateKey(NULL, (const unsigned char **)&ucp, asn1->nData)) == NULL) {
+#else
+ (void *)d2i_RSAPrivateKey(NULL, &ucp, asn1->nData)) == NULL) {
+#endif
+ ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load temporary 1024 bit RSA private key");
+ ssl_die();
+ }
+ }
+
+ ssl_log(s, SSL_LOG_INFO, "Init: Configuring temporary DH parameters (512/1024 bits)");
+
+ /* allocate 512 bit DH param */
+ if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tTmpKeys, "DH:512")) != NULL) {
+ ucp = asn1->cpData;
+ if ((mc->pTmpKeys[SSL_TKPIDX_DH512] =
+#if SSL_LIBRARY_VERSION >= 0x00907000
+ (void *)d2i_DHparams(NULL, (const unsigned char **)&ucp, asn1->nData)) == NULL) {
+#else
+ (void *)d2i_DHparams(NULL, &ucp, asn1->nData)) == NULL) {
+#endif
+ ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load temporary 512 bit DH parameters");
+ ssl_die();
+ }
+ }
+
+ /* allocate 1024 bit DH param */
+ if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tTmpKeys, "DH:1024")) != NULL) {
+ ucp = asn1->cpData;
+ if ((mc->pTmpKeys[SSL_TKPIDX_DH1024] =
+#if SSL_LIBRARY_VERSION >= 0x00907000
+ (void *)d2i_DHparams(NULL, (const unsigned char **)&ucp, asn1->nData)) == NULL) {
+#else
+ (void *)d2i_DHparams(NULL, &ucp, asn1->nData)) == NULL) {
+#endif
+ ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load temporary 1024 bit DH parameters");
+ ssl_die();
+ }
+ }
+ }
+
+ /* Free Keys and Params */
+ else if (action == SSL_TKP_FREE) {
+ if (mc->pTmpKeys[SSL_TKPIDX_RSA512] != NULL) {
+ RSA_free((RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA512]);
+ mc->pTmpKeys[SSL_TKPIDX_RSA512] = NULL;
+ }
+ if (mc->pTmpKeys[SSL_TKPIDX_RSA1024] != NULL) {
+ RSA_free((RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA1024]);
+ mc->pTmpKeys[SSL_TKPIDX_RSA1024] = NULL;
+ }
+ if (mc->pTmpKeys[SSL_TKPIDX_DH512] != NULL) {
+ DH_free((DH *)mc->pTmpKeys[SSL_TKPIDX_DH512]);
+ mc->pTmpKeys[SSL_TKPIDX_DH512] = NULL;
+ }
+ if (mc->pTmpKeys[SSL_TKPIDX_DH1024] != NULL) {
+ DH_free((DH *)mc->pTmpKeys[SSL_TKPIDX_DH1024]);
+ mc->pTmpKeys[SSL_TKPIDX_DH1024] = NULL;
+ }
+ }
+ return;
+}
+
+/*
+ * Configure a particular server
+ */
+void ssl_init_ConfigureServer(server_rec *s, pool *p, SSLSrvConfigRec *sc)
+{
+ SSLModConfigRec *mc = myModConfig();
+ int nVerify;
+ char *cpVHostID;
+ EVP_PKEY *pKey;
+ SSL_CTX *ctx;
+ STACK_OF(X509_NAME) *skCAList;
+ ssl_asn1_t *asn1;
+ unsigned char *ucp;
+ char *cp;
+ BOOL ok;
+ BOOL bSkipFirst;
+ int isca, pathlen;
+ int i, n;
+
+ /*
+ * Create the server host:port string because we need it a lot
+ */
+ cpVHostID = ssl_util_vhostid(p, s);
+
+ /*
+ * Now check for important parameters and the
+ * possibility that the user forgot to set them.
+ */
+ if (sc->szPublicCertFile[0] == NULL) {
+ ssl_log(s, SSL_LOG_ERROR,
+ "Init: (%s) No SSL Certificate set [hint: SSLCertificateFile]",
+ cpVHostID);
+ ssl_die();
+ }
+
+ /*
+ * Check for problematic re-initializations
+ */
+ if (sc->pPublicCert[SSL_AIDX_RSA] != NULL ||
+ sc->pPublicCert[SSL_AIDX_DSA] != NULL ) {
+ ssl_log(s, SSL_LOG_ERROR,
+ "Init: (%s) Illegal attempt to re-initialise SSL for server "
+ "(theoretically shouldn't happen!)", cpVHostID);
+ ssl_die();
+ }
+
+ /*
+ * Create the new per-server SSL context
+ */
+ if (sc->nProtocol == SSL_PROTOCOL_NONE) {
+ ssl_log(s, SSL_LOG_ERROR,
+ "Init: (%s) No SSL protocols available [hint: SSLProtocol]",
+ cpVHostID);
+ ssl_die();
+ }
+ cp = ap_pstrcat(p, (sc->nProtocol & SSL_PROTOCOL_SSLV2 ? "SSLv2, " : ""),
+ (sc->nProtocol & SSL_PROTOCOL_SSLV3 ? "SSLv3, " : ""),
+ (sc->nProtocol & SSL_PROTOCOL_TLSV1 ? "TLSv1, " : ""), NULL);
+ cp[strlen(cp)-2] = NUL;
+ ssl_log(s, SSL_LOG_TRACE,
+ "Init: (%s) Creating new SSL context (protocols: %s)", cpVHostID, cp);
+ if (sc->nProtocol == SSL_PROTOCOL_SSLV2)
+ ctx = SSL_CTX_new(SSLv2_server_method()); /* only SSLv2 is left */
+ else
+ ctx = SSL_CTX_new(SSLv23_server_method()); /* be more flexible */
+ SSL_CTX_set_options(ctx, SSL_OP_ALL);
+ if (!(sc->nProtocol & SSL_PROTOCOL_SSLV2))
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
+ if (!(sc->nProtocol & SSL_PROTOCOL_SSLV3))
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
+ if (!(sc->nProtocol & SSL_PROTOCOL_TLSV1))
+ SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
+ SSL_CTX_set_app_data(ctx, s);
+ sc->pSSLCtx = ctx;
+
+ /*
+ * Configure additional context ingredients
+ */
+ SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
+ if (mc->nSessionCacheMode == SSL_SCMODE_NONE)
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+ else
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
+
+ /*
+ * Configure callbacks for SSL context
+ */
+ nVerify = SSL_VERIFY_NONE;
+ if (sc->nVerifyClient == SSL_CVERIFY_REQUIRE)
+ nVerify |= SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ if ( (sc->nVerifyClient == SSL_CVERIFY_OPTIONAL)
+ || (sc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) )
+ nVerify |= SSL_VERIFY_PEER;
+ SSL_CTX_set_verify(ctx, nVerify, ssl_callback_SSLVerify);
+ SSL_CTX_sess_set_new_cb(ctx, ssl_callback_NewSessionCacheEntry);
+ SSL_CTX_sess_set_get_cb(ctx, ssl_callback_GetSessionCacheEntry);
+ SSL_CTX_sess_set_remove_cb(ctx, ssl_callback_DelSessionCacheEntry);
+ SSL_CTX_set_tmp_rsa_callback(ctx, ssl_callback_TmpRSA);
+ SSL_CTX_set_tmp_dh_callback(ctx, ssl_callback_TmpDH);
+ SSL_CTX_set_info_callback(ctx, ssl_callback_LogTracingState);
+
+ /*
+ * Configure SSL Cipher Suite
+ */
+ if (sc->szCipherSuite != NULL) {
+ ssl_log(s, SSL_LOG_TRACE,
+ "Init: (%s) Configuring permitted SSL ciphers [%s]",
+ cpVHostID, sc->szCipherSuite);
+ if (!SSL_CTX_set_cipher_list(ctx, sc->szCipherSuite)) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: (%s) Unable to configure permitted SSL ciphers",
+ cpVHostID);
+ ssl_die();
+ }
+ }
+
+ /*
+ * Configure Client Authentication details
+ */
+ if (sc->szCACertificateFile != NULL || sc->szCACertificatePath != NULL) {
+ ssl_log(s, SSL_LOG_TRACE,
+ "Init: (%s) Configuring client authentication", cpVHostID);
+ if (!SSL_CTX_load_verify_locations(ctx,
+ sc->szCACertificateFile,
+ sc->szCACertificatePath)) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: (%s) Unable to configure verify locations "
+ "for client authentication", cpVHostID);
+ ssl_die();
+ }
+ if ((skCAList = ssl_init_FindCAList(s, p, sc->szCACertificateFile,
+ sc->szCACertificatePath)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR,
+ "Init: (%s) Unable to determine list of available "
+ "CA certificates for client authentication", cpVHostID);
+ ssl_die();
+ }
+ SSL_CTX_set_client_CA_list(sc->pSSLCtx, skCAList);
+ }
+
+ /*
+ * Configure Certificate Revocation List (CRL) Details
+ */
+ if (sc->szCARevocationFile != NULL || sc->szCARevocationPath != NULL) {
+ ssl_log(s, SSL_LOG_TRACE,
+ "Init: (%s) Configuring certificate revocation facility", cpVHostID);
+ if ((sc->pRevocationStore =
+ SSL_X509_STORE_create(sc->szCARevocationFile,
+ sc->szCARevocationPath)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: (%s) Unable to configure X.509 CRL storage "
+ "for certificate revocation", cpVHostID);
+ ssl_die();
+ }
+ }
+
+ /*
+ * Give a warning when no CAs were configured but client authentication
+ * should take place. This cannot work.
+ */
+ if (sc->nVerifyClient == SSL_CVERIFY_REQUIRE) {
+ skCAList = SSL_CTX_get_client_CA_list(ctx);
+ if (sk_X509_NAME_num(skCAList) == 0)
+ ssl_log(s, SSL_LOG_WARN,
+ "Init: Ops, you want to request client authentication, "
+ "but no CAs are known for verification!? "
+ "[Hint: SSLCACertificate*]");
+ }
+
+ /*
+ * Configure server certificate(s)
+ */
+ ok = FALSE;
+ cp = ap_psprintf(p, "%s:RSA", cpVHostID);
+ if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tPublicCert, cp)) != NULL) {
+ ssl_log(s, SSL_LOG_TRACE,
+ "Init: (%s) Configuring RSA server certificate", cpVHostID);
+ ucp = asn1->cpData;
+ if ((sc->pPublicCert[SSL_AIDX_RSA] = d2i_X509(NULL, &ucp, asn1->nData)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: (%s) Unable to import RSA server certificate",
+ cpVHostID);
+ ssl_die();
+ }
+ if (SSL_CTX_use_certificate(ctx, sc->pPublicCert[SSL_AIDX_RSA]) <= 0) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: (%s) Unable to configure RSA server certificate",
+ cpVHostID);
+ ssl_die();
+ }
+ ok = TRUE;
+ }
+ cp = ap_psprintf(p, "%s:DSA", cpVHostID);
+ if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tPublicCert, cp)) != NULL) {
+ ssl_log(s, SSL_LOG_TRACE,
+ "Init: (%s) Configuring DSA server certificate", cpVHostID);
+ ucp = asn1->cpData;
+ if ((sc->pPublicCert[SSL_AIDX_DSA] = d2i_X509(NULL, &ucp, asn1->nData)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: (%s) Unable to import DSA server certificate",
+ cpVHostID);
+ ssl_die();
+ }
+ if (SSL_CTX_use_certificate(ctx, sc->pPublicCert[SSL_AIDX_DSA]) <= 0) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: (%s) Unable to configure DSA server certificate",
+ cpVHostID);
+ ssl_die();
+ }
+ ok = TRUE;
+ }
+ if (!ok) {
+ ssl_log(s, SSL_LOG_ERROR,
+ "Init: (%s) Ops, no RSA or DSA server certificate found?!", cpVHostID);
+ ssl_log(s, SSL_LOG_ERROR,
+ "Init: (%s) You have to perform a *full* server restart when you added or removed a certificate and/or key file", cpVHostID);
+ ssl_die();
+ }
+
+ /*
+ * Some information about the certificate(s)
+ */
+ for (i = 0; i < SSL_AIDX_MAX; i++) {
+ if (sc->pPublicCert[i] != NULL) {
+ if (SSL_X509_isSGC(sc->pPublicCert[i])) {
+ ssl_log(s, SSL_LOG_INFO,
+ "Init: (%s) %s server certificate enables "
+ "Server Gated Cryptography (SGC)",
+ cpVHostID, (i == SSL_AIDX_RSA ? "RSA" : "DSA"));
+ }
+ if (SSL_X509_getBC(sc->pPublicCert[i], &isca, &pathlen)) {
+ if (isca)
+ ssl_log(s, SSL_LOG_WARN,
+ "Init: (%s) %s server certificate is a CA certificate "
+ "(BasicConstraints: CA == TRUE !?)",
+ cpVHostID, (i == SSL_AIDX_RSA ? "RSA" : "DSA"));
+ if (pathlen > 0)
+ ssl_log(s, SSL_LOG_WARN,
+ "Init: (%s) %s server certificate is not a leaf certificate "
+ "(BasicConstraints: pathlen == %d > 0 !?)",
+ cpVHostID, (i == SSL_AIDX_RSA ? "RSA" : "DSA"), pathlen);
+ }
+ if (SSL_X509_getCN(p, sc->pPublicCert[i], &cp)) {
+ if (ap_is_fnmatch(cp) &&
+ !ap_fnmatch(cp, s->server_hostname, FNM_PERIOD|FNM_CASE_BLIND)) {
+ ssl_log(s, SSL_LOG_WARN,
+ "Init: (%s) %s server certificate wildcard CommonName (CN) `%s' "
+ "does NOT match server name!?", cpVHostID,
+ (i == SSL_AIDX_RSA ? "RSA" : "DSA"), cp);
+ }
+ else if (strNE(s->server_hostname, cp)) {
+ ssl_log(s, SSL_LOG_WARN,
+ "Init: (%s) %s server certificate CommonName (CN) `%s' "
+ "does NOT match server name!?", cpVHostID,
+ (i == SSL_AIDX_RSA ? "RSA" : "DSA"), cp);
+ }
+ }
+ }
+ }
+
+ /*
+ * Configure server private key(s)
+ */
+ ok = FALSE;
+ cp = ap_psprintf(p, "%s:RSA", cpVHostID);
+ if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tPrivateKey, cp)) != NULL) {
+ ssl_log(s, SSL_LOG_TRACE,
+ "Init: (%s) Configuring RSA server private key", cpVHostID);
+ ucp = asn1->cpData;
+ if ((sc->pPrivateKey[SSL_AIDX_RSA] =
+ d2i_PrivateKey(EVP_PKEY_RSA, NULL, &ucp, asn1->nData)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: (%s) Unable to import RSA server private key",
+ cpVHostID);
+ ssl_die();
+ }
+ if (SSL_CTX_use_PrivateKey(ctx, sc->pPrivateKey[SSL_AIDX_RSA]) <= 0) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: (%s) Unable to configure RSA server private key",
+ cpVHostID);
+ ssl_die();
+ }
+ ok = TRUE;
+ }
+ cp = ap_psprintf(p, "%s:DSA", cpVHostID);
+ if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tPrivateKey, cp)) != NULL) {
+ ssl_log(s, SSL_LOG_TRACE,
+ "Init: (%s) Configuring DSA server private key", cpVHostID);
+ ucp = asn1->cpData;
+ if ((sc->pPrivateKey[SSL_AIDX_DSA] =
+ d2i_PrivateKey(EVP_PKEY_DSA, NULL, &ucp, asn1->nData)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: (%s) Unable to import DSA server private key",
+ cpVHostID);
+ ssl_die();
+ }
+ if (SSL_CTX_use_PrivateKey(ctx, sc->pPrivateKey[SSL_AIDX_DSA]) <= 0) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: (%s) Unable to configure DSA server private key",
+ cpVHostID);
+ ssl_die();
+ }
+ ok = TRUE;
+ }
+ if (!ok) {
+ ssl_log(s, SSL_LOG_ERROR,
+ "Init: (%s) Ops, no RSA or DSA server private key found?!", cpVHostID);
+ ssl_die();
+ }
+
+ /*
+ * Optionally copy DSA parameters for certificate from private key
+ * (see http://www.psy.uq.edu.au/~ftp/Crypto/ssleay/TODO.html)
+ */
+ if ( sc->pPublicCert[SSL_AIDX_DSA] != NULL
+ && sc->pPrivateKey[SSL_AIDX_DSA] != NULL) {
+ pKey = X509_get_pubkey(sc->pPublicCert[SSL_AIDX_DSA]);
+ if ( pKey != NULL
+ && EVP_PKEY_type(pKey->type) == EVP_PKEY_DSA
+ && EVP_PKEY_missing_parameters(pKey))
+ EVP_PKEY_copy_parameters(pKey, sc->pPrivateKey[SSL_AIDX_DSA]);
+ }
+
+ /*
+ * Optionally configure extra server certificate chain certificates.
+ * This is usually done by OpenSSL automatically when one of the
+ * server cert issuers are found under SSLCACertificatePath or in
+ * SSLCACertificateFile. But because these are intended for client
+ * authentication it can conflict. For instance when you use a
+ * Global ID server certificate you've to send out the intermediate
+ * CA certificate, too. When you would just configure this with
+ * SSLCACertificateFile and also use client authentication mod_ssl
+ * would accept all clients also issued by this CA. Obviously this
+ * isn't what we want in this situation. So this feature here exists
+ * to allow one to explicity configure CA certificates which are
+ * used only for the server certificate chain.
+ */
+ if (sc->szCertificateChain != NULL) {
+ bSkipFirst = FALSE;
+ for (i = 0; i < SSL_AIDX_MAX && sc->szPublicCertFile[i] != NULL; i++) {
+ if (strEQ(sc->szPublicCertFile[i], sc->szCertificateChain)) {
+ bSkipFirst = TRUE;
+ break;
+ }
+ }
+ if ((n = SSL_CTX_use_certificate_chain(ctx, sc->szCertificateChain,
+ bSkipFirst, NULL)) < 0) {
+ ssl_log(s, SSL_LOG_ERROR,
+ "Init: (%s) Failed to configure CA certificate chain!", cpVHostID);
+ ssl_die();
+ }
+ ssl_log(s, SSL_LOG_TRACE, "Init: (%s) Configuring "
+ "server certificate chain (%d CA certificate%s)", cpVHostID,
+ n, n == 1 ? "" : "s");
+ }
+
+#ifdef SSL_VENDOR
+ ap_hook_use("ap::mod_ssl::vendor::configure_server",
+ AP_HOOK_SIG4(void,ptr,ptr,ptr), AP_HOOK_ALL,
+ s, p, sc);
+#endif
+
+ return;
+}
+
+void ssl_init_CheckServers(server_rec *sm, pool *p)
+{
+ server_rec *s;
+ server_rec **ps;
+ SSLSrvConfigRec *sc;
+ ssl_ds_table *t;
+ pool *sp;
+ char *key;
+ BOOL bConflict;
+
+ /*
+ * Give out warnings when a server has HTTPS configured
+ * for the HTTP port or vice versa
+ */
+ for (s = sm; s != NULL; s = s->next) {
+ sc = mySrvConfig(s);
+ if (sc->bEnabled && s->port == DEFAULT_HTTP_PORT)
+ ssl_log(sm, SSL_LOG_WARN,
+ "Init: (%s) You configured HTTPS(%d) on the standard HTTP(%d) port!",
+ ssl_util_vhostid(p, s), DEFAULT_HTTPS_PORT, DEFAULT_HTTP_PORT);
+ if (!sc->bEnabled && s->port == DEFAULT_HTTPS_PORT)
+ ssl_log(sm, SSL_LOG_WARN,
+ "Init: (%s) You configured HTTP(%d) on the standard HTTPS(%d) port!",
+ ssl_util_vhostid(p, s), DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT);
+ }
+
+ /*
+ * Give out warnings when more than one SSL-aware virtual server uses the
+ * same IP:port. This doesn't work because mod_ssl then will always use
+ * just the certificate/keys of one virtual host (which one cannot be said
+ * easily - but that doesn't matter here).
+ */
+ sp = ap_make_sub_pool(p);
+ t = ssl_ds_table_make(sp, sizeof(server_rec *));
+ bConflict = FALSE;
+ for (s = sm; s != NULL; s = s->next) {
+ sc = mySrvConfig(s);
+ if (!sc->bEnabled)
+ continue;
+ key = ap_psprintf(sp, "%pA:%u", &s->addrs->host_addr, s->addrs->host_port);
+ ps = ssl_ds_table_get(t, key);
+ if (ps != NULL) {
+ ssl_log(sm, SSL_LOG_WARN,
+ "Init: SSL server IP/port conflict: %s (%s:%d) vs. %s (%s:%d)",
+ ssl_util_vhostid(p, s),
+ (s->defn_name != NULL ? s->defn_name : "unknown"),
+ s->defn_line_number,
+ ssl_util_vhostid(p, *ps),
+ ((*ps)->defn_name != NULL ? (*ps)->defn_name : "unknown"),
+ (*ps)->defn_line_number);
+ bConflict = TRUE;
+ continue;
+ }
+ ps = ssl_ds_table_push(t, key);
+ *ps = s;
+ }
+ ssl_ds_table_kill(t);
+ ap_destroy_pool(sp);
+ if (bConflict)
+ ssl_log(sm, SSL_LOG_WARN,
+ "Init: You should not use name-based virtual hosts in conjunction with SSL!!");
+
+ return;
+}
+
+static int ssl_init_FindCAList_X509NameCmp(X509_NAME **a, X509_NAME **b)
+{
+ return(X509_NAME_cmp(*a, *b));
+}
+
+STACK_OF(X509_NAME) *ssl_init_FindCAList(server_rec *s, pool *pp, char *cpCAfile, char *cpCApath)
+{
+ STACK_OF(X509_NAME) *skCAList;
+ STACK_OF(X509_NAME) *sk;
+ DIR *dir;
+ struct DIR_TYPE *direntry;
+ char *cp;
+ pool *p;
+ int n;
+
+ /*
+ * Use a subpool so we don't bloat up the server pool which
+ * is remains in memory for the complete operation time of
+ * the server.
+ */
+ p = ap_make_sub_pool(pp);
+
+ /*
+ * Start with a empty stack/list where new
+ * entries get added in sorted order.
+ */
+ skCAList = sk_X509_NAME_new(ssl_init_FindCAList_X509NameCmp);
+
+ /*
+ * Process CA certificate bundle file
+ */
+ if (cpCAfile != NULL) {
+ sk = SSL_load_client_CA_file(cpCAfile);
+ for(n = 0; sk != NULL && n < sk_X509_NAME_num(sk); n++) {
+ ssl_log(s, SSL_LOG_TRACE,
+ "CA certificate: %s",
+ X509_NAME_oneline(sk_X509_NAME_value(sk, n), NULL, 0));
+ if (sk_X509_NAME_find(skCAList, sk_X509_NAME_value(sk, n)) < 0)
+ sk_X509_NAME_push(skCAList, sk_X509_NAME_value(sk, n));
+ }
+ }
+
+ /*
+ * Process CA certificate path files
+ */
+ if (cpCApath != NULL) {
+ dir = ap_popendir(p, cpCApath);
+ while ((direntry = readdir(dir)) != NULL) {
+ cp = ap_pstrcat(p, cpCApath, "/", direntry->d_name, NULL);
+ sk = SSL_load_client_CA_file(cp);
+ for(n = 0; sk != NULL && n < sk_X509_NAME_num(sk); n++) {
+ ssl_log(s, SSL_LOG_TRACE,
+ "CA certificate: %s",
+ X509_NAME_oneline(sk_X509_NAME_value(sk, n), NULL, 0));
+ if (sk_X509_NAME_find(skCAList, sk_X509_NAME_value(sk, n)) < 0)
+ sk_X509_NAME_push(skCAList, sk_X509_NAME_value(sk, n));
+ }
+ }
+ ap_pclosedir(p, dir);
+ }
+
+ /*
+ * Cleanup
+ */
+ sk_X509_NAME_set_cmp_func(skCAList, NULL);
+ ap_destroy_pool(p);
+
+ return skCAList;
+}
+
+void ssl_init_Child(server_rec *s, pool *p)
+{
+ /* open the mutex lockfile */
+ ssl_mutex_reinit(s, p);
+ return;
+}
+
+void ssl_init_ChildKill(void *data)
+{
+ /* currently nothing to do */
+ return;
+}
+
+void ssl_init_ModuleKill(void *data)
+{
+ SSLSrvConfigRec *sc;
+ server_rec *s = (server_rec *)data;
+
+ /*
+ * Drop the session cache and mutex
+ */
+ ssl_scache_kill(s);
+ ssl_mutex_kill(s);
+
+ /*
+ * Destroy the temporary keys and params
+ */
+ ssl_init_TmpKeysHandle(SSL_TKP_FREE, s, NULL);
+
+ /*
+ * Free the non-pool allocated structures
+ * in the per-server configurations
+ */
+ for (; s != NULL; s = s->next) {
+ sc = mySrvConfig(s);
+ if (sc->pPublicCert[SSL_AIDX_RSA] != NULL) {
+ X509_free(sc->pPublicCert[SSL_AIDX_RSA]);
+ sc->pPublicCert[SSL_AIDX_RSA] = NULL;
+ }
+ if (sc->pPublicCert[SSL_AIDX_DSA] != NULL) {
+ X509_free(sc->pPublicCert[SSL_AIDX_DSA]);
+ sc->pPublicCert[SSL_AIDX_DSA] = NULL;
+ }
+ if (sc->pPrivateKey[SSL_AIDX_RSA] != NULL) {
+ EVP_PKEY_free(sc->pPrivateKey[SSL_AIDX_RSA]);
+ sc->pPrivateKey[SSL_AIDX_RSA] = NULL;
+ }
+ if (sc->pPrivateKey[SSL_AIDX_DSA] != NULL) {
+ EVP_PKEY_free(sc->pPrivateKey[SSL_AIDX_DSA]);
+ sc->pPrivateKey[SSL_AIDX_DSA] = NULL;
+ }
+ if (sc->pSSLCtx != NULL) {
+ SSL_CTX_free(sc->pSSLCtx);
+ sc->pSSLCtx = NULL;
+ }
+ }
+
+ /*
+ * Try to kill the internals of the SSL library.
+ */
+#ifdef SHARED_MODULE
+ ERR_free_strings();
+ ERR_remove_state(0);
+ EVP_cleanup();
+#endif
+
+ return;
+}
+
diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c
new file mode 100644
index 0000000000..4ba1574ca8
--- /dev/null
+++ b/modules/ssl/ssl_engine_io.c
@@ -0,0 +1,728 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_engine_io.c
+** I/O Functions
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``MY HACK: This universe.
+ Just one little problem:
+ core keeps dumping.''
+ -- Unknown */
+#include "mod_ssl.h"
+
+/* _________________________________________________________________
+**
+** I/O Request Body Sucking and Re-Injection
+** _________________________________________________________________
+*/
+
+#ifndef SSL_CONSERVATIVE
+
+/*
+ * Background:
+ *
+ * 1. When the client sends a HTTP/HTTPS request, Apache's core code
+ * reads only the request line ("METHOD /path HTTP/x.y") and the
+ * attached MIME headers ("Foo: bar") up to the terminating line ("CR
+ * LF"). An attached request body (for instance the data of a POST
+ * method) is _NOT_ read. Instead it is read by mod_cgi's content
+ * handler and directly passed to the CGI script.
+ *
+ * 2. mod_ssl supports per-directory re-configuration of SSL parameters.
+ * This is implemented by performing an SSL renegotiation of the
+ * re-configured parameters after the request is read, but before the
+ * response is sent. In more detail: the renegotiation happens after the
+ * request line and MIME headers were read, but _before_ the attached
+ * request body is read. The reason simply is that in the HTTP protocol
+ * usually there is no acknowledgment step between the headers and the
+ * body (there is the 100-continue feature and the chunking facility
+ * only), so Apache has no API hook for this step.
+ *
+ * 3. the problem now occurs when the client sends a POST request for
+ * URL /foo via HTTPS the server and the server has SSL parameters
+ * re-configured on a per-URL basis for /foo. Then mod_ssl has to
+ * perform an SSL renegotiation after the request was read and before
+ * the response is sent. But the problem is the pending POST body data
+ * in the receive buffer of SSL (which Apache still has not read - it's
+ * pending until mod_cgi sucks it in). When mod_ssl now tries to perform
+ * the renegotiation the pending data leads to an I/O error.
+ *
+ * Solution Idea:
+ *
+ * There are only two solutions: Either to simply state that POST
+ * requests to URLs with SSL re-configurations are not allowed, or to
+ * renegotiate really after the _complete_ request (i.e. including
+ * the POST body) was read. Obviously the latter would be preferred,
+ * but it cannot be done easily inside Apache, because as already
+ * mentioned, there is no API step between the body reading and the body
+ * processing. And even when we mod_ssl would hook directly into the
+ * loop of mod_cgi, we wouldn't solve the problem for other handlers, of
+ * course. So the only general solution is to suck in the pending data
+ * of the request body from the OpenSSL BIO into the Apache BUFF. Then
+ * the renegotiation can be done and after this step Apache can proceed
+ * processing the request as before.
+ *
+ * Solution Implementation:
+ *
+ * We cannot simply suck in the data via an SSL_read-based loop because of
+ * HTTP chunking. Instead we _have_ to use the Apache API for this step which
+ * is aware of HTTP chunking. So the trick is to suck in the pending request
+ * data via the Apache API (which uses Apache's BUFF code and in the
+ * background mod_ssl's I/O glue code) and re-inject it later into the Apache
+ * BUFF code again. This way the data flows twice through the Apache BUFF, of
+ * course. But this way the solution doesn't depend on any Apache specifics
+ * and is fully transparent to Apache modules.
+ */
+
+struct ssl_io_suck_st {
+ BOOL active;
+ char *bufptr;
+ int buflen;
+ char *pendptr;
+ int pendlen;
+};
+
+/* prepare request_rec structure for input sucking */
+static void ssl_io_suck_start(request_rec *r)
+{
+ struct ssl_io_suck_st *ss;
+
+ ss = ap_ctx_get(r->ctx, "ssl::io::suck");
+ if (ss == NULL) {
+ ss = ap_palloc(r->pool, sizeof(struct ssl_io_suck_st));
+ ap_ctx_set(r->ctx, "ssl::io::suck", ss);
+ ss->buflen = 8192;
+ ss->bufptr = ap_palloc(r->pool, ss->buflen);
+ }
+ ss->pendptr = ss->bufptr;
+ ss->pendlen = 0;
+ ss->active = FALSE;
+ return;
+}
+
+/* record a sucked input chunk */
+static void ssl_io_suck_record(request_rec *r, char *buf, int len)
+{
+ struct ssl_io_suck_st *ss;
+
+ if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL)
+ return;
+ if (((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) < len) {
+ /* "expand" buffer: actually we cannot really expand the buffer
+ here, because Apache's pool system doesn't support expanding chunks
+ of memory. Instead we have to either reuse processed data or
+ allocate a new chunk of memory in advance if we really need more
+ memory. */
+ int newlen;
+ char *newptr;
+
+ if (( (ss->pendptr - ss->bufptr)
+ + ((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) ) >= len) {
+ /* make memory available by reusing already processed data */
+ memmove(ss->bufptr, ss->pendptr, ss->pendlen);
+ ss->pendptr = ss->bufptr;
+ }
+ else {
+ /* too bad, we have to allocate a new larger buffer */
+ newlen = (ss->buflen * 2) + len;
+ newptr = ap_palloc(r->pool, newlen);
+ ss->bufptr = newptr;
+ ss->buflen = newlen;
+ memcpy(ss->bufptr, ss->pendptr, ss->pendlen);
+ ss->pendptr = ss->bufptr;
+ }
+ }
+ memcpy(ss->pendptr+ss->pendlen, buf, len);
+ ss->pendlen += len;
+ return;
+}
+
+/* finish request_rec after input sucking */
+static void ssl_io_suck_end(request_rec *r)
+{
+ struct ssl_io_suck_st *ss;
+
+ if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL)
+ return;
+ ss->active = TRUE;
+ r->read_body = REQUEST_NO_BODY;
+ r->read_length = 0;
+ r->read_chunked = 0;
+ r->remaining = 0;
+ ap_bsetflag(r->connection->client, B_CHUNK, 0);
+ return;
+}
+
+void ssl_io_suck(request_rec *r, SSL *ssl)
+{
+ int rc;
+ int len;
+ char *buf;
+ int buflen;
+ char c;
+ int sucked;
+
+ if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) == OK) {
+ if (ap_should_client_block(r)) {
+
+ /* read client request block through Apache API */
+ buflen = HUGE_STRING_LEN;
+ buf = ap_palloc(r->pool, buflen);
+ ap_hard_timeout("SSL I/O request body pre-sucking", r);
+ sucked = 0;
+ ssl_io_suck_start(r);
+ while ((len = ap_get_client_block(r, buf, buflen)) > 0) {
+ ssl_io_suck_record(r, buf, len);
+ sucked += len;
+ }
+ ssl_io_suck_end(r);
+ ap_kill_timeout(r);
+
+ /* suck trailing data (usually CR LF) which
+ is still in the Apache BUFF layer */
+ while (ap_bpeekc(r->connection->client) != EOF) {
+ c = ap_bgetc(r->connection->client);
+ ssl_io_suck_record(r, &c, 1);
+ sucked++;
+ }
+
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "I/O: sucked %d bytes of input data from SSL/TLS I/O layer "
+ "for delayed injection into Apache I/O layer", sucked);
+ }
+ }
+ return;
+}
+
+/* the SSL_read replacement routine which knows about the suck buffer */
+static int ssl_io_suck_read(SSL *ssl, char *buf, int len)
+{
+ ap_ctx *actx;
+ struct ssl_io_suck_st *ss;
+ request_rec *r = NULL;
+ int rv;
+
+ actx = (ap_ctx *)SSL_get_app_data2(ssl);
+ if (actx != NULL)
+ r = (request_rec *)ap_ctx_get(actx, "ssl::request_rec");
+
+ rv = -1;
+ if (r != NULL) {
+ ss = ap_ctx_get(r->ctx, "ssl::io::suck");
+ if (ss != NULL) {
+ if (ss->active && ss->pendlen > 0) {
+ /* ok, there is pre-sucked data */
+ len = (ss->pendlen > len ? len : ss->pendlen);
+ memcpy(buf, ss->pendptr, len);
+ ss->pendptr += len;
+ ss->pendlen -= len;
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "I/O: injecting %d bytes of pre-sucked data "
+ "into Apache I/O layer", len);
+ rv = len;
+ }
+ }
+ }
+ if (rv == -1)
+ rv = SSL_read(ssl, buf, len);
+ return rv;
+}
+
+/* override SSL_read in the following code... */
+#define SSL_read ssl_io_suck_read
+
+#endif /* !SSL_CONSERVATIVE */
+
+/* _________________________________________________________________
+**
+** I/O Hooks
+** _________________________________________________________________
+*/
+
+#ifndef NO_WRITEV
+#include <sys/types.h>
+#include <sys/uio.h>
+#endif
+
+static int ssl_io_hook_read(BUFF *fb, char *buf, int len);
+static int ssl_io_hook_write(BUFF *fb, char *buf, int len);
+#ifndef NO_WRITEV
+static int ssl_io_hook_writev(BUFF *fb, const struct iovec *iov, int iovcnt);
+#endif
+#ifdef WIN32
+static int ssl_io_hook_recvwithtimeout(BUFF *fb, char *buf, int len);
+static int ssl_io_hook_sendwithtimeout(BUFF *fb, const char *buf, int len);
+#endif /* WIN32 */
+
+void ssl_io_register(void)
+{
+ ap_hook_register("ap::buff::read", ssl_io_hook_read, AP_HOOK_NOCTX);
+ ap_hook_register("ap::buff::write", ssl_io_hook_write, AP_HOOK_NOCTX);
+#ifndef NO_WRITEV
+ ap_hook_register("ap::buff::writev", ssl_io_hook_writev, AP_HOOK_NOCTX);
+#endif
+#ifdef WIN32
+ ap_hook_register("ap::buff::recvwithtimeout",
+ ssl_io_hook_recvwithtimeout, AP_HOOK_NOCTX);
+ ap_hook_register("ap::buff::sendwithtimeout",
+ ssl_io_hook_sendwithtimeout, AP_HOOK_NOCTX);
+#endif
+ return;
+}
+
+void ssl_io_unregister(void)
+{
+ ap_hook_unregister("ap::buff::read", ssl_io_hook_read);
+ ap_hook_unregister("ap::buff::write", ssl_io_hook_write);
+#ifndef NO_WRITEV
+ ap_hook_unregister("ap::buff::writev", ssl_io_hook_writev);
+#endif
+#ifdef WIN32
+ ap_hook_unregister("ap::buff::recvwithtimeout", ssl_io_hook_recvwithtimeout);
+ ap_hook_unregister("ap::buff::sendwithtimeout", ssl_io_hook_sendwithtimeout);
+#endif
+ return;
+}
+
+static int ssl_io_hook_read(BUFF *fb, char *buf, int len)
+{
+ SSL *ssl;
+ conn_rec *c;
+ int rc;
+
+ if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) {
+ rc = SSL_read(ssl, buf, len);
+ /*
+ * Simulate an EINTR in case OpenSSL wants to read more.
+ * (This is usually the case when the client forces an SSL
+ * renegotation which is handled implicitly by OpenSSL.)
+ */
+ if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ)
+ errno = EINTR;
+ /*
+ * Log SSL errors
+ */
+ if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) {
+ c = (conn_rec *)SSL_get_app_data(ssl);
+ ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "SSL error on reading data");
+ }
+ /*
+ * read(2) returns only the generic error number -1
+ */
+ if (rc < 0)
+ rc = -1;
+ }
+ else
+ rc = read(fb->fd_in, buf, len);
+ return rc;
+}
+
+static int ssl_io_hook_write(BUFF *fb, char *buf, int len)
+{
+ SSL *ssl;
+ conn_rec *c;
+ int rc;
+
+ if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) {
+ rc = SSL_write(ssl, buf, len);
+ /*
+ * Simulate an EINTR in case OpenSSL wants to write more.
+ */
+ if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_WRITE)
+ errno = EINTR;
+ /*
+ * Log SSL errors
+ */
+ if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) {
+ c = (conn_rec *)SSL_get_app_data(ssl);
+ ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "SSL error on writing data");
+ }
+ /*
+ * write(2) returns only the generic error number -1
+ */
+ if (rc < 0)
+ rc = -1;
+ }
+ else
+ rc = write(fb->fd, buf, len);
+ return rc;
+}
+
+#ifndef NO_WRITEV
+/* the prototype for our own SSL_writev() */
+static int SSL_writev(SSL *, const struct iovec *, int);
+
+static int ssl_io_hook_writev(BUFF *fb, const struct iovec *iov, int iovcnt)
+{
+ SSL *ssl;
+ conn_rec *c;
+ int rc;
+
+ if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) {
+ rc = SSL_writev(ssl, iov, iovcnt);
+ /*
+ * Simulate an EINTR in case OpenSSL wants to write more.
+ */
+ if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_WRITE)
+ errno = EINTR;
+ /*
+ * Log SSL errors
+ */
+ if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) {
+ c = (conn_rec *)SSL_get_app_data(ssl);
+ ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "SSL error on writing data");
+ }
+ /*
+ * writev(2) returns only the generic error number -1
+ */
+ if (rc < 0)
+ rc = -1;
+ }
+ else
+ rc = writev(fb->fd, iov, iovcnt);
+ return rc;
+}
+#endif
+
+#ifdef WIN32
+
+/* these two functions are exported from buff.c under WIN32 */
+API_EXPORT(int) sendwithtimeout(int sock, const char *buf, int len, int flags);
+API_EXPORT(int) recvwithtimeout(int sock, char *buf, int len, int flags);
+
+/* and the prototypes for our SSL_xxx variants */
+static int SSL_sendwithtimeout(BUFF *fb, const char *buf, int len);
+static int SSL_recvwithtimeout(BUFF *fb, char *buf, int len);
+
+static int ssl_io_hook_recvwithtimeout(BUFF *fb, char *buf, int len)
+{
+ SSL *ssl;
+ int rc;
+
+ if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL)
+ rc = SSL_recvwithtimeout(fb, buf, len);
+ else
+ rc = recvwithtimeout(fb->fd, buf, len, 0);
+ return rc;
+}
+
+static int ssl_io_hook_sendwithtimeout(BUFF *fb, const char *buf, int len)
+{
+ SSL *ssl;
+ int rc;
+
+ if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL)
+ rc = SSL_sendwithtimeout(fb, buf, len);
+ else
+ rc = sendwithtimeout(fb->fd, buf, len, 0);
+ return rc;
+}
+
+#endif /* WIN32 */
+
+/* _________________________________________________________________
+**
+** Special Functions for OpenSSL
+** _________________________________________________________________
+*/
+
+#ifdef WIN32
+
+static int SSL_sendwithtimeout(BUFF *fb, const char *buf, int len)
+{
+ int iostate = 1;
+ fd_set fdset;
+ struct timeval tv;
+ int err = WSAEWOULDBLOCK;
+ int rv;
+ int retry;
+ int sock = fb->fd;
+ SSL *ssl;
+
+ ssl = ap_ctx_get(fb->ctx, "ssl");
+
+ if (!(tv.tv_sec = ap_check_alarm()))
+ return (SSL_write(ssl, (char*)buf, len));
+
+ rv = ioctlsocket(sock, FIONBIO, &iostate);
+ iostate = 0;
+ if (rv) {
+ err = WSAGetLastError();
+ ap_assert(0);
+ }
+ rv = SSL_write(ssl, (char*)buf, len);
+ if (rv <= 0) {
+ if (BIO_sock_should_retry(rv)) {
+ do {
+ retry = 0;
+ FD_ZERO(&fdset);
+ FD_SET((unsigned int)sock, &fdset);
+ tv.tv_usec = 0;
+ rv = select(FD_SETSIZE, NULL, &fdset, NULL, &tv);
+ if (rv == SOCKET_ERROR)
+ err = WSAGetLastError();
+ else if (rv == 0) {
+ ioctlsocket(sock, FIONBIO, &iostate);
+ if(ap_check_alarm() < 0) {
+ WSASetLastError(EINTR); /* Simulate an alarm() */
+ return (SOCKET_ERROR);
+ }
+ }
+ else {
+ rv = SSL_write(ssl, (char*)buf, len);
+ if (BIO_sock_should_retry(rv)) {
+ ap_log_error(APLOG_MARK,APLOG_DEBUG, NULL,
+ "select claimed we could write, "
+ "but in fact we couldn't. "
+ "This is a bug in Windows.");
+ retry = 1;
+ Sleep(100);
+ }
+ }
+ } while(retry);
+ }
+ }
+ ioctlsocket(sock, FIONBIO, &iostate);
+ if (rv == SOCKET_ERROR)
+ WSASetLastError(err);
+ return (rv);
+}
+
+static int SSL_recvwithtimeout(BUFF *fb, char *buf, int len)
+{
+ int iostate = 1;
+ fd_set fdset;
+ struct timeval tv;
+ int err = WSAEWOULDBLOCK;
+ int rv;
+ int sock = fb->fd_in;
+ SSL *ssl;
+ int retry;
+
+ ssl = ap_ctx_get(fb->ctx, "ssl");
+
+ if (!(tv.tv_sec = ap_check_alarm()))
+ return (SSL_read(ssl, buf, len));
+
+ rv = ioctlsocket(sock, FIONBIO, &iostate);
+ iostate = 0;
+ ap_assert(!rv);
+ rv = SSL_read(ssl, buf, len);
+ if (rv <= 0) {
+ if (BIO_sock_should_retry(rv)) {
+ do {
+ retry = 0;
+ FD_ZERO(&fdset);
+ FD_SET((unsigned int)sock, &fdset);
+ tv.tv_usec = 0;
+ rv = select(FD_SETSIZE, &fdset, NULL, NULL, &tv);
+ if (rv == SOCKET_ERROR)
+ err = WSAGetLastError();
+ else if (rv == 0) {
+ ioctlsocket(sock, FIONBIO, &iostate);
+ ap_check_alarm();
+ WSASetLastError(WSAEWOULDBLOCK);
+ return (SOCKET_ERROR);
+ }
+ else {
+ rv = SSL_read(ssl, buf, len);
+ if (rv == SOCKET_ERROR) {
+ if (BIO_sock_should_retry(rv)) {
+ ap_log_error(APLOG_MARK,APLOG_DEBUG, NULL,
+ "select claimed we could read, "
+ "but in fact we couldn't. "
+ "This is a bug in Windows.");
+ retry = 1;
+ Sleep(100);
+ }
+ else {
+ err = WSAGetLastError();
+ }
+ }
+ }
+ } while(retry);
+ }
+ }
+ ioctlsocket(sock, FIONBIO, &iostate);
+ if (rv == SOCKET_ERROR)
+ WSASetLastError(err);
+ return (rv);
+}
+
+#endif /*WIN32*/
+
+/*
+ * There is no SSL_writev() provided by OpenSSL. The reason is mainly because
+ * OpenSSL has to fragment the data itself again for the SSL record layer, so a
+ * writev() like interface makes not much sense. What we do is to emulate it
+ * to at least being able to use the write() like interface. But keep in mind
+ * that the network I/O performance is not write() like, of course.
+ */
+#ifndef NO_WRITEV
+static int SSL_writev(SSL *ssl, const struct iovec *iov, int iovcnt)
+{
+ int i;
+ int n;
+ int rc;
+
+ rc = 0;
+ for (i = 0; i < iovcnt; i++) {
+ if ((n = SSL_write(ssl, iov[i].iov_base, iov[i].iov_len)) == -1) {
+ rc = -1;
+ break;
+ }
+ rc += n;
+ }
+ return rc;
+}
+#endif
+
+/* _________________________________________________________________
+**
+** I/O Data Debugging
+** _________________________________________________________________
+*/
+
+#define DUMP_WIDTH 16
+
+static void ssl_io_data_dump(server_rec *srvr, const char *s, long len)
+{
+ char buf[256];
+ char tmp[64];
+ int i, j, rows, trunc;
+ unsigned char ch;
+
+ trunc = 0;
+ for(; (len > 0) && ((s[len-1] == ' ') || (s[len-1] == '\0')); len--)
+ trunc++;
+ rows = (len / DUMP_WIDTH);
+ if ((rows * DUMP_WIDTH) < len)
+ rows++;
+ ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID,
+ "+-------------------------------------------------------------------------+");
+ for(i = 0 ; i< rows; i++) {
+ ap_snprintf(tmp, sizeof(tmp), "| %04x: ", i * DUMP_WIDTH);
+ ap_cpystrn(buf, tmp, sizeof(buf));
+ for (j = 0; j < DUMP_WIDTH; j++) {
+ if (((i * DUMP_WIDTH) + j) >= len)
+ ap_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
+ else {
+ ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff;
+ ap_snprintf(tmp, sizeof(tmp), "%02x%c", ch , j==7 ? '-' : ' ');
+ ap_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf));
+ }
+ }
+ ap_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
+ for (j = 0; j < DUMP_WIDTH; j++) {
+ if (((i * DUMP_WIDTH) + j) >= len)
+ ap_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
+ else {
+ ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff;
+ ap_snprintf(tmp, sizeof(tmp), "%c", ((ch >= ' ') && (ch <= '~')) ? ch : '.');
+ ap_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf));
+ }
+ }
+ ap_cpystrn(buf+strlen(buf), " |", sizeof(buf)-strlen(buf));
+ ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID, "%s", buf);
+ }
+ if (trunc > 0)
+ ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID,
+ "| %04x - <SPACES/NULS>", len + trunc);
+ ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID,
+ "+-------------------------------------------------------------------------+");
+ return;
+}
+
+long ssl_io_data_cb(BIO *bio, int cmd, const char *argp, int argi, long argl, long rc)
+{
+ SSL *ssl;
+ conn_rec *c;
+ server_rec *s;
+
+ if ((ssl = (SSL *)BIO_get_callback_arg(bio)) == NULL)
+ return rc;
+ if ((c = (conn_rec *)SSL_get_app_data(ssl)) == NULL)
+ return rc;
+ s = c->server;
+
+ if ( cmd == (BIO_CB_WRITE|BIO_CB_RETURN)
+ || cmd == (BIO_CB_READ |BIO_CB_RETURN) ) {
+ if (rc >= 0) {
+ ssl_log(s, SSL_LOG_DEBUG,
+ "%s: %s %ld/%d bytes %s BIO#%08X [mem: %08lX] %s",
+ SSL_LIBRARY_NAME,
+ (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
+ rc, argi, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "to" : "from"),
+ bio, argp,
+ (argp != NULL ? "(BIO dump follows)" : "(Ops, no memory buffer?)"));
+ if (argp != NULL)
+ ssl_io_data_dump(s, argp, rc);
+ }
+ else {
+ ssl_log(s, SSL_LOG_DEBUG,
+ "%s: I/O error, %d bytes expected to %s on BIO#%08X [mem: %08lX]",
+ SSL_LIBRARY_NAME, argi,
+ (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
+ bio, argp);
+ }
+ }
+ return rc;
+}
+
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
new file mode 100644
index 0000000000..ca1b3f0a55
--- /dev/null
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -0,0 +1,1905 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_engine_kernel.c
+** The SSL engine kernel
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/* ====================================================================
+ * Copyright (c) 1995-1999 Ben Laurie. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by Ben Laurie
+ * for use in the Apache-SSL HTTP server project."
+ *
+ * 4. The name "Apache-SSL Server" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Ben Laurie
+ * for use in the Apache-SSL HTTP server project."
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN LAURIE ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BEN LAURIE OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``It took me fifteen years to discover
+ I had no talent for programming, but
+ I couldn't give it up because by that
+ time I was too famous.''
+ -- Unknown */
+#include "mod_ssl.h"
+
+
+/* _________________________________________________________________
+**
+** SSL Engine Kernel
+** _________________________________________________________________
+*/
+
+/*
+ * Connect Handler:
+ * Connect SSL to the accepted socket
+ *
+ * Usually we would need an Apache API hook which is triggered right after
+ * the socket is accepted for handling a new request. But Apache 1.3 doesn't
+ * provide such a hook, so we have to patch http_main.c and call this
+ * function directly.
+ */
+void ssl_hook_NewConnection(conn_rec *conn)
+{
+ server_rec *srvr;
+ BUFF *fb;
+ SSLSrvConfigRec *sc;
+ ap_ctx *apctx;
+ SSL *ssl;
+ char *cp;
+ char *cpVHostID;
+ char *cpVHostMD5;
+ X509 *xs;
+ int rc;
+
+ /*
+ * Get context
+ */
+ srvr = conn->server;
+ fb = conn->client;
+ sc = mySrvConfig(srvr);
+
+ /*
+ * Create SSL context
+ */
+ ap_ctx_set(fb->ctx, "ssl", NULL);
+
+ /*
+ * Immediately stop processing if SSL
+ * is disabled for this connection
+ */
+ if (sc == NULL || !sc->bEnabled)
+ return;
+
+ /*
+ * Remember the connection information for
+ * later access inside callback functions
+ */
+ cpVHostID = ssl_util_vhostid(conn->pool, srvr);
+ ssl_log(srvr, SSL_LOG_INFO, "Connection to child %d established "
+ "(server %s, client %s)", conn->child_num, cpVHostID,
+ conn->remote_ip != NULL ? conn->remote_ip : "unknown");
+
+ /*
+ * Seed the Pseudo Random Number Generator (PRNG)
+ */
+ ssl_rand_seed(srvr, conn->pool, SSL_RSCTX_CONNECT, "");
+
+ /*
+ * Create a new SSL connection with the configured server SSL context and
+ * attach this to the socket. Additionally we register this attachment
+ * so we can detach later.
+ */
+ if ((ssl = SSL_new(sc->pSSLCtx)) == NULL) {
+ ssl_log(conn->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Unable to create a new SSL connection from the SSL context");
+ ap_ctx_set(fb->ctx, "ssl", NULL);
+ ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+ conn->aborted = 1;
+ return;
+ }
+ SSL_clear(ssl);
+ cpVHostMD5 = ap_md5(conn->pool, (unsigned char *)cpVHostID);
+ if (!SSL_set_session_id_context(ssl, (unsigned char *)cpVHostMD5, strlen(cpVHostMD5))) {
+ ssl_log(conn->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Unable to set session id context to `%s'", cpVHostMD5);
+ ap_ctx_set(fb->ctx, "ssl", NULL);
+ ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+ conn->aborted = 1;
+ return;
+ }
+ SSL_set_app_data(ssl, conn);
+ apctx = ap_ctx_new(conn->pool);
+ ap_ctx_set(apctx, "ssl::request_rec", NULL);
+ ap_ctx_set(apctx, "ssl::verify::depth", AP_CTX_NUM2PTR(0));
+ SSL_set_app_data2(ssl, apctx);
+ SSL_set_fd(ssl, fb->fd);
+ ap_ctx_set(fb->ctx, "ssl", ssl);
+
+ /*
+ * Configure callbacks for SSL connection
+ */
+ SSL_set_tmp_rsa_callback(ssl, ssl_callback_TmpRSA);
+ SSL_set_tmp_dh_callback(ssl, ssl_callback_TmpDH);
+ if (sc->nLogLevel >= SSL_LOG_DEBUG) {
+ BIO_set_callback(SSL_get_rbio(ssl), ssl_io_data_cb);
+ BIO_set_callback_arg(SSL_get_rbio(ssl), ssl);
+ }
+
+ /*
+ * Predefine some client verification results
+ */
+ ap_ctx_set(fb->ctx, "ssl::client::dn", NULL);
+ ap_ctx_set(fb->ctx, "ssl::verify::error", NULL);
+ ap_ctx_set(fb->ctx, "ssl::verify::info", NULL);
+ SSL_set_verify_result(ssl, X509_V_OK);
+
+ /*
+ * We have to manage a I/O timeout ourself, because Apache
+ * does it the first time when reading the request, but we're
+ * working some time before this happens.
+ */
+ ap_ctx_set(ap_global_ctx, "ssl::handshake::timeout", (void *)FALSE);
+ ap_set_callback_and_alarm(ssl_hook_TimeoutConnection, srvr->timeout);
+
+ /*
+ * Now enter the SSL Handshake Phase
+ */
+ while (!SSL_is_init_finished(ssl)) {
+
+ if ((rc = SSL_accept(ssl)) <= 0) {
+
+ if (SSL_get_error(ssl, rc) == SSL_ERROR_ZERO_RETURN) {
+ /*
+ * The case where the connection was closed before any data
+ * was transferred. That's not a real error and can occur
+ * sporadically with some clients.
+ */
+ ssl_log(srvr, SSL_LOG_INFO,
+ "SSL handshake stopped: connection was closed");
+ SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+ SSL_smart_shutdown(ssl);
+ SSL_free(ssl);
+ ap_ctx_set(fb->ctx, "ssl", NULL);
+ ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+ conn->aborted = 1;
+ return;
+ }
+ else if (ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) {
+ /*
+ * The case where OpenSSL has recognized a HTTP request:
+ * This means the client speaks plain HTTP on our HTTPS
+ * port. Hmmmm... At least for this error we can be more friendly
+ * and try to provide him with a HTML error page. We have only one
+ * problem: OpenSSL has already read some bytes from the HTTP
+ * request. So we have to skip the request line manually and
+ * instead provide a faked one in order to continue the internal
+ * Apache processing.
+ *
+ */
+ char ca[2];
+ int rv;
+
+ /* log the situation */
+ ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "SSL handshake failed: HTTP spoken on HTTPS port; "
+ "trying to send HTML error page");
+
+ /* first: skip the remaining bytes of the request line */
+ do {
+ do {
+ rv = read(fb->fd, ca, 1);
+ } while (rv == -1 && errno == EINTR);
+ } while (rv > 0 && ca[0] != '\012' /*LF*/);
+
+ /* second: fake the request line */
+ fb->inbase = ap_palloc(fb->pool, fb->bufsiz);
+ ap_cpystrn((char *)fb->inbase, "GET /mod_ssl:error:HTTP-request HTTP/1.0\r\n",
+ fb->bufsiz);
+ fb->inptr = fb->inbase;
+ fb->incnt = strlen((char *)fb->inptr);
+
+ /* third: kick away the SSL stuff */
+ SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
+ SSL_smart_shutdown(ssl);
+ SSL_free(ssl);
+ ap_ctx_set(fb->ctx, "ssl", NULL);
+
+ /* finally: let Apache go on with processing */
+ return;
+ }
+ else if (ap_ctx_get(ap_global_ctx, "ssl::handshake::timeout") == (void *)TRUE) {
+ ssl_log(srvr, SSL_LOG_ERROR,
+ "SSL handshake timed out (client %s, server %s)",
+ conn->remote_ip != NULL ? conn->remote_ip : "unknown", cpVHostID);
+ SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+ SSL_smart_shutdown(ssl);
+ SSL_free(ssl);
+ ap_ctx_set(fb->ctx, "ssl", NULL);
+ ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+ conn->aborted = 1;
+ return;
+ }
+ else if (SSL_get_error(ssl, rc) == SSL_ERROR_SYSCALL) {
+ if (errno == EINTR)
+ continue;
+ if (errno > 0)
+ ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR|SSL_ADD_ERRNO,
+ "SSL handshake interrupted by system "
+ "[Hint: Stop button pressed in browser?!]");
+ else
+ ssl_log(srvr, SSL_LOG_INFO|SSL_ADD_SSLERR|SSL_ADD_ERRNO,
+ "Spurious SSL handshake interrupt"
+ "[Hint: Usually just one of those OpenSSL confusions!?]");
+ SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+ SSL_smart_shutdown(ssl);
+ SSL_free(ssl);
+ ap_ctx_set(fb->ctx, "ssl", NULL);
+ ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+ conn->aborted = 1;
+ return;
+ }
+ else {
+ /*
+ * Ok, anything else is a fatal error
+ */
+ ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR|SSL_ADD_ERRNO,
+ "SSL handshake failed (server %s, client %s)", cpVHostID,
+ conn->remote_ip != NULL ? conn->remote_ip : "unknown");
+
+ /*
+ * try to gracefully shutdown the connection:
+ * - send an own shutdown message (be gracefully)
+ * - don't wait for peer's shutdown message (deadloop)
+ * - kick away the SSL stuff immediately
+ * - block the socket, so Apache cannot operate any more
+ */
+ SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+ SSL_smart_shutdown(ssl);
+ SSL_free(ssl);
+ ap_ctx_set(fb->ctx, "ssl", NULL);
+ ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+ conn->aborted = 1;
+ return;
+ }
+ }
+
+ /*
+ * Check for failed client authentication
+ */
+ if ( SSL_get_verify_result(ssl) != X509_V_OK
+ || ap_ctx_get(fb->ctx, "ssl::verify::error") != NULL) {
+ cp = (char *)ap_ctx_get(fb->ctx, "ssl::verify::error");
+ ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "SSL client authentication failed: %s",
+ cp != NULL ? cp : "unknown reason");
+ SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+ SSL_smart_shutdown(ssl);
+ SSL_free(ssl);
+ ap_ctx_set(fb->ctx, "ssl", NULL);
+ ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+ conn->aborted = 1;
+ return;
+ }
+
+ /*
+ * Remember the peer certificate's DN
+ */
+ if ((xs = SSL_get_peer_certificate(ssl)) != NULL) {
+ cp = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0);
+ ap_ctx_set(fb->ctx, "ssl::client::dn", ap_pstrdup(conn->pool, cp));
+ free(cp);
+ }
+
+ /*
+ * Make really sure that when a peer certificate
+ * is required we really got one... (be paranoid)
+ */
+ if ( sc->nVerifyClient == SSL_CVERIFY_REQUIRE
+ && ap_ctx_get(fb->ctx, "ssl::client::dn") == NULL) {
+ ssl_log(srvr, SSL_LOG_ERROR,
+ "No acceptable peer certificate available");
+ SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+ SSL_smart_shutdown(ssl);
+ SSL_free(ssl);
+ ap_ctx_set(fb->ctx, "ssl", NULL);
+ ap_bsetflag(fb, B_EOF|B_EOUT, 1);
+ conn->aborted = 1;
+ return;
+ }
+ }
+
+ /*
+ * Remove the timeout handling
+ */
+ ap_set_callback_and_alarm(NULL, 0);
+ ap_ctx_set(ap_global_ctx, "ssl::handshake::timeout", (void *)FALSE);
+
+ /*
+ * Improve I/O throughput by using
+ * OpenSSL's read-ahead functionality
+ * (don't used under Win32, because
+ * there we use select())
+ */
+#ifndef WIN32
+ SSL_set_read_ahead(ssl, TRUE);
+#endif
+
+#ifdef SSL_VENDOR
+ /* Allow vendors to do more things on connection time... */
+ ap_hook_use("ap::mod_ssl::vendor::new_connection",
+ AP_HOOK_SIG2(void,ptr), AP_HOOK_ALL, conn);
+#endif
+
+ return;
+}
+
+/*
+ * Signal handler function for the SSL handshake phase
+ */
+void ssl_hook_TimeoutConnection(int sig)
+{
+ /* we just set a flag for the handshake processing loop */
+ ap_ctx_set(ap_global_ctx, "ssl::handshake::timeout", (void *)TRUE);
+ return;
+}
+
+/*
+ * Close the SSL part of the socket connection
+ * (called immediately _before_ the socket is closed)
+ */
+void ssl_hook_CloseConnection(conn_rec *conn)
+{
+ SSL *ssl;
+ char *cpType;
+
+ ssl = ap_ctx_get(conn->client->ctx, "ssl");
+ if (ssl == NULL)
+ return;
+
+ /*
+ * First make sure that no more data is pending in Apache's BUFF,
+ * because when it's (implicitly) flushed later by the ap_bclose()
+ * calls of Apache it would lead to an I/O error in the browser due
+ * to the fact that the SSL layer was already removed by us.
+ */
+ ap_bflush(conn->client);
+
+ /*
+ * Now close the SSL layer of the connection. We've to take
+ * the TLSv1 standard into account here:
+ *
+ * | 7.2.1. Closure alerts
+ * |
+ * | The client and the server must share knowledge that the connection is
+ * | ending in order to avoid a truncation attack. Either party may
+ * | initiate the exchange of closing messages.
+ * |
+ * | close_notify
+ * | This message notifies the recipient that the sender will not send
+ * | any more messages on this connection. The session becomes
+ * | unresumable if any connection is terminated without proper
+ * | close_notify messages with level equal to warning.
+ * |
+ * | Either party may initiate a close by sending a close_notify alert.
+ * | Any data received after a closure alert is ignored.
+ * |
+ * | Each party is required to send a close_notify alert before closing
+ * | the write side of the connection. It is required that the other party
+ * | respond with a close_notify alert of its own and close down the
+ * | connection immediately, discarding any pending writes. It is not
+ * | required for the initiator of the close to wait for the responding
+ * | close_notify alert before closing the read side of the connection.
+ *
+ * This means we've to send a close notify message, but haven't to wait
+ * for the close notify of the client. Actually we cannot wait for the
+ * close notify of the client because some clients (including Netscape
+ * 4.x) don't send one, so we would hang.
+ */
+
+ /*
+ * exchange close notify messages, but allow the user
+ * to force the type of handshake via SetEnvIf directive
+ */
+ if (ap_ctx_get(conn->client->ctx, "ssl::flag::unclean-shutdown") == PTRUE) {
+ /* perform no close notify handshake at all
+ (violates the SSL/TLS standard!) */
+ SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
+ cpType = "unclean";
+ }
+ else if (ap_ctx_get(conn->client->ctx, "ssl::flag::accurate-shutdown") == PTRUE) {
+ /* send close notify and wait for clients close notify
+ (standard compliant, but usually causes connection hangs) */
+ SSL_set_shutdown(ssl, 0);
+ cpType = "accurate";
+ }
+ else {
+ /* send close notify, but don't wait for clients close notify
+ (standard compliant and safe, so it's the DEFAULT!) */
+ SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+ cpType = "standard";
+ }
+ SSL_smart_shutdown(ssl);
+
+ /* deallocate the SSL connection */
+ SSL_free(ssl);
+ ap_ctx_set(conn->client->ctx, "ssl", NULL);
+
+ /* and finally log the fact that we've closed the connection */
+ ssl_log(conn->server, SSL_LOG_INFO,
+ "Connection to child %d closed with %s shutdown (server %s, client %s)",
+ conn->child_num, cpType, ssl_util_vhostid(conn->pool, conn->server),
+ conn->remote_ip != NULL ? conn->remote_ip : "unknown");
+ return;
+}
+
+/*
+ * Post Read Request Handler
+ */
+int ssl_hook_ReadReq(request_rec *r)
+{
+ SSL *ssl;
+ ap_ctx *apctx;
+
+ /*
+ * Get the SSL connection structure and perform the
+ * delayed interlinking from SSL back to request_rec
+ */
+ ssl = ap_ctx_get(r->connection->client->ctx, "ssl");
+ if (ssl != NULL) {
+ apctx = SSL_get_app_data2(ssl);
+ ap_ctx_set(apctx, "ssl::request_rec", r);
+ }
+
+ /*
+ * Force the mod_ssl content handler when URL indicates this
+ */
+ if (strEQn(r->uri, "/mod_ssl:", 9))
+ r->handler = "mod_ssl:content-handler";
+ if (ssl != NULL) {
+ ap_ctx_set(r->ctx, "ap::http::method", "https");
+ ap_ctx_set(r->ctx, "ap::default::port", "443");
+ }
+ else {
+ ap_ctx_set(r->ctx, "ap::http::method", NULL);
+ ap_ctx_set(r->ctx, "ap::default::port", NULL);
+ }
+ return DECLINED;
+}
+
+/*
+ * URL Translation Handler
+ */
+int ssl_hook_Translate(request_rec *r)
+{
+ if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL)
+ return DECLINED;
+
+ /*
+ * Log information about incoming HTTPS requests
+ */
+ if (ap_is_initial_req(r))
+ ssl_log(r->server, SSL_LOG_INFO,
+ "%s HTTPS request received for child %d (server %s)",
+ r->connection->keepalives <= 0 ?
+ "Initial (No.1)" :
+ ap_psprintf(r->pool, "Subsequent (No.%d)",
+ r->connection->keepalives+1),
+ r->connection->child_num,
+ ssl_util_vhostid(r->pool, r->server));
+
+ /*
+ * Move SetEnvIf information from request_rec to conn_rec/BUFF
+ * to allow the close connection handler to use them.
+ */
+ if (ap_table_get(r->subprocess_env, "ssl-unclean-shutdown") != NULL)
+ ap_ctx_set(r->connection->client->ctx, "ssl::flag::unclean-shutdown", PTRUE);
+ else
+ ap_ctx_set(r->connection->client->ctx, "ssl::flag::unclean-shutdown", PFALSE);
+ if (ap_table_get(r->subprocess_env, "ssl-accurate-shutdown") != NULL)
+ ap_ctx_set(r->connection->client->ctx, "ssl::flag::accurate-shutdown", PTRUE);
+ else
+ ap_ctx_set(r->connection->client->ctx, "ssl::flag::accurate-shutdown", PFALSE);
+
+ return DECLINED;
+}
+
+/*
+ * Content Handler
+ */
+int ssl_hook_Handler(request_rec *r)
+{
+ int port;
+ char *thisport;
+ char *thisurl;
+
+ if (strNEn(r->uri, "/mod_ssl:", 9))
+ return DECLINED;
+
+ if (strEQ(r->uri, "/mod_ssl:error:HTTP-request")) {
+ thisport = "";
+ port = ap_get_server_port(r);
+ if (!ap_is_default_port(port, r))
+ thisport = ap_psprintf(r->pool, ":%u", port);
+ thisurl = ap_psprintf(r->pool, "https://%s%s/",
+ ap_get_server_name(r), thisport);
+
+ ap_table_setn(r->notes, "error-notes", ap_psprintf(r->pool,
+ "Reason: You're speaking plain HTTP to an SSL-enabled server port.<BR>\n"
+ "Instead use the HTTPS scheme to access this URL, please.<BR>\n"
+ "<BLOCKQUOTE>Hint: <A HREF=\"%s\"><B>%s</B></A></BLOCKQUOTE>",
+ thisurl, thisurl));
+ }
+
+ return HTTP_BAD_REQUEST;
+}
+
+/*
+ * Access Handler
+ */
+int ssl_hook_Access(request_rec *r)
+{
+ SSLDirConfigRec *dc;
+ SSLSrvConfigRec *sc;
+ SSL *ssl;
+ SSL_CTX *ctx = NULL;
+ array_header *apRequirement;
+ ssl_require_t *pRequirements;
+ ssl_require_t *pRequirement;
+ char *cp;
+ int ok;
+ int i;
+ BOOL renegotiate;
+ BOOL renegotiate_quick;
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+ BOOL reconfigured_locations;
+ STACK_OF(X509_NAME) *skCAList;
+ char *cpCAPath;
+ char *cpCAFile;
+#endif
+ X509 *cert;
+ STACK_OF(X509) *certstack;
+ X509_STORE *certstore;
+ X509_STORE_CTX certstorectx;
+ int depth;
+ STACK_OF(SSL_CIPHER) *skCipherOld;
+ STACK_OF(SSL_CIPHER) *skCipher;
+ SSL_CIPHER *pCipher;
+ ap_ctx *apctx;
+ int nVerifyOld;
+ int nVerify;
+ int n;
+ void *vp;
+ int rc;
+
+ dc = myDirConfig(r);
+ sc = mySrvConfig(r->server);
+ ssl = ap_ctx_get(r->connection->client->ctx, "ssl");
+ if (ssl != NULL)
+ ctx = SSL_get_SSL_CTX(ssl);
+
+ /*
+ * Support for SSLRequireSSL directive
+ */
+ if (dc->bSSLRequired && ssl == NULL) {
+ ap_log_reason("SSL connection required", r->filename, r);
+ /* remember forbidden access for strict require option */
+ ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1);
+ return FORBIDDEN;
+ }
+
+ /*
+ * Check to see if SSL protocol is on
+ */
+ if (!sc->bEnabled)
+ return DECLINED;
+ if (ssl == NULL)
+ return DECLINED;
+
+ /*
+ * Support for per-directory reconfigured SSL connection parameters.
+ *
+ * This is implemented by forcing an SSL renegotiation with the
+ * reconfigured parameter suite. But Apache's internal API processing
+ * makes our life very hard here, because when internal sub-requests occur
+ * we nevertheless should avoid multiple unnecessary SSL handshakes (they
+ * require extra network I/O and especially time to perform).
+ *
+ * But the optimization for filtering out the unnecessary handshakes isn't
+ * obvious and trivial. Especially because while Apache is in its
+ * sub-request processing the client could force additional handshakes,
+ * too. And these take place perhaps without our notice. So the only
+ * possibility is to explicitly _ask_ OpenSSL whether the renegotiation
+ * has to be performed or not. It has to performed when some parameters
+ * which were previously known (by us) are not those we've now
+ * reconfigured (as known by OpenSSL) or (in optimized way) at least when
+ * the reconfigured parameter suite is stronger (more restrictions) than
+ * the currently active one.
+ */
+ renegotiate = FALSE;
+ renegotiate_quick = FALSE;
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+ reconfigured_locations = FALSE;
+#endif
+
+ /*
+ * Override of SSLCipherSuite
+ *
+ * We provide two options here:
+ *
+ * o The paranoid and default approach where we force a renegotiation when
+ * the cipher suite changed in _any_ way (which is straight-forward but
+ * often forces renegotiations too often and is perhaps not what the
+ * user actually wanted).
+ *
+ * o The optimized and still secure way where we force a renegotiation
+ * only if the currently active cipher is no longer contained in the
+ * reconfigured/new cipher suite. Any other changes are not important
+ * because it's the servers choice to select a cipher from the ones the
+ * client supports. So as long as the current cipher is still in the new
+ * cipher suite we're happy. Because we can assume we would have
+ * selected it again even when other (better) ciphers exists now in the
+ * new cipher suite. This approach is fine because the user explicitly
+ * has to enable this via ``SSLOptions +OptRenegotiate''. So we do no
+ * implicit optimizations.
+ */
+ if (dc->szCipherSuite != NULL) {
+ /* remember old state */
+ pCipher = NULL;
+ skCipherOld = NULL;
+ if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE)
+ pCipher = SSL_get_current_cipher(ssl);
+ else {
+ skCipherOld = SSL_get_ciphers(ssl);
+ if (skCipherOld != NULL)
+ skCipherOld = sk_SSL_CIPHER_dup(skCipherOld);
+ }
+ /* configure new state */
+ if (!SSL_set_cipher_list(ssl, dc->szCipherSuite)) {
+ ssl_log(r->server, SSL_LOG_WARN|SSL_ADD_SSLERR,
+ "Unable to reconfigure (per-directory) permitted SSL ciphers");
+ if (skCipherOld != NULL)
+ sk_SSL_CIPHER_free(skCipherOld);
+ return FORBIDDEN;
+ }
+ /* determine whether a renegotiation has to be forced */
+ skCipher = SSL_get_ciphers(ssl);
+ if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) {
+ /* optimized way */
+ if ((pCipher == NULL && skCipher != NULL) ||
+ (pCipher != NULL && skCipher == NULL) )
+ renegotiate = TRUE;
+ else if (pCipher != NULL && skCipher != NULL
+ && sk_SSL_CIPHER_find(skCipher, pCipher) < 0) {
+ renegotiate = TRUE;
+ }
+ }
+ else {
+ /* paranoid way */
+ if ((skCipherOld == NULL && skCipher != NULL) ||
+ (skCipherOld != NULL && skCipher == NULL) )
+ renegotiate = TRUE;
+ else if (skCipherOld != NULL && skCipher != NULL) {
+ for (n = 0; !renegotiate && n < sk_SSL_CIPHER_num(skCipher); n++) {
+ if (sk_SSL_CIPHER_find(skCipherOld, sk_SSL_CIPHER_value(skCipher, n)) < 0)
+ renegotiate = TRUE;
+ }
+ for (n = 0; !renegotiate && n < sk_SSL_CIPHER_num(skCipherOld); n++) {
+ if (sk_SSL_CIPHER_find(skCipher, sk_SSL_CIPHER_value(skCipherOld, n)) < 0)
+ renegotiate = TRUE;
+ }
+ }
+ }
+ /* cleanup */
+ if (skCipherOld != NULL)
+ sk_SSL_CIPHER_free(skCipherOld);
+ /* tracing */
+ if (renegotiate)
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "Reconfigured cipher suite will force renegotiation");
+ }
+
+ /*
+ * override of SSLVerifyDepth
+ *
+ * The depth checks are handled by us manually inside the verify callback
+ * function and not by OpenSSL internally (and our function is aware of
+ * both the per-server and per-directory contexts). So we cannot ask
+ * OpenSSL about the currently verify depth. Instead we remember it in our
+ * ap_ctx attached to the SSL* of OpenSSL. We've to force the
+ * renegotiation if the reconfigured/new verify depth is less than the
+ * currently active/remembered verify depth (because this means more
+ * restriction on the certificate chain).
+ */
+ if (dc->nVerifyDepth != UNSET) {
+ apctx = SSL_get_app_data2(ssl);
+ if ((vp = ap_ctx_get(apctx, "ssl::verify::depth")) != NULL)
+ n = (int)AP_CTX_PTR2NUM(vp);
+ else
+ n = sc->nVerifyDepth;
+ ap_ctx_set(apctx, "ssl::verify::depth",
+ AP_CTX_NUM2PTR(dc->nVerifyDepth));
+ /* determine whether a renegotiation has to be forced */
+ if (dc->nVerifyDepth < n) {
+ renegotiate = TRUE;
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "Reduced client verification depth will force renegotiation");
+ }
+ }
+
+ /*
+ * override of SSLVerifyClient
+ *
+ * We force a renegotiation if the reconfigured/new verify type is
+ * stronger than the currently active verify type.
+ *
+ * The order is: none << optional_no_ca << optional << require
+ *
+ * Additionally the following optimization is possible here: When the
+ * currently active verify type is "none" but a client certificate is
+ * already known/present, it's enough to manually force a client
+ * verification but at least skip the I/O-intensive renegotation
+ * handshake.
+ */
+ if (dc->nVerifyClient != SSL_CVERIFY_UNSET) {
+ /* remember old state */
+ nVerifyOld = SSL_get_verify_mode(ssl);
+ /* configure new state */
+ nVerify = SSL_VERIFY_NONE;
+ if (dc->nVerifyClient == SSL_CVERIFY_REQUIRE)
+ nVerify |= SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ if ( (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL)
+ || (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) )
+ nVerify |= SSL_VERIFY_PEER;
+ SSL_set_verify(ssl, nVerify, ssl_callback_SSLVerify);
+ SSL_set_verify_result(ssl, X509_V_OK);
+ /* determine whether we've to force a renegotiation */
+ if (nVerify != nVerifyOld) {
+ if ( ( (nVerifyOld == SSL_VERIFY_NONE)
+ && (nVerify != SSL_VERIFY_NONE))
+ || ( !(nVerifyOld & SSL_VERIFY_PEER)
+ && (nVerify & SSL_VERIFY_PEER))
+ || ( !(nVerifyOld & (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT))
+ && (nVerify & (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT)))) {
+ renegotiate = TRUE;
+ /* optimization */
+ if ( dc->nOptions & SSL_OPT_OPTRENEGOTIATE
+ && nVerifyOld == SSL_VERIFY_NONE
+ && SSL_get_peer_certificate(ssl) != NULL)
+ renegotiate_quick = TRUE;
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "Changed client verification type will force %srenegotiation",
+ renegotiate_quick ? "quick " : "");
+ }
+ }
+ }
+
+ /*
+ * override SSLCACertificateFile & SSLCACertificatePath
+ * This is tagged experimental because it has to use an ugly kludge: We
+ * have to change the locations inside the SSL_CTX* (per-server global)
+ * instead inside SSL* (per-connection local) and reconfigure it to the
+ * old values later. That's problematic at least for the threaded process
+ * model of Apache under Win32 or when an error occurs. But unless
+ * OpenSSL provides a SSL_load_verify_locations() function we've no other
+ * chance to provide this functionality...
+ */
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+ if ( ( dc->szCACertificateFile != NULL
+ && ( sc->szCACertificateFile == NULL
+ || ( sc->szCACertificateFile != NULL
+ && strNE(dc->szCACertificateFile, sc->szCACertificateFile))))
+ || ( dc->szCACertificatePath != NULL
+ && ( sc->szCACertificatePath == NULL
+ || ( sc->szCACertificatePath != NULL
+ && strNE(dc->szCACertificatePath, sc->szCACertificatePath)))) ) {
+ cpCAFile = dc->szCACertificateFile != NULL ?
+ dc->szCACertificateFile : sc->szCACertificateFile;
+ cpCAPath = dc->szCACertificatePath != NULL ?
+ dc->szCACertificatePath : sc->szCACertificatePath;
+ /*
+ FIXME: This should be...
+ if (!SSL_load_verify_locations(ssl, cpCAFile, cpCAPath)) {
+ ...but OpenSSL still doesn't provide this!
+ */
+ if (!SSL_CTX_load_verify_locations(ctx, cpCAFile, cpCAPath)) {
+ ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Unable to reconfigure verify locations "
+ "for client authentication");
+ return FORBIDDEN;
+ }
+ if ((skCAList = ssl_init_FindCAList(r->server, r->pool,
+ cpCAFile, cpCAPath)) == NULL) {
+ ssl_log(r->server, SSL_LOG_ERROR,
+ "Unable to determine list of available "
+ "CA certificates for client authentication");
+ return FORBIDDEN;
+ }
+ SSL_set_client_CA_list(ssl, skCAList);
+ renegotiate = TRUE;
+ reconfigured_locations = TRUE;
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "Changed client verification locations will force renegotiation");
+ }
+#endif /* SSL_EXPERIMENTAL_PERDIRCA */
+
+#ifdef SSL_CONSERVATIVE
+ /*
+ * SSL renegotiations in conjunction with HTTP
+ * requests using the POST method are not supported.
+ */
+ if (renegotiate && r->method_number == M_POST) {
+ ssl_log(r->server, SSL_LOG_ERROR,
+ "SSL Re-negotiation in conjunction with POST method not supported!");
+ ssl_log(r->server, SSL_LOG_INFO,
+ "You have to compile without -DSSL_CONSERVATIVE to enabled support for this.");
+ return METHOD_NOT_ALLOWED;
+ }
+#endif /* SSL_CONSERVATIVE */
+
+ /*
+ * now do the renegotiation if anything was actually reconfigured
+ */
+ if (renegotiate) {
+ /*
+ * Now we force the SSL renegotation by sending the Hello Request
+ * message to the client. Here we have to do a workaround: Actually
+ * OpenSSL returns immediately after sending the Hello Request (the
+ * intent AFAIK is because the SSL/TLS protocol says it's not a must
+ * that the client replies to a Hello Request). But because we insist
+ * on a reply (anything else is an error for us) we have to go to the
+ * ACCEPT state manually. Using SSL_set_accept_state() doesn't work
+ * here because it resets too much of the connection. So we set the
+ * state explicitly and continue the handshake manually.
+ */
+ ssl_log(r->server, SSL_LOG_INFO, "Requesting connection re-negotiation");
+ if (renegotiate_quick) {
+ /* perform just a manual re-verification of the peer */
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "Performing quick renegotiation: just re-verifying the peer");
+ certstore = SSL_CTX_get_cert_store(ctx);
+ if (certstore == NULL) {
+ ssl_log(r->server, SSL_LOG_ERROR, "Cannot find certificate storage");
+ return FORBIDDEN;
+ }
+ certstack = SSL_get_peer_cert_chain(ssl);
+ if (certstack == NULL || sk_X509_num(certstack) == 0) {
+ ssl_log(r->server, SSL_LOG_ERROR, "Cannot find peer certificate chain");
+ return FORBIDDEN;
+ }
+ cert = sk_X509_value(certstack, 0);
+ X509_STORE_CTX_init(&certstorectx, certstore, cert, certstack);
+ depth = SSL_get_verify_depth(ssl);
+ if (depth >= 0)
+ X509_STORE_CTX_set_depth(&certstorectx, depth);
+ X509_STORE_CTX_set_ex_data(&certstorectx,
+ SSL_get_ex_data_X509_STORE_CTX_idx(), (char *)ssl);
+ if (!X509_verify_cert(&certstorectx))
+ ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Re-negotiation verification step failed");
+ SSL_set_verify_result(ssl, certstorectx.error);
+ X509_STORE_CTX_cleanup(&certstorectx);
+ }
+ else {
+ /* do a full renegotiation */
+ ssl_log(r->server, SSL_LOG_TRACE,
+ "Performing full renegotiation: complete handshake protocol");
+ if (r->main != NULL)
+ SSL_set_session_id_context(ssl, (unsigned char *)&(r->main), sizeof(r->main));
+ else
+ SSL_set_session_id_context(ssl, (unsigned char *)&r, sizeof(r));
+#ifndef SSL_CONSERVATIVE
+ ssl_io_suck(r, ssl);
+#endif
+ SSL_renegotiate(ssl);
+ SSL_do_handshake(ssl);
+ if (SSL_get_state(ssl) != SSL_ST_OK) {
+ ssl_log(r->server, SSL_LOG_ERROR, "Re-negotiation request failed");
+ return FORBIDDEN;
+ }
+ ssl_log(r->server, SSL_LOG_INFO, "Awaiting re-negotiation handshake");
+ SSL_set_state(ssl, SSL_ST_ACCEPT);
+ SSL_do_handshake(ssl);
+ if (SSL_get_state(ssl) != SSL_ST_OK) {
+ ssl_log(r->server, SSL_LOG_ERROR,
+ "Re-negotiation handshake failed: Not accepted by client!?");
+ return FORBIDDEN;
+ }
+ }
+
+ /*
+ * Remember the peer certificate's DN
+ */
+ if ((cert = SSL_get_peer_certificate(ssl)) != NULL) {
+ cp = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
+ ap_ctx_set(r->connection->client->ctx, "ssl::client::dn",
+ ap_pstrdup(r->connection->pool, cp));
+ free(cp);
+ }
+
+ /*
+ * Finally check for acceptable renegotiation results
+ */
+ if (dc->nVerifyClient != SSL_CVERIFY_NONE) {
+ if ( dc->nVerifyClient == SSL_CVERIFY_REQUIRE
+ && SSL_get_verify_result(ssl) != X509_V_OK ) {
+ ssl_log(r->server, SSL_LOG_ERROR,
+ "Re-negotiation handshake failed: Client verification failed");
+ return FORBIDDEN;
+ }
+ if ( dc->nVerifyClient == SSL_CVERIFY_REQUIRE
+ && SSL_get_peer_certificate(ssl) == NULL ) {
+ ssl_log(r->server, SSL_LOG_ERROR,
+ "Re-negotiation handshake failed: Client certificate missing");
+ return FORBIDDEN;
+ }
+ }
+ }
+
+ /*
+ * Under old OpenSSL we had to change the X509_STORE inside the
+ * SSL_CTX instead inside the SSL structure, so we have to reconfigure it
+ * to the old values. This should be changed with forthcoming OpenSSL
+ * versions when better functionality is avaiable.
+ */
+#ifdef SSL_EXPERIMENTAL_PERDIRCA
+ if (renegotiate && reconfigured_locations) {
+ if (!SSL_CTX_load_verify_locations(ctx,
+ sc->szCACertificateFile, sc->szCACertificatePath)) {
+ ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Unable to reconfigure verify locations "
+ "to per-server configuration parameters");
+ return FORBIDDEN;
+ }
+ }
+#endif /* SSL_EXPERIMENTAL_PERDIRCA */
+
+ /*
+ * Check SSLRequire boolean expressions
+ */
+ apRequirement = dc->aRequirement;
+ pRequirements = (ssl_require_t *)apRequirement->elts;
+ for (i = 0; i < apRequirement->nelts; i++) {
+ pRequirement = &pRequirements[i];
+ ok = ssl_expr_exec(r, pRequirement->mpExpr);
+ if (ok < 0) {
+ cp = ap_psprintf(r->pool, "Failed to execute SSL requirement expression: %s",
+ ssl_expr_get_error());
+ ap_log_reason(cp, r->filename, r);
+ /* remember forbidden access for strict require option */
+ ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1);
+ return FORBIDDEN;
+ }
+ if (ok != 1) {
+ ssl_log(r->server, SSL_LOG_INFO,
+ "Access to %s denied for %s (requirement expression not fulfilled)",
+ r->filename, r->connection->remote_ip);
+ ssl_log(r->server, SSL_LOG_INFO,
+ "Failed expression: %s", pRequirement->cpExpr);
+ ap_log_reason("SSL requirement expression not fulfilled "
+ "(see SSL logfile for more details)", r->filename, r);
+ /* remember forbidden access for strict require option */
+ ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1);
+ return FORBIDDEN;
+ }
+ }
+
+ /*
+ * Else access is granted from our point of view (except vendor
+ * handlers override). But we have to return DECLINED here instead
+ * of OK, because mod_auth and other modules still might want to
+ * deny access.
+ */
+ rc = DECLINED;
+#ifdef SSL_VENDOR
+ ap_hook_use("ap::mod_ssl::vendor::access_handler",
+ AP_HOOK_SIG2(int,ptr), AP_HOOK_DECLINE(DECLINED),
+ &rc, r);
+#endif
+ return rc;
+}
+
+/*
+ * Auth Handler:
+ * Fake a Basic authentication from the X509 client certificate.
+ *
+ * This must be run fairly early on to prevent a real authentication from
+ * occuring, in particular it must be run before anything else that
+ * authenticates a user. This means that the Module statement for this
+ * module should be LAST in the Configuration file.
+ */
+int ssl_hook_Auth(request_rec *r)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(r->server);
+ SSLDirConfigRec *dc = myDirConfig(r);
+ char b1[MAX_STRING_LEN], b2[MAX_STRING_LEN];
+ char *clientdn;
+ const char *cpAL;
+ const char *cpUN;
+ const char *cpPW;
+
+ /*
+ * Additionally forbid access (again)
+ * when strict require option is used.
+ */
+ if ( (dc->nOptions & SSL_OPT_STRICTREQUIRE)
+ && (ap_table_get(r->notes, "ssl-access-forbidden") != NULL))
+ return FORBIDDEN;
+
+ /*
+ * Make sure the user is not able to fake the client certificate
+ * based authentication by just entering an X.509 Subject DN
+ * ("/XX=YYY/XX=YYY/..") as the username and "password" as the
+ * password.
+ */
+ if ((cpAL = ap_table_get(r->headers_in, "Authorization")) != NULL) {
+ if (strcEQ(ap_getword(r->pool, &cpAL, ' '), "Basic")) {
+ while (*cpAL == ' ' || *cpAL == '\t')
+ cpAL++;
+ cpAL = ap_pbase64decode(r->pool, cpAL);
+ cpUN = ap_getword_nulls(r->pool, &cpAL, ':');
+ cpPW = cpAL;
+ if (cpUN[0] == '/' && strEQ(cpPW, "password"))
+ return FORBIDDEN;
+ }
+ }
+
+ /*
+ * We decline operation in various situations...
+ */
+ if (!sc->bEnabled)
+ return DECLINED;
+ if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL)
+ return DECLINED;
+ if (!(dc->nOptions & SSL_OPT_FAKEBASICAUTH))
+ return DECLINED;
+ if (r->connection->user)
+ return DECLINED;
+ if ((clientdn = (char *)ap_ctx_get(r->connection->client->ctx, "ssl::client::dn")) == NULL)
+ return DECLINED;
+
+ /*
+ * Fake a password - which one would be immaterial, as, it seems, an empty
+ * password in the users file would match ALL incoming passwords, if only
+ * we were using the standard crypt library routine. Unfortunately, OpenSSL
+ * "fixes" a "bug" in crypt and thus prevents blank passwords from
+ * working. (IMHO what they really fix is a bug in the users of the code
+ * - failing to program correctly for shadow passwords). We need,
+ * therefore, to provide a password. This password can be matched by
+ * adding the string "xxj31ZMTZzkVA" as the password in the user file.
+ * This is just the crypted variant of the word "password" ;-)
+ */
+ ap_snprintf(b1, sizeof(b1), "%s:password", clientdn);
+ ssl_util_uuencode(b2, b1, FALSE);
+ ap_snprintf(b1, sizeof(b1), "Basic %s", b2);
+ ap_table_set(r->headers_in, "Authorization", b1);
+ ssl_log(r->server, SSL_LOG_INFO,
+ "Faking HTTP Basic Auth header: \"Authorization: %s\"", b1);
+
+ return DECLINED;
+}
+
+int ssl_hook_UserCheck(request_rec *r)
+{
+ SSLDirConfigRec *dc = myDirConfig(r);
+
+ /*
+ * Additionally forbid access (again)
+ * when strict require option is used.
+ */
+ if ( (dc->nOptions & SSL_OPT_STRICTREQUIRE)
+ && (ap_table_get(r->notes, "ssl-access-forbidden") != NULL))
+ return FORBIDDEN;
+
+ return DECLINED;
+}
+
+/*
+ * Fixup Handler
+ */
+
+static const char *ssl_hook_Fixup_vars[] = {
+ "SSL_VERSION_INTERFACE",
+ "SSL_VERSION_LIBRARY",
+ "SSL_PROTOCOL",
+ "SSL_CIPHER",
+ "SSL_CIPHER_EXPORT",
+ "SSL_CIPHER_USEKEYSIZE",
+ "SSL_CIPHER_ALGKEYSIZE",
+ "SSL_CLIENT_VERIFY",
+ "SSL_CLIENT_M_VERSION",
+ "SSL_CLIENT_M_SERIAL",
+ "SSL_CLIENT_V_START",
+ "SSL_CLIENT_V_END",
+ "SSL_CLIENT_S_DN",
+ "SSL_CLIENT_S_DN_C",
+ "SSL_CLIENT_S_DN_ST",
+ "SSL_CLIENT_S_DN_L",
+ "SSL_CLIENT_S_DN_O",
+ "SSL_CLIENT_S_DN_OU",
+ "SSL_CLIENT_S_DN_CN",
+ "SSL_CLIENT_S_DN_T",
+ "SSL_CLIENT_S_DN_I",
+ "SSL_CLIENT_S_DN_G",
+ "SSL_CLIENT_S_DN_S",
+ "SSL_CLIENT_S_DN_D",
+ "SSL_CLIENT_S_DN_UID",
+ "SSL_CLIENT_S_DN_Email",
+ "SSL_CLIENT_I_DN",
+ "SSL_CLIENT_I_DN_C",
+ "SSL_CLIENT_I_DN_ST",
+ "SSL_CLIENT_I_DN_L",
+ "SSL_CLIENT_I_DN_O",
+ "SSL_CLIENT_I_DN_OU",
+ "SSL_CLIENT_I_DN_CN",
+ "SSL_CLIENT_I_DN_T",
+ "SSL_CLIENT_I_DN_I",
+ "SSL_CLIENT_I_DN_G",
+ "SSL_CLIENT_I_DN_S",
+ "SSL_CLIENT_I_DN_D",
+ "SSL_CLIENT_I_DN_UID",
+ "SSL_CLIENT_I_DN_Email",
+ "SSL_CLIENT_A_KEY",
+ "SSL_CLIENT_A_SIG",
+ "SSL_SERVER_M_VERSION",
+ "SSL_SERVER_M_SERIAL",
+ "SSL_SERVER_V_START",
+ "SSL_SERVER_V_END",
+ "SSL_SERVER_S_DN",
+ "SSL_SERVER_S_DN_C",
+ "SSL_SERVER_S_DN_ST",
+ "SSL_SERVER_S_DN_L",
+ "SSL_SERVER_S_DN_O",
+ "SSL_SERVER_S_DN_OU",
+ "SSL_SERVER_S_DN_CN",
+ "SSL_SERVER_S_DN_T",
+ "SSL_SERVER_S_DN_I",
+ "SSL_SERVER_S_DN_G",
+ "SSL_SERVER_S_DN_S",
+ "SSL_SERVER_S_DN_D",
+ "SSL_SERVER_S_DN_UID",
+ "SSL_SERVER_S_DN_Email",
+ "SSL_SERVER_I_DN",
+ "SSL_SERVER_I_DN_C",
+ "SSL_SERVER_I_DN_ST",
+ "SSL_SERVER_I_DN_L",
+ "SSL_SERVER_I_DN_O",
+ "SSL_SERVER_I_DN_OU",
+ "SSL_SERVER_I_DN_CN",
+ "SSL_SERVER_I_DN_T",
+ "SSL_SERVER_I_DN_I",
+ "SSL_SERVER_I_DN_G",
+ "SSL_SERVER_I_DN_S",
+ "SSL_SERVER_I_DN_D",
+ "SSL_SERVER_I_DN_UID",
+ "SSL_SERVER_I_DN_Email",
+ "SSL_SERVER_A_KEY",
+ "SSL_SERVER_A_SIG",
+ "SSL_SESSION_ID",
+ NULL
+};
+
+int ssl_hook_Fixup(request_rec *r)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(r->server);
+ SSLDirConfigRec *dc = myDirConfig(r);
+ table *e = r->subprocess_env;
+ char *var;
+ char *val;
+ STACK_OF(X509) *sk;
+ SSL *ssl;
+ int i;
+
+ /*
+ * Check to see if SSL is on
+ */
+ if (!sc->bEnabled)
+ return DECLINED;
+ if ((ssl = ap_ctx_get(r->connection->client->ctx, "ssl")) == NULL)
+ return DECLINED;
+
+ /*
+ * Annotate the SSI/CGI environment with standard SSL information
+ */
+ /* the always present HTTPS (=HTTP over SSL) flag! */
+ ap_table_set(e, "HTTPS", "on");
+ /* standard SSL environment variables */
+ if (dc->nOptions & SSL_OPT_STDENVVARS) {
+ for (i = 0; ssl_hook_Fixup_vars[i] != NULL; i++) {
+ var = (char *)ssl_hook_Fixup_vars[i];
+ val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
+ if (!strIsEmpty(val))
+ ap_table_set(e, var, val);
+ }
+ }
+
+ /*
+ * On-demand bloat up the SSI/CGI environment with certificate data
+ */
+ if (dc->nOptions & SSL_OPT_EXPORTCERTDATA) {
+ val = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_SERVER_CERT");
+ ap_table_set(e, "SSL_SERVER_CERT", val);
+ val = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_CERT");
+ ap_table_set(e, "SSL_CLIENT_CERT", val);
+ if ((sk = SSL_get_peer_cert_chain(ssl)) != NULL) {
+ for (i = 0; i < sk_X509_num(sk); i++) {
+ var = ap_psprintf(r->pool, "SSL_CLIENT_CERT_CHAIN_%d", i);
+ val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
+ if (val != NULL)
+ ap_table_set(e, var, val);
+ }
+ }
+ }
+
+ /*
+ * On-demand bloat up the SSI/CGI environment with compat variables
+ */
+#ifdef SSL_COMPAT
+ if (dc->nOptions & SSL_OPT_COMPATENVVARS)
+ ssl_compat_variables(r);
+#endif
+
+ return DECLINED;
+}
+
+/* _________________________________________________________________
+**
+** OpenSSL Callback Functions
+** _________________________________________________________________
+*/
+
+/*
+ * Handle out temporary RSA private keys on demand
+ *
+ * The background of this as the TLSv1 standard explains it:
+ *
+ * | D.1. Temporary RSA keys
+ * |
+ * | US Export restrictions limit RSA keys used for encryption to 512
+ * | bits, but do not place any limit on lengths of RSA keys used for
+ * | signing operations. Certificates often need to be larger than 512
+ * | bits, since 512-bit RSA keys are not secure enough for high-value
+ * | transactions or for applications requiring long-term security. Some
+ * | certificates are also designated signing-only, in which case they
+ * | cannot be used for key exchange.
+ * |
+ * | When the public key in the certificate cannot be used for encryption,
+ * | the server signs a temporary RSA key, which is then exchanged. In
+ * | exportable applications, the temporary RSA key should be the maximum
+ * | allowable length (i.e., 512 bits). Because 512-bit RSA keys are
+ * | relatively insecure, they should be changed often. For typical
+ * | electronic commerce applications, it is suggested that keys be
+ * | changed daily or every 500 transactions, and more often if possible.
+ * | Note that while it is acceptable to use the same temporary key for
+ * | multiple transactions, it must be signed each time it is used.
+ * |
+ * | RSA key generation is a time-consuming process. In many cases, a
+ * | low-priority process can be assigned the task of key generation.
+ * | Whenever a new key is completed, the existing temporary key can be
+ * | replaced with the new one.
+ *
+ * So we generated 512 and 1024 bit temporary keys on startup
+ * which we now just handle out on demand....
+ */
+RSA *ssl_callback_TmpRSA(SSL *pSSL, int nExport, int nKeyLen)
+{
+ SSLModConfigRec *mc = myModConfig();
+ RSA *rsa;
+
+ rsa = NULL;
+ if (nExport) {
+ /* It's because an export cipher is used */
+ if (nKeyLen == 512)
+ rsa = (RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA512];
+ else if (nKeyLen == 1024)
+ rsa = (RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA1024];
+ else
+ /* it's too expensive to generate on-the-fly, so keep 1024bit */
+ rsa = (RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA1024];
+ }
+ else {
+ /* It's because a sign-only certificate situation exists */
+ rsa = (RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA1024];
+ }
+ return rsa;
+}
+
+/*
+ * Handle out the already generated DH parameters...
+ */
+DH *ssl_callback_TmpDH(SSL *pSSL, int nExport, int nKeyLen)
+{
+ SSLModConfigRec *mc = myModConfig();
+ DH *dh;
+
+ dh = NULL;
+ if (nExport) {
+ /* It's because an export cipher is used */
+ if (nKeyLen == 512)
+ dh = (DH *)mc->pTmpKeys[SSL_TKPIDX_DH512];
+ else if (nKeyLen == 1024)
+ dh = (DH *)mc->pTmpKeys[SSL_TKPIDX_DH1024];
+ else
+ /* it's too expensive to generate on-the-fly, so keep 1024bit */
+ dh = (DH *)mc->pTmpKeys[SSL_TKPIDX_DH1024];
+ }
+ else {
+ /* It's because a sign-only certificate situation exists */
+ dh = (DH *)mc->pTmpKeys[SSL_TKPIDX_DH1024];
+ }
+ return dh;
+}
+
+/*
+ * This OpenSSL callback function is called when OpenSSL
+ * does client authentication and verifies the certificate chain.
+ */
+int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
+{
+ SSL *ssl;
+ conn_rec *conn;
+ server_rec *s;
+ request_rec *r;
+ SSLSrvConfigRec *sc;
+ SSLDirConfigRec *dc;
+ ap_ctx *actx;
+ X509 *xs;
+ int errnum;
+ int errdepth;
+ char *cp;
+ char *cp2;
+ int depth;
+ int verify;
+
+ /*
+ * Get Apache context back through OpenSSL context
+ */
+ ssl = (SSL *)X509_STORE_CTX_get_app_data(ctx);
+ conn = (conn_rec *)SSL_get_app_data(ssl);
+ actx = (ap_ctx *)SSL_get_app_data2(ssl);
+ r = (request_rec *)ap_ctx_get(actx, "ssl::request_rec");
+ s = conn->server;
+ sc = mySrvConfig(s);
+ dc = (r != NULL ? myDirConfig(r) : NULL);
+
+ /*
+ * Get verify ingredients
+ */
+ xs = X509_STORE_CTX_get_current_cert(ctx);
+ errnum = X509_STORE_CTX_get_error(ctx);
+ errdepth = X509_STORE_CTX_get_error_depth(ctx);
+
+ /*
+ * Log verification information
+ */
+ cp = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0);
+ cp2 = X509_NAME_oneline(X509_get_issuer_name(xs), NULL, 0);
+ ssl_log(s, SSL_LOG_TRACE,
+ "Certificate Verification: depth: %d, subject: %s, issuer: %s",
+ errdepth, cp != NULL ? cp : "-unknown-",
+ cp2 != NULL ? cp2 : "-unknown");
+ if (cp)
+ free(cp);
+ if (cp2)
+ free(cp2);
+
+ /*
+ * Check for optionally acceptable non-verifiable issuer situation
+ */
+ if (dc != NULL && dc->nVerifyClient != SSL_CVERIFY_UNSET)
+ verify = dc->nVerifyClient;
+ else
+ verify = sc->nVerifyClient;
+ if ( ( errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
+ || errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
+ || errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
+#if SSL_LIBRARY_VERSION >= 0x00905000
+ || errnum == X509_V_ERR_CERT_UNTRUSTED
+#endif
+ || errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE )
+ && verify == SSL_CVERIFY_OPTIONAL_NO_CA ) {
+ ssl_log(s, SSL_LOG_TRACE,
+ "Certificate Verification: Verifiable Issuer is configured as "
+ "optional, therefore we're accepting the certificate");
+ ap_ctx_set(conn->client->ctx, "ssl::verify::info", "GENEROUS");
+ ok = TRUE;
+ }
+
+ /*
+ * Additionally perform CRL-based revocation checks
+ */
+ if (ok) {
+ ok = ssl_callback_SSLVerify_CRL(ok, ctx, s);
+ if (!ok)
+ errnum = X509_STORE_CTX_get_error(ctx);
+ }
+
+ /*
+ * If we already know it's not ok, log the real reason
+ */
+ if (!ok) {
+ ssl_log(s, SSL_LOG_ERROR, "Certificate Verification: Error (%d): %s",
+ errnum, X509_verify_cert_error_string(errnum));
+ ap_ctx_set(conn->client->ctx, "ssl::client::dn", NULL);
+ ap_ctx_set(conn->client->ctx, "ssl::verify::error",
+ (void *)X509_verify_cert_error_string(errnum));
+ }
+
+ /*
+ * Finally check the depth of the certificate verification
+ */
+ if (dc != NULL && dc->nVerifyDepth != UNSET)
+ depth = dc->nVerifyDepth;
+ else
+ depth = sc->nVerifyDepth;
+ if (errdepth > depth) {
+ ssl_log(s, SSL_LOG_ERROR,
+ "Certificate Verification: Certificate Chain too long "
+ "(chain has %d certificates, but maximum allowed are only %d)",
+ errdepth, depth);
+ ap_ctx_set(conn->client->ctx, "ssl::verify::error",
+ (void *)X509_verify_cert_error_string(X509_V_ERR_CERT_CHAIN_TOO_LONG));
+ ok = FALSE;
+ }
+
+ /*
+ * And finally signal OpenSSL the (perhaps changed) state
+ */
+ return (ok);
+}
+
+int ssl_callback_SSLVerify_CRL(
+ int ok, X509_STORE_CTX *ctx, server_rec *s)
+{
+ SSLSrvConfigRec *sc;
+ X509_OBJECT obj;
+ X509_NAME *subject;
+ X509_NAME *issuer;
+ X509 *xs;
+ X509_CRL *crl;
+ X509_REVOKED *revoked;
+ long serial;
+ BIO *bio;
+ int i, n, rc;
+ char *cp;
+ char *cp2;
+
+ /*
+ * Unless a revocation store for CRLs was created we
+ * cannot do any CRL-based verification, of course.
+ */
+ sc = mySrvConfig(s);
+ if (sc->pRevocationStore == NULL)
+ return ok;
+
+ /*
+ * Determine certificate ingredients in advance
+ */
+ xs = X509_STORE_CTX_get_current_cert(ctx);
+ subject = X509_get_subject_name(xs);
+ issuer = X509_get_issuer_name(xs);
+
+ /*
+ * OpenSSL provides the general mechanism to deal with CRLs but does not
+ * use them automatically when verifying certificates, so we do it
+ * explicitly here. We will check the CRL for the currently checked
+ * certificate, if there is such a CRL in the store.
+ *
+ * We come through this procedure for each certificate in the certificate
+ * chain, starting with the root-CA's certificate. At each step we've to
+ * both verify the signature on the CRL (to make sure it's a valid CRL)
+ * and it's revocation list (to make sure the current certificate isn't
+ * revoked). But because to check the signature on the CRL we need the
+ * public key of the issuing CA certificate (which was already processed
+ * one round before), we've a little problem. But we can both solve it and
+ * at the same time optimize the processing by using the following
+ * verification scheme (idea and code snippets borrowed from the GLOBUS
+ * project):
+ *
+ * 1. We'll check the signature of a CRL in each step when we find a CRL
+ * through the _subject_ name of the current certificate. This CRL
+ * itself will be needed the first time in the next round, of course.
+ * But we do the signature processing one round before this where the
+ * public key of the CA is available.
+ *
+ * 2. We'll check the revocation list of a CRL in each step when
+ * we find a CRL through the _issuer_ name of the current certificate.
+ * This CRLs signature was then already verified one round before.
+ *
+ * This verification scheme allows a CA to revoke its own certificate as
+ * well, of course.
+ */
+
+ /*
+ * Try to retrieve a CRL corresponding to the _subject_ of
+ * the current certificate in order to verify it's integrity.
+ */
+ memset((char *)&obj, 0, sizeof(obj));
+ rc = SSL_X509_STORE_lookup(sc->pRevocationStore, X509_LU_CRL, subject, &obj);
+ crl = obj.data.crl;
+ if (rc > 0 && crl != NULL) {
+ /*
+ * Log information about CRL
+ * (A little bit complicated because of ASN.1 and BIOs...)
+ */
+ if (ssl_log_applies(s, SSL_LOG_TRACE)) {
+ bio = BIO_new(BIO_s_mem());
+ BIO_printf(bio, "lastUpdate: ");
+ ASN1_UTCTIME_print(bio, X509_CRL_get_lastUpdate(crl));
+ BIO_printf(bio, ", nextUpdate: ");
+ ASN1_UTCTIME_print(bio, X509_CRL_get_nextUpdate(crl));
+ n = BIO_pending(bio);
+ cp = malloc(n+1);
+ n = BIO_read(bio, cp, n);
+ cp[n] = NUL;
+ BIO_free(bio);
+ cp2 = X509_NAME_oneline(subject, NULL, 0);
+ ssl_log(s, SSL_LOG_TRACE, "CA CRL: Issuer: %s, %s", cp2, cp);
+ free(cp2);
+ free(cp);
+ }
+
+ /*
+ * Verify the signature on this CRL
+ */
+ if (X509_CRL_verify(crl, X509_get_pubkey(xs)) <= 0) {
+ ssl_log(s, SSL_LOG_WARN, "Invalid signature on CRL");
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
+ X509_OBJECT_free_contents(&obj);
+ return FALSE;
+ }
+
+ /*
+ * Check date of CRL to make sure it's not expired
+ */
+ i = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl));
+ if (i == 0) {
+ ssl_log(s, SSL_LOG_WARN, "Found CRL has invalid nextUpdate field");
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
+ X509_OBJECT_free_contents(&obj);
+ return FALSE;
+ }
+ if (i < 0) {
+ ssl_log(s, SSL_LOG_WARN,
+ "Found CRL is expired - "
+ "revoking all certificates until you get updated CRL");
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED);
+ X509_OBJECT_free_contents(&obj);
+ return FALSE;
+ }
+ X509_OBJECT_free_contents(&obj);
+ }
+
+ /*
+ * Try to retrieve a CRL corresponding to the _issuer_ of
+ * the current certificate in order to check for revocation.
+ */
+ memset((char *)&obj, 0, sizeof(obj));
+ rc = SSL_X509_STORE_lookup(sc->pRevocationStore, X509_LU_CRL, issuer, &obj);
+ crl = obj.data.crl;
+ if (rc > 0 && crl != NULL) {
+ /*
+ * Check if the current certificate is revoked by this CRL
+ */
+#if SSL_LIBRARY_VERSION < 0x00904000
+ n = sk_num(X509_CRL_get_REVOKED(crl));
+#else
+ n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
+#endif
+ for (i = 0; i < n; i++) {
+#if SSL_LIBRARY_VERSION < 0x00904000
+ revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i);
+#else
+ revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
+#endif
+ if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(xs)) == 0) {
+
+ serial = ASN1_INTEGER_get(revoked->serialNumber);
+ cp = X509_NAME_oneline(issuer, NULL, 0);
+ ssl_log(s, SSL_LOG_INFO,
+ "Certificate with serial %ld (0x%lX) "
+ "revoked per CRL from issuer %s",
+ serial, serial, cp);
+ free(cp);
+
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED);
+ X509_OBJECT_free_contents(&obj);
+ return FALSE;
+ }
+ }
+ X509_OBJECT_free_contents(&obj);
+ }
+ return ok;
+}
+
+/*
+ * This callback function is executed by OpenSSL whenever a new SSL_SESSION is
+ * added to the internal OpenSSL session cache. We use this hook to spread the
+ * SSL_SESSION also to the inter-process disk-cache to make share it with our
+ * other Apache pre-forked server processes.
+ */
+int ssl_callback_NewSessionCacheEntry(SSL *ssl, SSL_SESSION *pNew)
+{
+ conn_rec *conn;
+ server_rec *s;
+ SSLSrvConfigRec *sc;
+ long t;
+ BOOL rc;
+
+ /*
+ * Get Apache context back through OpenSSL context
+ */
+ conn = (conn_rec *)SSL_get_app_data(ssl);
+ s = conn->server;
+ sc = mySrvConfig(s);
+
+ /*
+ * Set the timeout also for the internal OpenSSL cache, because this way
+ * our inter-process cache is consulted only when it's really necessary.
+ */
+ t = sc->nSessionCacheTimeout;
+ SSL_set_timeout(pNew, t);
+
+ /*
+ * Store the SSL_SESSION in the inter-process cache with the
+ * same expire time, so it expires automatically there, too.
+ */
+ t = (SSL_get_time(pNew) + sc->nSessionCacheTimeout);
+ rc = ssl_scache_store(s, pNew->session_id, pNew->session_id_length, t, pNew);
+
+ /*
+ * Log this cache operation
+ */
+ ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: "
+ "request=SET status=%s id=%s timeout=%ds (session caching)",
+ rc == TRUE ? "OK" : "BAD",
+ SSL_SESSION_id2sz(pNew->session_id, pNew->session_id_length),
+ t-time(NULL));
+
+ /*
+ * return 0 which means to OpenSSL that the pNew is still
+ * valid and was not freed by us with SSL_SESSION_free().
+ */
+ return 0;
+}
+
+/*
+ * This callback function is executed by OpenSSL whenever a
+ * SSL_SESSION is looked up in the internal OpenSSL cache and it
+ * was not found. We use this to lookup the SSL_SESSION in the
+ * inter-process disk-cache where it was perhaps stored by one
+ * of our other Apache pre-forked server processes.
+ */
+SSL_SESSION *ssl_callback_GetSessionCacheEntry(
+ SSL *ssl, unsigned char *id, int idlen, int *pCopy)
+{
+ conn_rec *conn;
+ server_rec *s;
+ SSL_SESSION *pSession;
+
+ /*
+ * Get Apache context back through OpenSSL context
+ */
+ conn = (conn_rec *)SSL_get_app_data(ssl);
+ s = conn->server;
+
+ /*
+ * Try to retrieve the SSL_SESSION from the inter-process cache
+ */
+ pSession = ssl_scache_retrieve(s, id, idlen);
+
+ /*
+ * Log this cache operation
+ */
+ if (pSession != NULL)
+ ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: "
+ "request=GET status=FOUND id=%s (session reuse)",
+ SSL_SESSION_id2sz(id, idlen));
+ else
+ ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: "
+ "request=GET status=MISSED id=%s (session renewal)",
+ SSL_SESSION_id2sz(id, idlen));
+
+ /*
+ * Return NULL or the retrieved SSL_SESSION. But indicate (by
+ * setting pCopy to 0) that the reference count on the
+ * SSL_SESSION should not be incremented by the SSL library,
+ * because we will no longer hold a reference to it ourself.
+ */
+ *pCopy = 0;
+ return pSession;
+}
+
+/*
+ * This callback function is executed by OpenSSL whenever a
+ * SSL_SESSION is removed from the the internal OpenSSL cache.
+ * We use this to remove the SSL_SESSION in the inter-process
+ * disk-cache, too.
+ */
+void ssl_callback_DelSessionCacheEntry(
+ SSL_CTX *ctx, SSL_SESSION *pSession)
+{
+ server_rec *s;
+
+ /*
+ * Get Apache context back through OpenSSL context
+ */
+ s = (server_rec *)SSL_CTX_get_app_data(ctx);
+ if (s == NULL) /* on server shutdown Apache is already gone */
+ return;
+
+ /*
+ * Remove the SSL_SESSION from the inter-process cache
+ */
+ ssl_scache_remove(s, pSession->session_id, pSession->session_id_length);
+
+ /*
+ * Log this cache operation
+ */
+ ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: "
+ "request=REM status=OK id=%s (session dead)",
+ SSL_SESSION_id2sz(pSession->session_id,
+ pSession->session_id_length));
+
+ return;
+}
+
+/*
+ * This callback function is executed while OpenSSL processes the
+ * SSL handshake and does SSL record layer stuff. We use it to
+ * trace OpenSSL's processing in out SSL logfile.
+ */
+void ssl_callback_LogTracingState(SSL *ssl, int where, int rc)
+{
+ conn_rec *c;
+ server_rec *s;
+ SSLSrvConfigRec *sc;
+ char *str;
+
+ /*
+ * find corresponding server
+ */
+ if ((c = (conn_rec *)SSL_get_app_data(ssl)) == NULL)
+ return;
+ s = c->server;
+ if ((sc = mySrvConfig(s)) == NULL)
+ return;
+
+ /*
+ * create the various trace messages
+ */
+ if (sc->nLogLevel >= SSL_LOG_TRACE) {
+ if (where & SSL_CB_HANDSHAKE_START)
+ ssl_log(s, SSL_LOG_TRACE, "%s: Handshake: start", SSL_LIBRARY_NAME);
+ else if (where & SSL_CB_HANDSHAKE_DONE)
+ ssl_log(s, SSL_LOG_TRACE, "%s: Handshake: done", SSL_LIBRARY_NAME);
+ else if (where & SSL_CB_LOOP)
+ ssl_log(s, SSL_LOG_TRACE, "%s: Loop: %s",
+ SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ else if (where & SSL_CB_READ)
+ ssl_log(s, SSL_LOG_TRACE, "%s: Read: %s",
+ SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ else if (where & SSL_CB_WRITE)
+ ssl_log(s, SSL_LOG_TRACE, "%s: Write: %s",
+ SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ else if (where & SSL_CB_ALERT) {
+ str = (where & SSL_CB_READ) ? "read" : "write";
+ ssl_log(s, SSL_LOG_TRACE, "%s: Alert: %s:%s:%s\n",
+ SSL_LIBRARY_NAME, str,
+ SSL_alert_type_string_long(rc),
+ SSL_alert_desc_string_long(rc));
+ }
+ else if (where & SSL_CB_EXIT) {
+ if (rc == 0)
+ ssl_log(s, SSL_LOG_TRACE, "%s: Exit: failed in %s",
+ SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ else if (rc < 0)
+ ssl_log(s, SSL_LOG_TRACE, "%s: Exit: error in %s",
+ SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ }
+ }
+
+ /*
+ * Because SSL renegotations can happen at any time (not only after
+ * SSL_accept()), the best way to log the current connection details is
+ * right after a finished handshake.
+ */
+ if (where & SSL_CB_HANDSHAKE_DONE) {
+ ssl_log(s, SSL_LOG_INFO,
+ "Connection: Client IP: %s, Protocol: %s, Cipher: %s (%s/%s bits)",
+ ssl_var_lookup(NULL, s, c, NULL, "REMOTE_ADDR"),
+ ssl_var_lookup(NULL, s, c, NULL, "SSL_PROTOCOL"),
+ ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER"),
+ ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER_USEKEYSIZE"),
+ ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER_ALGKEYSIZE"));
+ }
+
+ return;
+}
+
diff --git a/modules/ssl/ssl_engine_log.c b/modules/ssl/ssl_engine_log.c
new file mode 100644
index 0000000000..0e1c53a852
--- /dev/null
+++ b/modules/ssl/ssl_engine_log.c
@@ -0,0 +1,326 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_engine_log.c
+** Logging Facility
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``The difference between a computer
+ industry job and open-source software
+ hacking is about 30 hours a week.''
+ -- Ralf S. Engelschall */
+#include "mod_ssl.h"
+
+
+/* _________________________________________________________________
+**
+** Logfile Support
+** _________________________________________________________________
+*/
+
+/*
+ * Open the SSL logfile
+ */
+void ssl_log_open(server_rec *s_main, server_rec *s, pool *p)
+{
+ char *szLogFile;
+ SSLSrvConfigRec *sc_main = mySrvConfig(s_main);
+ SSLSrvConfigRec *sc = mySrvConfig(s);
+ piped_log *pl;
+
+ /*
+ * Short-circuit for inherited logfiles in order to save
+ * filedescriptors in mass-vhost situation. Be careful, this works
+ * fine because the close happens implicitly by the pool facility.
+ */
+ if ( s != s_main
+ && sc_main->fileLogFile != NULL
+ && ( (sc->szLogFile == NULL)
+ || ( sc->szLogFile != NULL
+ && sc_main->szLogFile != NULL
+ && strEQ(sc->szLogFile, sc_main->szLogFile)))) {
+ sc->fileLogFile = sc_main->fileLogFile;
+ }
+ else if (sc->szLogFile != NULL) {
+ if (strEQ(sc->szLogFile, "/dev/null"))
+ return;
+ else if (sc->szLogFile[0] == '|') {
+ szLogFile = ssl_util_server_root_relative(p, "log", sc->szLogFile+1);
+ if ((pl = ap_open_piped_log(p, szLogFile)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Cannot open reliable pipe to SSL logfile filter %s", szLogFile);
+ ssl_die();
+ }
+ sc->fileLogFile = ap_pfdopen(p, ap_piped_log_write_fd(pl), "a");
+ setbuf(sc->fileLogFile, NULL);
+ }
+ else {
+ szLogFile = ssl_util_server_root_relative(p, "log", sc->szLogFile);
+ if ((sc->fileLogFile = ap_pfopen(p, szLogFile, "a")) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Cannot open SSL logfile %s", szLogFile);
+ ssl_die();
+ }
+ setbuf(sc->fileLogFile, NULL);
+ }
+ }
+ return;
+}
+
+static struct {
+ int nLevel;
+ char *szLevel;
+} ssl_log_level2string[] = {
+ { SSL_LOG_ERROR, "error" },
+ { SSL_LOG_WARN, "warn" },
+ { SSL_LOG_INFO, "info" },
+ { SSL_LOG_TRACE, "trace" },
+ { SSL_LOG_DEBUG, "debug" },
+ { 0, NULL }
+};
+
+static struct {
+ char *cpPattern;
+ char *cpAnnotation;
+} ssl_log_annotate[] = {
+ { "*envelope*bad*decrypt*", "wrong pass phrase!?" },
+ { "*CLIENT_HELLO*unknown*protocol*", "speaking not SSL to HTTPS port!?" },
+ { "*CLIENT_HELLO*http*request*", "speaking HTTP to HTTPS port!?" },
+ { "*SSL3_READ_BYTES:sslv3*alert*bad*certificate*", "Subject CN in certificate not server name or identical to CA!?" },
+ { "*self signed certificate in certificate chain*", "Client certificate signed by CA not known to server?" },
+ { "*peer did not return a certificate*", "No CAs known to server for verification?" },
+ { "*no shared cipher*", "Too restrictive SSLCipherSuite or using DSA server certificate?" },
+ { "*no start line*", "Bad file contents or format - or even just a forgotten SSLCertificateKeyFile?" },
+ { "*bad password read*", "You entered an incorrect pass phrase!?" },
+ { "*bad mac decode*", "Browser still remembered details of a re-created server certificate?" },
+ { NULL, NULL }
+};
+
+static char *ssl_log_annotation(char *error)
+{
+ char *errstr;
+ int i;
+
+ errstr = NULL;
+ for (i = 0; ssl_log_annotate[i].cpPattern != NULL; i++) {
+ if (ap_strcmp_match(error, ssl_log_annotate[i].cpPattern) == 0) {
+ errstr = ssl_log_annotate[i].cpAnnotation;
+ break;
+ }
+ }
+ return errstr;
+}
+
+BOOL ssl_log_applies(server_rec *s, int level)
+{
+ SSLSrvConfigRec *sc;
+
+ sc = mySrvConfig(s);
+ if ( sc->fileLogFile == NULL
+ && !(level & SSL_LOG_ERROR))
+ return FALSE;
+ if ( level > sc->nLogLevel
+ && !(level & SSL_LOG_ERROR))
+ return FALSE;
+ return TRUE;
+}
+
+void ssl_log(server_rec *s, int level, const char *msg, ...)
+{
+ char tstr[80];
+ char lstr[20];
+ char vstr[1024];
+ char str[1024];
+ char nstr[2];
+ int timz;
+ struct tm *t;
+ va_list ap;
+ int add;
+ int i;
+ char *astr;
+ int safe_errno;
+ unsigned long e;
+ SSLSrvConfigRec *sc;
+ char *cpE;
+ char *cpA;
+
+ /* initialization */
+ va_start(ap, msg);
+ safe_errno = errno;
+ sc = mySrvConfig(s);
+
+ /* strip out additional flags */
+ add = (level & ~SSL_LOG_MASK);
+ level = (level & SSL_LOG_MASK);
+
+ /* reduce flags when not reasonable in context */
+ if (add & SSL_ADD_ERRNO && errno == 0)
+ add &= ~SSL_ADD_ERRNO;
+ if (add & SSL_ADD_SSLERR && ERR_peek_error() == 0)
+ add &= ~SSL_ADD_SSLERR;
+
+ /* we log only levels below, except for errors */
+ if ( sc->fileLogFile == NULL
+ && !(level & SSL_LOG_ERROR))
+ return;
+ if ( level > sc->nLogLevel
+ && !(level & SSL_LOG_ERROR))
+ return;
+
+ /* determine the time entry string */
+ if (add & SSL_NO_TIMESTAMP)
+ tstr[0] = NUL;
+ else {
+ t = ap_get_gmtoff(&timz);
+ strftime(tstr, 80, "[%d/%b/%Y %H:%M:%S", t);
+ i = strlen(tstr);
+ ap_snprintf(tstr+i, 80-i, " %05d] ", (unsigned int)getpid());
+ }
+
+ /* determine whether newline should be written */
+ if (add & SSL_NO_NEWLINE)
+ nstr[0] = NUL;
+ else {
+ nstr[0] = '\n';
+ nstr[1] = NUL;
+ }
+
+ /* determine level name */
+ lstr[0] = NUL;
+ if (!(add & SSL_NO_LEVELID)) {
+ for (i = 0; ssl_log_level2string[i].nLevel != 0; i++) {
+ if (ssl_log_level2string[i].nLevel == level) {
+ ap_snprintf(lstr, sizeof(lstr), "[%s]", ssl_log_level2string[i].szLevel);
+ break;
+ }
+ }
+ for (i = strlen(lstr); i <= 7; i++)
+ lstr[i] = ' ';
+ lstr[i] = NUL;
+ }
+
+ /* create custom message */
+ ap_vsnprintf(vstr, sizeof(vstr), msg, ap);
+
+ /* write out SSLog message */
+ if ((add & SSL_ADD_ERRNO) && (add & SSL_ADD_SSLERR))
+ astr = " (System and " SSL_LIBRARY_NAME " library errors follow)";
+ else if (add & SSL_ADD_ERRNO)
+ astr = " (System error follows)";
+ else if (add & SSL_ADD_SSLERR)
+ astr = " (" SSL_LIBRARY_NAME " library error follows)";
+ else
+ astr = "";
+ if (level <= sc->nLogLevel && sc->fileLogFile != NULL) {
+ ap_snprintf(str, sizeof(str), "%s%s%s%s%s", tstr, lstr, vstr, astr, nstr);
+ fprintf(sc->fileLogFile, "%s", str);
+ }
+ if (level & SSL_LOG_ERROR)
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
+ "mod_ssl: %s%s", vstr, astr);
+
+ /* write out additional attachment messages */
+ if (add & SSL_ADD_ERRNO) {
+ if (level <= sc->nLogLevel && sc->fileLogFile != NULL) {
+ ap_snprintf(str, sizeof(str), "%s%sSystem: %s (errno: %d)%s",
+ tstr, lstr, strerror(safe_errno), safe_errno, nstr);
+ fprintf(sc->fileLogFile, "%s", str);
+ }
+ if (level & SSL_LOG_ERROR)
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
+ "System: %s (errno: %d)",
+ strerror(safe_errno), safe_errno);
+ }
+ if (add & SSL_ADD_SSLERR) {
+ while ((e = ERR_get_error())) {
+ cpE = ERR_error_string(e, NULL);
+ cpA = ssl_log_annotation(cpE);
+ if (level <= sc->nLogLevel && sc->fileLogFile != NULL) {
+ ap_snprintf(str, sizeof(str), "%s%s%s: %s%s%s%s%s",
+ tstr, lstr, SSL_LIBRARY_NAME, cpE,
+ cpA != NULL ? " [Hint: " : "",
+ cpA != NULL ? cpA : "", cpA != NULL ? "]" : "",
+ nstr);
+ fprintf(sc->fileLogFile, "%s", str);
+ }
+ if (level & SSL_LOG_ERROR)
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s,
+ "%s: %s%s%s%s", SSL_LIBRARY_NAME, cpE,
+ cpA != NULL ? " [Hint: " : "",
+ cpA != NULL ? cpA : "", cpA != NULL ? "]" : "");
+ }
+ }
+ /* make sure the next log starts from a clean base */
+ /* ERR_clear_error(); */
+
+ /* cleanup and return */
+ if (sc->fileLogFile != NULL)
+ fflush(sc->fileLogFile);
+ errno = safe_errno;
+ va_end(ap);
+ return;
+}
+
+void ssl_die(void)
+{
+ /*
+ * This is used for fatal errors and here
+ * it is common module practice to really
+ * exit from the complete program.
+ */
+ exit(1);
+}
+
diff --git a/modules/ssl/ssl_engine_mutex.c b/modules/ssl/ssl_engine_mutex.c
new file mode 100644
index 0000000000..146f9ce4d9
--- /dev/null
+++ b/modules/ssl/ssl_engine_mutex.c
@@ -0,0 +1,397 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_engine_mutex.c
+** Semaphore for Mutual Exclusion
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``Real programmers confuse
+ Christmas and Halloween
+ because DEC 25 = OCT 31.''
+ -- Unknown */
+#include "mod_ssl.h"
+
+
+/* _________________________________________________________________
+**
+** Mutex Support (Common)
+** _________________________________________________________________
+*/
+
+void ssl_mutex_init(server_rec *s, pool *p)
+{
+ SSLModConfigRec *mc = myModConfig();
+
+ if (mc->nMutexMode == SSL_MUTEXMODE_FILE)
+ ssl_mutex_file_create(s, p);
+ else if (mc->nMutexMode == SSL_MUTEXMODE_SEM)
+ ssl_mutex_sem_create(s, p);
+ return;
+}
+
+void ssl_mutex_reinit(server_rec *s, pool *p)
+{
+ SSLModConfigRec *mc = myModConfig();
+
+ if (mc->nMutexMode == SSL_MUTEXMODE_FILE)
+ ssl_mutex_file_open(s, p);
+ else if (mc->nMutexMode == SSL_MUTEXMODE_SEM)
+ ssl_mutex_sem_open(s, p);
+ return;
+}
+
+void ssl_mutex_on(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig();
+ BOOL ok = TRUE;
+
+ if (mc->nMutexMode == SSL_MUTEXMODE_FILE)
+ ok = ssl_mutex_file_acquire();
+ else if (mc->nMutexMode == SSL_MUTEXMODE_SEM)
+ ok = ssl_mutex_sem_acquire();
+ if (!ok)
+ ssl_log(s, SSL_LOG_WARN, "Failed to acquire global mutex lock");
+ return;
+}
+
+void ssl_mutex_off(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig();
+ BOOL ok = TRUE;
+
+ if (mc->nMutexMode == SSL_MUTEXMODE_FILE)
+ ok = ssl_mutex_file_release();
+ else if (mc->nMutexMode == SSL_MUTEXMODE_SEM)
+ ok = ssl_mutex_sem_release();
+ if (!ok)
+ ssl_log(s, SSL_LOG_WARN, "Failed to release global mutex lock");
+ return;
+}
+
+void ssl_mutex_kill(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig();
+
+ if (mc->nMutexMode == SSL_MUTEXMODE_FILE)
+ ssl_mutex_file_remove(s);
+ else if (mc->nMutexMode == SSL_MUTEXMODE_SEM)
+ ssl_mutex_sem_remove(s);
+ return;
+}
+
+
+/* _________________________________________________________________
+**
+** Mutex Support (Lockfile)
+** _________________________________________________________________
+*/
+
+void ssl_mutex_file_create(server_rec *s, pool *p)
+{
+#ifndef WIN32
+ SSLModConfigRec *mc = myModConfig();
+
+ /* create the lockfile */
+ unlink(mc->szMutexFile);
+ if ((mc->nMutexFD = ap_popenf(p, mc->szMutexFile,
+ O_WRONLY|O_CREAT, SSL_MUTEX_LOCK_MODE)) < 0) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Parent process could not create SSLMutex lockfile %s",
+ mc->szMutexFile);
+ ssl_die();
+ }
+ ap_pclosef(p, mc->nMutexFD);
+
+ /* make sure the childs have access to this file */
+#ifndef OS2
+ if (geteuid() == 0 /* is superuser */)
+ chown(mc->szMutexFile, ap_user_id, -1 /* no gid change */);
+#endif
+
+ /* open the lockfile for real */
+ if ((mc->nMutexFD = ap_popenf(p, mc->szMutexFile,
+ O_WRONLY, SSL_MUTEX_LOCK_MODE)) < 0) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Parent could not open SSLMutex lockfile %s",
+ mc->szMutexFile);
+ ssl_die();
+ }
+#endif
+ return;
+}
+
+void ssl_mutex_file_open(server_rec *s, pool *p)
+{
+#ifndef WIN32
+ SSLModConfigRec *mc = myModConfig();
+
+ /* open the lockfile (once per child) to get a unique fd */
+ if ((mc->nMutexFD = ap_popenf(p, mc->szMutexFile,
+ O_WRONLY, SSL_MUTEX_LOCK_MODE)) < 0) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Child could not open SSLMutex lockfile %s",
+ mc->szMutexFile);
+ ssl_die();
+ }
+#endif
+ return;
+}
+
+void ssl_mutex_file_remove(void *data)
+{
+#ifndef WIN32
+ SSLModConfigRec *mc = myModConfig();
+
+ /* remove the mutex lockfile */
+ unlink(mc->szMutexFile);
+#endif
+ return;
+}
+
+#ifndef WIN32
+#ifdef SSL_USE_FCNTL
+static struct flock lock_it;
+static struct flock unlock_it;
+#endif
+#endif
+
+BOOL ssl_mutex_file_acquire(void)
+{
+ int rc = -1;
+#ifndef WIN32
+ SSLModConfigRec *mc = myModConfig();
+
+#ifdef SSL_USE_FCNTL
+ lock_it.l_whence = SEEK_SET; /* from current point */
+ lock_it.l_start = 0; /* -"- */
+ lock_it.l_len = 0; /* until end of file */
+ lock_it.l_type = F_WRLCK; /* set exclusive/write lock */
+ lock_it.l_pid = 0; /* pid not actually interesting */
+
+ while ( ((rc = fcntl(mc->nMutexFD, F_SETLKW, &lock_it)) < 0)
+ && (errno == EINTR) )
+ ;
+#endif
+#ifdef SSL_USE_FLOCK
+ while ( ((rc = flock(mc->nMutexFD, LOCK_EX)) < 0)
+ && (errno == EINTR) )
+ ;
+#endif
+#endif
+
+ if (rc < 0)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+BOOL ssl_mutex_file_release(void)
+{
+ int rc = -1;
+#ifndef WIN32
+ SSLModConfigRec *mc = myModConfig();
+
+#ifdef SSL_USE_FCNTL
+ unlock_it.l_whence = SEEK_SET; /* from current point */
+ unlock_it.l_start = 0; /* -"- */
+ unlock_it.l_len = 0; /* until end of file */
+ unlock_it.l_type = F_UNLCK; /* unlock */
+ unlock_it.l_pid = 0; /* pid not actually interesting */
+
+ while ( (rc = fcntl(mc->nMutexFD, F_SETLKW, &unlock_it)) < 0
+ && (errno == EINTR) )
+ ;
+#endif
+#ifdef SSL_USE_FLOCK
+ while ( (rc = flock(mc->nMutexFD, LOCK_UN)) < 0
+ && (errno == EINTR) )
+ ;
+#endif
+#endif
+
+ if (rc < 0)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/* _________________________________________________________________
+**
+** Mutex Support (Process Semaphore)
+** _________________________________________________________________
+*/
+
+void ssl_mutex_sem_create(server_rec *s, pool *p)
+{
+#ifdef SSL_CAN_USE_SEM
+ int semid;
+ SSLModConfigRec *mc = myModConfig();
+#ifdef SSL_HAVE_IPCSEM
+ union ssl_ipc_semun semctlarg;
+ struct semid_ds semctlbuf;
+#endif
+
+#ifdef SSL_HAVE_IPCSEM
+ semid = semget(IPC_PRIVATE, 1, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR);
+ if (semid == -1 && errno == EEXIST)
+ semid = semget(IPC_PRIVATE, 1, IPC_EXCL|S_IRUSR|S_IWUSR);
+ if (semid == -1) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Parent process could not create private SSLMutex semaphore");
+ ssl_die();
+ }
+ semctlarg.val = 0;
+ if (semctl(semid, 0, SETVAL, semctlarg) < 0) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Parent process could not initialize SSLMutex semaphore value");
+ ssl_die();
+ }
+ semctlbuf.sem_perm.uid = ap_user_id;
+ semctlbuf.sem_perm.gid = ap_group_id;
+ semctlbuf.sem_perm.mode = 0660;
+ semctlarg.buf = &semctlbuf;
+ if (semctl(semid, 0, IPC_SET, semctlarg) < 0) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Parent process could not set permissions for SSLMutex semaphore");
+ ssl_die();
+ }
+#endif
+#ifdef SSL_HAVE_W32SEM
+ semid = (int)ap_create_mutex("mod_ssl_mutex");
+#endif
+ mc->nMutexSEMID = semid;
+#endif
+ return;
+}
+
+void ssl_mutex_sem_open(server_rec *s, pool *p)
+{
+#ifdef SSL_CAN_USE_SEM
+#ifdef SSL_HAVE_W32SEM
+ SSLModConfigRec *mc = myModConfig();
+
+ mc->nMutexSEMID = (int)ap_open_mutex("mod_ssl_mutex");
+#endif
+#endif
+ return;
+}
+
+void ssl_mutex_sem_remove(void *data)
+{
+#ifdef SSL_CAN_USE_SEM
+ SSLModConfigRec *mc = myModConfig();
+
+#ifdef SSL_HAVE_IPCSEM
+ semctl(mc->nMutexSEMID, 0, IPC_RMID, 0);
+#endif
+#ifdef SSL_HAVE_W32SEM
+ ap_destroy_mutex((mutex *)mc->nMutexSEMID);
+#endif
+#endif
+ return;
+}
+
+BOOL ssl_mutex_sem_acquire(void)
+{
+ int rc = 0;
+#ifdef SSL_CAN_USE_SEM
+ SSLModConfigRec *mc = myModConfig();
+
+#ifdef SSL_HAVE_IPCSEM
+ struct sembuf sb[] = {
+ { 0, 0, 0 }, /* wait for semaphore */
+ { 0, 1, SEM_UNDO } /* increment semaphore */
+ };
+
+ while ( (rc = semop(mc->nMutexSEMID, sb, 2)) < 0
+ && (errno == EINTR) )
+ ;
+#endif
+#ifdef SSL_HAVE_W32SEM
+ rc = ap_acquire_mutex((mutex *)mc->nMutexSEMID);
+#endif
+#endif
+ if (rc != 0)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+BOOL ssl_mutex_sem_release(void)
+{
+ int rc = 0;
+#ifdef SSL_CAN_USE_SEM
+ SSLModConfigRec *mc = myModConfig();
+
+#ifdef SSL_HAVE_IPCSEM
+ struct sembuf sb[] = {
+ { 0, -1, SEM_UNDO } /* decrements semaphore */
+ };
+
+ while ( (rc = semop(mc->nMutexSEMID, sb, 1)) < 0
+ && (errno == EINTR) )
+ ;
+#endif
+#ifdef SSL_HAVE_W32SEM
+ rc = ap_release_mutex((mutex *)mc->nMutexSEMID);
+#endif
+#endif
+ if (rc != 0)
+ return FALSE;
+ else
+ return TRUE;
+}
+
diff --git a/modules/ssl/ssl_engine_pphrase.c b/modules/ssl/ssl_engine_pphrase.c
new file mode 100644
index 0000000000..2cef4e309e
--- /dev/null
+++ b/modules/ssl/ssl_engine_pphrase.c
@@ -0,0 +1,545 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_engine_pphrase.c
+** Pass Phrase Dialog
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``Treat your password like your
+ toothbrush. Don't let anybody
+ else use it, and get a new one
+ every six months.''
+ -- Clifford Stoll */
+#include "mod_ssl.h"
+
+
+/* _________________________________________________________________
+**
+** Pass Phrase and Private Key Handling
+** _________________________________________________________________
+*/
+
+#define STDERR_FILENO_STORE 50
+#define BUILTIN_DIALOG_BACKOFF 2
+#define BUILTIN_DIALOG_RETRIES 5
+
+void ssl_pphrase_Handle(server_rec *s, pool *p)
+{
+ SSLModConfigRec *mc = myModConfig();
+ SSLSrvConfigRec *sc;
+ server_rec *pServ;
+ char *cpVHostID;
+ char szPath[MAX_STRING_LEN];
+ EVP_PKEY *pPrivateKey;
+ ssl_asn1_t *asn1;
+ unsigned char *ucp;
+ X509 *pX509Cert;
+ FILE *fp;
+ BOOL bReadable;
+ ssl_ds_array *aPassPhrase;
+ int nPassPhrase;
+ int nPassPhraseCur;
+ char *cpPassPhraseCur;
+ int nPassPhraseRetry;
+ int nPassPhraseDialog;
+ int nPassPhraseDialogCur;
+ BOOL bPassPhraseDialogOnce;
+ char **cpp;
+ int i, j;
+ ssl_algo_t algoCert, algoKey, at;
+ char *an;
+ char *cp;
+
+ /*
+ * Start with a fresh pass phrase array
+ */
+ aPassPhrase = ssl_ds_array_make(p, sizeof(char *));
+ nPassPhrase = 0;
+ nPassPhraseDialog = 0;
+
+ /*
+ * Walk through all configured servers
+ */
+ for (pServ = s; pServ != NULL; pServ = pServ->next) {
+ sc = mySrvConfig(pServ);
+
+ if (!sc->bEnabled)
+ continue;
+
+ cpVHostID = ssl_util_vhostid(p, pServ);
+ ssl_log(pServ, SSL_LOG_INFO,
+ "Init: Loading certificate & private key of SSL-aware server %s",
+ cpVHostID);
+
+ /*
+ * Read in server certificate(s): This is the easy part
+ * because this file isn't encrypted in any way.
+ */
+ if (sc->szPublicCertFile[0] == NULL) {
+ ssl_log(pServ, SSL_LOG_ERROR,
+ "Init: Server %s should be SSL-aware but has no certificate configured "
+ "[Hint: SSLCertificateFile]", cpVHostID);
+ ssl_die();
+ }
+ algoCert = SSL_ALGO_UNKNOWN;
+ algoKey = SSL_ALGO_UNKNOWN;
+ for (i = 0, j = 0; i < SSL_AIDX_MAX && sc->szPublicCertFile[i] != NULL; i++) {
+
+ ap_cpystrn(szPath, sc->szPublicCertFile[i], sizeof(szPath));
+ if ((fp = ap_pfopen(p, szPath, "r")) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Init: Can't open server certificate file %s", szPath);
+ ssl_die();
+ }
+ if ((pX509Cert = SSL_read_X509(fp, NULL, NULL)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: Unable to read server certificate from file %s", szPath);
+ ssl_die();
+ }
+ ap_pfclose(p, fp);
+
+ /*
+ * check algorithm type of certificate and make
+ * sure only one certificate per type is used.
+ */
+ at = ssl_util_algotypeof(pX509Cert, NULL);
+ an = ssl_util_algotypestr(at);
+ if (algoCert & at) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: Multiple %s server certificates not allowed", an);
+ ssl_die();
+ }
+ algoCert |= at;
+
+ /*
+ * Insert the certificate into global module configuration to let it
+ * survive the processing between the 1st Apache API init round (where
+ * we operate here) and the 2nd Apache init round (where the
+ * certificate is actually used to configure mod_ssl's per-server
+ * configuration structures).
+ */
+ cp = ap_psprintf(mc->pPool, "%s:%s", cpVHostID, an);
+ asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tPublicCert, cp);
+ asn1->nData = i2d_X509(pX509Cert, NULL);
+ asn1->cpData = ap_palloc(mc->pPool, asn1->nData);
+ ucp = asn1->cpData; i2d_X509(pX509Cert, &ucp); /* 2nd arg increments */
+
+ /*
+ * Free the X509 structure
+ */
+ X509_free(pX509Cert);
+
+ /*
+ * Read in the private key: This is the non-trivial part, because the
+ * key is typically encrypted, so a pass phrase dialog has to be used
+ * to request it from the user (or it has to be alternatively gathered
+ * from a dialog program). The important point here is that ISPs
+ * usually have hundrets of virtual servers configured and a lot of
+ * them use SSL, so really we have to minimize the pass phrase
+ * dialogs.
+ *
+ * The idea is this: When N virtual hosts are configured and all of
+ * them use encrypted private keys with different pass phrases, we
+ * have no chance and have to pop up N pass phrase dialogs. But
+ * usually the admin is clever enough and uses the same pass phrase
+ * for more private key files (typically he even uses one single pass
+ * phrase for all). When this is the case we can minimize the dialogs
+ * by trying to re-use already known/entered pass phrases.
+ */
+ if (sc->szPrivateKeyFile[j] != NULL)
+ ap_cpystrn(szPath, sc->szPrivateKeyFile[j++], sizeof(szPath));
+
+ /*
+ * Try to read the private key file with the help of
+ * the callback function which serves the pass
+ * phrases to OpenSSL
+ */
+ myCtxVarSet(mc, 1, pServ);
+ myCtxVarSet(mc, 2, p);
+ myCtxVarSet(mc, 3, aPassPhrase);
+ myCtxVarSet(mc, 4, &nPassPhraseCur);
+ myCtxVarSet(mc, 5, &cpPassPhraseCur);
+ myCtxVarSet(mc, 6, cpVHostID);
+ myCtxVarSet(mc, 7, an);
+ myCtxVarSet(mc, 8, &nPassPhraseDialog);
+ myCtxVarSet(mc, 9, &nPassPhraseDialogCur);
+ myCtxVarSet(mc, 10, &bPassPhraseDialogOnce);
+
+ nPassPhraseCur = 0;
+ nPassPhraseRetry = 0;
+ nPassPhraseDialogCur = 0;
+ bPassPhraseDialogOnce = TRUE;
+
+ pPrivateKey = NULL;
+
+ for (;;) {
+ /*
+ * Try to read the private key file with the help of
+ * the callback function which serves the pass
+ * phrases to OpenSSL
+ */
+ if ((fp = ap_pfopen(p, szPath, "r")) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Init: Can't open server private key file %s", szPath);
+ ssl_die();
+ }
+ cpPassPhraseCur = NULL;
+ bReadable = ((pPrivateKey = SSL_read_PrivateKey(fp, NULL,
+ ssl_pphrase_Handle_CB)) != NULL ? TRUE : FALSE);
+ ap_pfclose(p, fp);
+
+ /*
+ * when the private key file now was readable,
+ * it's fine and we go out of the loop
+ */
+ if (bReadable)
+ break;
+
+ /*
+ * when we have more remembered pass phrases
+ * try to reuse these first.
+ */
+ if (nPassPhraseCur < nPassPhrase) {
+ nPassPhraseCur++;
+ continue;
+ }
+
+ /*
+ * else it's not readable and we have no more
+ * remembered pass phrases. Then this has to mean
+ * that the callback function popped up the dialog
+ * but a wrong pass phrase was entered. We give the
+ * user (but not the dialog program) a few more
+ * chances...
+ */
+ if ( sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN
+ && cpPassPhraseCur != NULL
+ && nPassPhraseRetry < BUILTIN_DIALOG_RETRIES ) {
+ fprintf(stdout, "Apache:mod_ssl:Error: Pass phrase incorrect "
+ "(%d more retr%s permitted).\n",
+ (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry),
+ (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry) == 1 ? "y" : "ies");
+ nPassPhraseRetry++;
+ if (nPassPhraseRetry > BUILTIN_DIALOG_BACKOFF)
+ sleep((nPassPhraseRetry-BUILTIN_DIALOG_BACKOFF)*5);
+ continue;
+ }
+
+ /*
+ * Ok, anything else now means a fatal error.
+ */
+ if (cpPassPhraseCur == NULL)
+ ssl_log(pServ, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Init: Private key not found");
+ if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) {
+ fprintf(stdout, "Apache:mod_ssl:Error: Private key not found.\n");
+ fprintf(stdout, "**Stopped\n");
+ }
+ else {
+ ssl_log(pServ, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Init: Pass phrase incorrect");
+ if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) {
+ fprintf(stdout, "Apache:mod_ssl:Error: Pass phrase incorrect.\n");
+ fprintf(stdout, "**Stopped\n");
+ }
+ }
+ ssl_die();
+ }
+
+ if (pPrivateKey == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: Unable to read server private key from file %s", szPath);
+ ssl_die();
+ }
+
+ /*
+ * check algorithm type of private key and make
+ * sure only one private key per type is used.
+ */
+ at = ssl_util_algotypeof(NULL, pPrivateKey);
+ an = ssl_util_algotypestr(at);
+ if (algoKey & at) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "Init: Multiple %s server private keys not allowed", an);
+ ssl_die();
+ }
+ algoKey |= at;
+
+ /*
+ * Log the type of reading
+ */
+ if (nPassPhraseDialogCur == 0)
+ ssl_log(pServ, SSL_LOG_TRACE,
+ "Init: (%s) unencrypted %s private key - pass phrase not required",
+ cpVHostID, an);
+ else {
+ if (cpPassPhraseCur != NULL)
+ ssl_log(pServ, SSL_LOG_TRACE,
+ "Init: (%s) encrypted %s private key - pass phrase requested",
+ cpVHostID, an);
+ else
+ ssl_log(pServ, SSL_LOG_TRACE,
+ "Init: (%s) encrypted %s private key - pass phrase reused",
+ cpVHostID, an);
+ }
+
+ /*
+ * Ok, when we have one more pass phrase store it
+ */
+ if (cpPassPhraseCur != NULL) {
+ cpp = (char **)ssl_ds_array_push(aPassPhrase);
+ *cpp = cpPassPhraseCur;
+ nPassPhrase++;
+ }
+
+ /*
+ * Insert private key into the global module configuration
+ * (we convert it to a stand-alone DER byte sequence
+ * because the SSL library uses static variables inside a
+ * RSA structure which do not survive DSO reloads!)
+ */
+ cp = ap_psprintf(mc->pPool, "%s:%s", cpVHostID, an);
+ asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tPrivateKey, cp);
+ asn1->nData = i2d_PrivateKey(pPrivateKey, NULL);
+ asn1->cpData = ap_palloc(mc->pPool, asn1->nData);
+ ucp = asn1->cpData; i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */
+
+ /*
+ * Free the private key structure
+ */
+ EVP_PKEY_free(pPrivateKey);
+ }
+ }
+
+ /*
+ * Let the user know when we're successful.
+ */
+ if (nPassPhraseDialog > 0) {
+ sc = mySrvConfig(s);
+ if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) {
+ fprintf(stdout, "\n");
+ fprintf(stdout, "Ok: Pass Phrase Dialog successful.\n");
+ }
+ }
+
+ /*
+ * Wipe out the used memory from the
+ * pass phrase array and then deallocate it
+ */
+ if (!ssl_ds_array_isempty(aPassPhrase)) {
+ ssl_ds_array_wipeout(aPassPhrase);
+ ssl_ds_array_kill(aPassPhrase);
+ ssl_log(s, SSL_LOG_INFO, "Init: Wiped out the queried pass phrases from memory");
+ }
+
+ return;
+}
+
+int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify)
+{
+ SSLModConfigRec *mc = myModConfig();
+ server_rec *s;
+ pool *p;
+ ssl_ds_array *aPassPhrase;
+ SSLSrvConfigRec *sc;
+ int *pnPassPhraseCur;
+ char **cppPassPhraseCur;
+ char *cpVHostID;
+ char *cpAlgoType;
+ int *pnPassPhraseDialog;
+ int *pnPassPhraseDialogCur;
+ BOOL *pbPassPhraseDialogOnce;
+ int stderr_store;
+ char **cpp;
+ int len = -1;
+
+ /*
+ * Reconnect to the context of ssl_phrase_Handle()
+ */
+ s = myCtxVarGet(mc, 1, server_rec *);
+ p = myCtxVarGet(mc, 2, pool *);
+ aPassPhrase = myCtxVarGet(mc, 3, ssl_ds_array *);
+ pnPassPhraseCur = myCtxVarGet(mc, 4, int *);
+ cppPassPhraseCur = myCtxVarGet(mc, 5, char **);
+ cpVHostID = myCtxVarGet(mc, 6, char *);
+ cpAlgoType = myCtxVarGet(mc, 7, char *);
+ pnPassPhraseDialog = myCtxVarGet(mc, 8, int *);
+ pnPassPhraseDialogCur = myCtxVarGet(mc, 9, int *);
+ pbPassPhraseDialogOnce = myCtxVarGet(mc, 10, BOOL *);
+ sc = mySrvConfig(s);
+
+ (*pnPassPhraseDialog)++;
+ (*pnPassPhraseDialogCur)++;
+
+ /*
+ * When remembered pass phrases are available use them...
+ */
+ if ((cpp = (char **)ssl_ds_array_get(aPassPhrase, *pnPassPhraseCur)) != NULL) {
+ ap_cpystrn(buf, *cpp, bufsize);
+ len = strlen(buf);
+ return len;
+ }
+
+ /*
+ * Builtin dialog
+ */
+ if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) {
+ char *prompt;
+ int i;
+#ifdef WIN32
+ FILE *con;
+#endif
+
+ ssl_log(s, SSL_LOG_INFO,
+ "Init: Requesting pass phrase via builtin terminal dialog");
+
+ /*
+ * Reconnect STDERR to terminal (here STDOUT) because
+ * at our init stage Apache already connected STDERR
+ * to the general error logfile.
+ */
+#ifdef WIN32
+ stderr_store = STDERR_FILENO_STORE;
+#else
+ if ((stderr_store = open("/dev/null", O_WRONLY)) == -1)
+ stderr_store = STDERR_FILENO_STORE;
+#endif
+ dup2(STDERR_FILENO, stderr_store);
+#ifdef WIN32
+ if ((con = fopen("con", "w")) != NULL)
+ dup2(fileno(con), STDERR_FILENO);
+ else
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+#else
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+#endif
+
+ /*
+ * The first time display a header to inform the user about what
+ * program he actually speaks to, which module is responsible for
+ * this terminal dialog and why to the hell he has to enter
+ * something...
+ */
+ if (*pnPassPhraseDialog == 1) {
+ fprintf(stderr, "%s mod_ssl/%s (Pass Phrase Dialog)\n",
+ SERVER_BASEVERSION, MOD_SSL_VERSION);
+ fprintf(stderr, "Some of your private key files are encrypted for security reasons.\n");
+ fprintf(stderr, "In order to read them you have to provide us with the pass phrases.\n");
+ }
+ if (*pbPassPhraseDialogOnce) {
+ *pbPassPhraseDialogOnce = FALSE;
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Server %s (%s)\n", cpVHostID, cpAlgoType);
+ }
+
+ /*
+ * Emulate the OpenSSL internal pass phrase dialog
+ * (see crypto/pem/pem_lib.c:def_callback() for details)
+ */
+ prompt = "Enter pass phrase:";
+ for (;;) {
+ if ((i = EVP_read_pw_string(buf, bufsize, prompt, FALSE)) != 0) {
+ PEMerr(PEM_F_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
+ memset(buf, 0, (unsigned int)bufsize);
+ return (-1);
+ }
+ len = strlen(buf);
+ if (len < 1)
+ fprintf(stderr, "Apache:mod_ssl:Error: Pass phrase empty (needs to be at least 1 character).\n");
+ else
+ break;
+ }
+
+ /*
+ * Restore STDERR to Apache error logfile
+ */
+ dup2(stderr_store, STDERR_FILENO);
+ close(stderr_store);
+#ifdef WIN32
+ if (con != NULL)
+ fclose(con);
+#endif
+ }
+
+ /*
+ * Filter program
+ */
+ else if (sc->nPassPhraseDialogType == SSL_PPTYPE_FILTER) {
+ char *cmd;
+ char *result;
+
+ ssl_log(s, SSL_LOG_INFO,
+ "Init: Requesting pass phrase from dialog filter program (%s)",
+ sc->szPassPhraseDialogPath);
+
+ if (strchr(sc->szPassPhraseDialogPath, ' ') != NULL)
+ cmd = ap_psprintf(p, "\"%s\" %s %s", sc->szPassPhraseDialogPath, cpVHostID, cpAlgoType);
+ else
+ cmd = ap_psprintf(p, "%s %s %s", sc->szPassPhraseDialogPath, cpVHostID, cpAlgoType);
+ result = ssl_util_readfilter(s, p, cmd);
+ ap_cpystrn(buf, result, bufsize);
+ len = strlen(buf);
+ }
+
+ /*
+ * Ok, we now have the pass phrase, so give it back
+ */
+ *cppPassPhraseCur = ap_pstrdup(p, buf);
+
+ /*
+ * And return it's length to OpenSSL...
+ */
+ return (len);
+}
+
diff --git a/modules/ssl/ssl_engine_rand.c b/modules/ssl/ssl_engine_rand.c
new file mode 100644
index 0000000000..afb49b4f5c
--- /dev/null
+++ b/modules/ssl/ssl_engine_rand.c
@@ -0,0 +1,215 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_engine_rand.c
+** Random Number Generator Seeding
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``The generation of random
+ numbers is too important
+ to be left to chance.'' */
+
+#include "mod_ssl.h"
+
+
+/* _________________________________________________________________
+**
+** Support for better seeding of SSL library's RNG
+** _________________________________________________________________
+*/
+
+static int ssl_rand_choosenum(int, int);
+static int ssl_rand_feedfp(pool *, FILE *, int);
+
+int ssl_rand_seed(server_rec *s, pool *p, ssl_rsctx_t nCtx, char *prefix)
+{
+ SSLModConfigRec *mc;
+ array_header *apRandSeed;
+ ssl_randseed_t *pRandSeeds;
+ ssl_randseed_t *pRandSeed;
+ unsigned char stackdata[256];
+ int nReq, nDone;
+ FILE *fp;
+ int i, n, l;
+ time_t t;
+ pid_t pid;
+
+ mc = myModConfig();
+ nReq = 0;
+ nDone = 0;
+ apRandSeed = mc->aRandSeed;
+ pRandSeeds = (ssl_randseed_t *)apRandSeed->elts;
+ for (i = 0; i < apRandSeed->nelts; i++) {
+ pRandSeed = &pRandSeeds[i];
+ if (pRandSeed->nCtx == nCtx) {
+ nReq += pRandSeed->nBytes;
+ if (pRandSeed->nSrc == SSL_RSSRC_FILE) {
+ /*
+ * seed in contents of an external file
+ */
+ if ((fp = ap_pfopen(p, pRandSeed->cpPath, "r")) == NULL)
+ continue;
+ nDone += ssl_rand_feedfp(p, fp, pRandSeed->nBytes);
+ ap_pfclose(p, fp);
+ }
+ else if (pRandSeed->nSrc == SSL_RSSRC_EXEC) {
+ /*
+ * seed in contents generated by an external program
+ */
+ if ((fp = ssl_util_ppopen(s, p, ap_psprintf(p, "%s %d",
+ pRandSeed->cpPath, pRandSeed->nBytes))) == NULL)
+ continue;
+ nDone += ssl_rand_feedfp(p, fp, pRandSeed->nBytes);
+ ssl_util_ppclose(s, p, fp);
+ }
+#if SSL_LIBRARY_VERSION >= 0x00905100
+ else if (pRandSeed->nSrc == SSL_RSSRC_EGD) {
+ /*
+ * seed in contents provided by the external
+ * Entropy Gathering Daemon (EGD)
+ */
+ if ((n = RAND_egd(pRandSeed->cpPath)) == -1)
+ continue;
+ nDone += n;
+ }
+#endif
+ else if (pRandSeed->nSrc == SSL_RSSRC_BUILTIN) {
+ /*
+ * seed in the current time (usually just 4 bytes)
+ */
+ t = time(NULL);
+ l = sizeof(time_t);
+ RAND_seed((unsigned char *)&t, l);
+ nDone += l;
+
+ /*
+ * seed in the current process id (usually just 4 bytes)
+ */
+ pid = getpid();
+ l = sizeof(pid_t);
+ RAND_seed((unsigned char *)&pid, l);
+ nDone += l;
+
+ /*
+ * seed in some current state of the run-time stack (128 bytes)
+ */
+ n = ssl_rand_choosenum(0, sizeof(stackdata)-128-1);
+ RAND_seed(stackdata+n, 128);
+ nDone += 128;
+
+ /*
+ * seed in an 1KB extract of the current scoreboard
+ */
+ if (ap_scoreboard_image != NULL) {
+ n = ssl_rand_choosenum(0, SCOREBOARD_SIZE-1024-1);
+ RAND_seed((unsigned char *)ap_scoreboard_image+n, 1024);
+ nDone += 1024;
+ }
+ }
+ }
+ }
+ ssl_log(s, SSL_LOG_INFO, "%sSeeding PRNG with %d bytes of entropy", prefix, nDone);
+
+#if SSL_LIBRARY_VERSION >= 0x00905100
+ if (RAND_status() == 0)
+ ssl_log(s, SSL_LOG_WARN, "%sPRNG still contains not sufficient entropy!", prefix);
+#endif
+ return nDone;
+}
+
+#define BUFSIZE 8192
+
+static int ssl_rand_feedfp(pool *p, FILE *fp, int nReq)
+{
+ int nDone;
+ unsigned char caBuf[BUFSIZE];
+ int nBuf;
+ int nRead;
+ int nTodo;
+
+ nDone = 0;
+ nRead = BUFSIZE;
+ nTodo = nReq;
+ while (1) {
+ if (nReq > 0)
+ nRead = (nTodo < BUFSIZE ? nTodo : BUFSIZE);
+ if ((nBuf = (int)fread(caBuf, 1, nRead, fp)) <= 0)
+ break;
+ RAND_seed(caBuf, nBuf);
+ nDone += nBuf;
+ if (nReq > 0) {
+ nTodo -= nBuf;
+ if (nTodo <= 0)
+ break;
+ }
+ }
+ return nDone;
+}
+
+static int ssl_rand_choosenum(int l, int h)
+{
+ int i;
+ char buf[50];
+
+ srand((unsigned int)time(NULL));
+ ap_snprintf(buf, sizeof(buf), "%.0f",
+ (((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l)));
+ i = atoi(buf)+1;
+ if (i < l) i = l;
+ if (i > h) i = h;
+ return i;
+}
+
diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c
new file mode 100644
index 0000000000..c0755a5ec8
--- /dev/null
+++ b/modules/ssl/ssl_engine_vars.c
@@ -0,0 +1,615 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_engine_vars.c
+** Variable Lookup Facility
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``Those of you who think they
+ know everything are very annoying
+ to those of us who do.''
+ -- Unknown */
+#include "mod_ssl.h"
+
+
+/* _________________________________________________________________
+**
+** Variable Lookup
+** _________________________________________________________________
+*/
+
+static char *ssl_var_lookup_header(pool *p, request_rec *r, const char *name);
+static char *ssl_var_lookup_ssl(pool *p, conn_rec *c, char *var);
+static char *ssl_var_lookup_ssl_cert(pool *p, X509 *xs, char *var);
+static char *ssl_var_lookup_ssl_cert_dn(pool *p, X509_NAME *xsname, char *var);
+static char *ssl_var_lookup_ssl_cert_valid(pool *p, ASN1_UTCTIME *tm);
+static char *ssl_var_lookup_ssl_cert_serial(pool *p, X509 *xs);
+static char *ssl_var_lookup_ssl_cert_chain(pool *p, STACK_OF(X509) *sk, char *var);
+static char *ssl_var_lookup_ssl_cert_PEM(pool *p, X509 *xs);
+static char *ssl_var_lookup_ssl_cert_verify(pool *p, conn_rec *c);
+static char *ssl_var_lookup_ssl_cipher(pool *p, conn_rec *c, char *var);
+static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
+static char *ssl_var_lookup_ssl_version(pool *p, char *var);
+
+void ssl_var_register(void)
+{
+ ap_hook_configure("ap::mod_ssl::var_lookup",
+ AP_HOOK_SIG6(ptr,ptr,ptr,ptr,ptr,ptr), AP_HOOK_DECLINE(NULL));
+ ap_hook_register("ap::mod_ssl::var_lookup",
+ ssl_var_lookup, AP_HOOK_NOCTX);
+ return;
+}
+
+void ssl_var_unregister(void)
+{
+ ap_hook_unregister("ap::mod_ssl::var_lookup", ssl_var_lookup);
+ return;
+}
+
+char *ssl_var_lookup(pool *p, server_rec *s, conn_rec *c, request_rec *r, char *var)
+{
+ SSLModConfigRec *mc = myModConfig();
+ char *result;
+ BOOL resdup;
+ time_t tc;
+ struct tm *tm;
+
+ result = NULL;
+ resdup = TRUE;
+
+ /*
+ * When no pool is given try to find one
+ */
+ if (p == NULL) {
+ if (r != NULL)
+ p = r->pool;
+ else if (c != NULL)
+ p = c->pool;
+ else
+ p = mc->pPool;
+ }
+
+ /*
+ * Request dependent stuff
+ */
+ if (r != NULL) {
+ if (strcEQ(var, "HTTP_USER_AGENT"))
+ result = ssl_var_lookup_header(p, r, "User-Agent");
+ else if (strcEQ(var, "HTTP_REFERER"))
+ result = ssl_var_lookup_header(p, r, "Referer");
+ else if (strcEQ(var, "HTTP_COOKIE"))
+ result = ssl_var_lookup_header(p, r, "Cookie");
+ else if (strcEQ(var, "HTTP_FORWARDED"))
+ result = ssl_var_lookup_header(p, r, "Forwarded");
+ else if (strcEQ(var, "HTTP_HOST"))
+ result = ssl_var_lookup_header(p, r, "Host");
+ else if (strcEQ(var, "HTTP_PROXY_CONNECTION"))
+ result = ssl_var_lookup_header(p, r, "Proxy-Connection");
+ else if (strcEQ(var, "HTTP_ACCEPT"))
+ result = ssl_var_lookup_header(p, r, "Accept");
+ else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5))
+ /* all other headers from which we are still not know about */
+ result = ssl_var_lookup_header(p, r, var+5);
+ else if (strcEQ(var, "THE_REQUEST"))
+ result = r->the_request;
+ else if (strcEQ(var, "REQUEST_METHOD"))
+ result = (char *)(r->method);
+ else if (strcEQ(var, "REQUEST_SCHEME"))
+ result = ap_http_method(r);
+ else if (strcEQ(var, "REQUEST_URI"))
+ result = r->uri;
+ else if (strcEQ(var, "SCRIPT_FILENAME") ||
+ strcEQ(var, "REQUEST_FILENAME"))
+ result = r->filename;
+ else if (strcEQ(var, "PATH_INFO"))
+ result = r->path_info;
+ else if (strcEQ(var, "QUERY_STRING"))
+ result = r->args;
+ else if (strcEQ(var, "REMOTE_HOST"))
+ result = (char *)ap_get_remote_host(r->connection,
+ r->per_dir_config, REMOTE_NAME);
+ else if (strcEQ(var, "REMOTE_IDENT"))
+ result = (char *)ap_get_remote_logname(r);
+ else if (strcEQ(var, "IS_SUBREQ"))
+ result = (r->main != NULL ? "true" : "false");
+ else if (strcEQ(var, "DOCUMENT_ROOT"))
+ result = (char *)ap_document_root(r);
+ else if (strcEQ(var, "SERVER_ADMIN"))
+ result = r->server->server_admin;
+ else if (strcEQ(var, "SERVER_NAME"))
+ result = (char *)ap_get_server_name(r);
+ else if (strcEQ(var, "SERVER_PORT"))
+ result = ap_psprintf(p, "%u", ap_get_server_port(r));
+ else if (strcEQ(var, "SERVER_PROTOCOL"))
+ result = r->protocol;
+ }
+
+ /*
+ * Connection stuff
+ */
+ if (result == NULL && c != NULL) {
+ if (strcEQ(var, "REMOTE_ADDR"))
+ result = c->remote_ip;
+ else if (strcEQ(var, "REMOTE_USER"))
+ result = c->user;
+ else if (strcEQ(var, "AUTH_TYPE"))
+ result = c->ap_auth_type;
+ else if (strlen(var) > 4 && strcEQn(var, "SSL_", 4))
+ result = ssl_var_lookup_ssl(p, c, var+4);
+ else if (strcEQ(var, "HTTPS")) {
+ if (ap_ctx_get(c->client->ctx, "ssl") != NULL)
+ result = "on";
+ else
+ result = "off";
+ }
+ }
+
+ /*
+ * Totally independent stuff
+ */
+ if (result == NULL) {
+ if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12))
+ result = ssl_var_lookup_ssl_version(p, var+12);
+ else if (strcEQ(var, "SERVER_SOFTWARE"))
+ result = (char *)ap_get_server_version();
+ else if (strcEQ(var, "API_VERSION")) {
+ result = ap_psprintf(p, "%d", MODULE_MAGIC_NUMBER);
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "TIME_YEAR")) {
+ tc = time(NULL);
+ tm = localtime(&tc);
+ result = ap_psprintf(p, "%02d%02d",
+ (tm->tm_year / 100) + 19, tm->tm_year % 100);
+ resdup = FALSE;
+ }
+#define MKTIMESTR(format, tmfield) \
+ tc = time(NULL); \
+ tm = localtime(&tc); \
+ result = ap_psprintf(p, format, tm->tmfield); \
+ resdup = FALSE;
+ else if (strcEQ(var, "TIME_MON")) {
+ MKTIMESTR("%02d", tm_mon+1)
+ }
+ else if (strcEQ(var, "TIME_DAY")) {
+ MKTIMESTR("%02d", tm_mday)
+ }
+ else if (strcEQ(var, "TIME_HOUR")) {
+ MKTIMESTR("%02d", tm_hour)
+ }
+ else if (strcEQ(var, "TIME_MIN")) {
+ MKTIMESTR("%02d", tm_min)
+ }
+ else if (strcEQ(var, "TIME_SEC")) {
+ MKTIMESTR("%02d", tm_sec)
+ }
+ else if (strcEQ(var, "TIME_WDAY")) {
+ MKTIMESTR("%d", tm_wday)
+ }
+ else if (strcEQ(var, "TIME")) {
+ tc = time(NULL);
+ tm = localtime(&tc);
+ result = ap_psprintf(p,
+ "%02d%02d%02d%02d%02d%02d%02d", (tm->tm_year / 100) + 19,
+ (tm->tm_year % 100), tm->tm_mon+1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ resdup = FALSE;
+ }
+ /* all other env-variables from the parent Apache process */
+ else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) {
+ result = (char *)ap_table_get(r->notes, var+4);
+ if (result == NULL)
+ result = (char *)ap_table_get(r->subprocess_env, var+4);
+ if (result == NULL)
+ result = getenv(var+4);
+ }
+ }
+
+ if (result != NULL && resdup)
+ result = ap_pstrdup(p, result);
+ if (result == NULL)
+ result = "";
+ return result;
+}
+
+static char *ssl_var_lookup_header(pool *p, request_rec *r, const char *name)
+{
+ array_header *hdrs_arr;
+ table_entry *hdrs;
+ int i;
+
+ hdrs_arr = ap_table_elts(r->headers_in);
+ hdrs = (table_entry *)hdrs_arr->elts;
+ for (i = 0; i < hdrs_arr->nelts; ++i) {
+ if (hdrs[i].key == NULL)
+ continue;
+ if (strcEQ(hdrs[i].key, name))
+ return ap_pstrdup(p, hdrs[i].val);
+ }
+ return NULL;
+}
+
+static char *ssl_var_lookup_ssl(pool *p, conn_rec *c, char *var)
+{
+ char *result;
+ X509 *xs;
+ STACK_OF(X509) *sk;
+ SSL *ssl;
+
+ result = NULL;
+
+ ssl = ap_ctx_get(c->client->ctx, "ssl");
+ if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) {
+ result = ssl_var_lookup_ssl_version(p, var+8);
+ }
+ else if (ssl != NULL && strcEQ(var, "PROTOCOL")) {
+ result = (char *)SSL_get_version(ssl);
+ }
+ else if (ssl != NULL && strcEQ(var, "SESSION_ID")) {
+ SSL_SESSION *pSession = SSL_get_session(ssl);
+ result = ap_pstrdup(p, SSL_SESSION_id2sz(pSession->session_id,
+ pSession->session_id_length));
+ }
+ else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) {
+ result = ssl_var_lookup_ssl_cipher(p, c, var+6);
+ }
+ else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) {
+ sk = SSL_get_peer_cert_chain(ssl);
+ result = ssl_var_lookup_ssl_cert_chain(p, sk, var+17);
+ }
+ else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) {
+ result = ssl_var_lookup_ssl_cert_verify(p, c);
+ }
+ else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) {
+ if ((xs = SSL_get_peer_certificate(ssl)) != NULL)
+ result = ssl_var_lookup_ssl_cert(p, xs, var+7);
+ }
+ else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) {
+ if ((xs = SSL_get_certificate(ssl)) != NULL)
+ result = ssl_var_lookup_ssl_cert(p, xs, var+7);
+ }
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cert(pool *p, X509 *xs, char *var)
+{
+ char *result;
+ BOOL resdup;
+ X509_NAME *xsname;
+ int nid;
+ char *cp;
+
+ result = NULL;
+ resdup = TRUE;
+
+ if (strcEQ(var, "M_VERSION")) {
+ result = ap_psprintf(p, "%lu", X509_get_version(xs)+1);
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "M_SERIAL")) {
+ result = ssl_var_lookup_ssl_cert_serial(p, xs);
+ }
+ else if (strcEQ(var, "V_START")) {
+ result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs));
+ }
+ else if (strcEQ(var, "V_END")) {
+ result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs));
+ }
+ else if (strcEQ(var, "S_DN")) {
+ xsname = X509_get_subject_name(xs);
+ cp = X509_NAME_oneline(xsname, NULL, 0);
+ result = ap_pstrdup(p, cp);
+ free(cp);
+ resdup = FALSE;
+ }
+ else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) {
+ xsname = X509_get_subject_name(xs);
+ result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "I_DN")) {
+ xsname = X509_get_issuer_name(xs);
+ cp = X509_NAME_oneline(xsname, NULL, 0);
+ result = ap_pstrdup(p, cp);
+ free(cp);
+ resdup = FALSE;
+ }
+ else if (strlen(var) > 5 && strcEQn(var, "I_DN_", 5)) {
+ xsname = X509_get_issuer_name(xs);
+ result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "A_SIG")) {
+ nid = OBJ_obj2nid(xs->cert_info->signature->algorithm);
+ result = ap_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "A_KEY")) {
+ nid = OBJ_obj2nid(xs->cert_info->key->algor->algorithm);
+ result = ap_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid));
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "CERT")) {
+ result = ssl_var_lookup_ssl_cert_PEM(p, xs);
+ }
+
+ if (result != NULL && resdup)
+ result = ap_pstrdup(p, result);
+ return result;
+}
+
+static const struct {
+ char *name;
+ int nid;
+} ssl_var_lookup_ssl_cert_dn_rec[] = {
+ { "C", NID_countryName },
+ { "ST", NID_stateOrProvinceName }, /* officially (RFC2156) */
+ { "SP", NID_stateOrProvinceName }, /* compatibility (SSLeay) */
+ { "L", NID_localityName },
+ { "O", NID_organizationName },
+ { "OU", NID_organizationalUnitName },
+ { "CN", NID_commonName },
+ { "T", NID_title },
+ { "I", NID_initials },
+ { "G", NID_givenName },
+ { "S", NID_surname },
+ { "D", NID_description },
+ { "UID", NID_uniqueIdentifier },
+ { "Email", NID_pkcs9_emailAddress },
+ { NULL, 0 }
+};
+
+static char *ssl_var_lookup_ssl_cert_dn(pool *p, X509_NAME *xsname, char *var)
+{
+ char *result;
+ X509_NAME_ENTRY *xsne;
+ int i, j, n;
+
+ result = NULL;
+
+ for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) {
+ if (strEQ(var, ssl_var_lookup_ssl_cert_dn_rec[i].name)) {
+ for (j = 0; j < sk_X509_NAME_ENTRY_num(xsname->entries); j++) {
+ xsne = sk_X509_NAME_ENTRY_value(xsname->entries, j);
+ n = OBJ_obj2nid(xsne->object);
+ if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid) {
+ result = ap_palloc(p, xsne->value->length+1);
+ ap_cpystrn(result, (char *)xsne->value->data, xsne->value->length+1);
+#ifdef CHARSET_EBCDIC
+ ascii2ebcdic(result, result, xsne->value->length);
+#endif /* CHARSET_EBCDIC */
+ result[xsne->value->length] = NUL;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_valid(pool *p, ASN1_UTCTIME *tm)
+{
+ char *result;
+ BIO* bio;
+ int n;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ return NULL;
+ ASN1_UTCTIME_print(bio, tm);
+ n = BIO_pending(bio);
+ result = ap_pcalloc(p, n+1);
+ n = BIO_read(bio, result, n);
+ result[n] = NUL;
+ BIO_free(bio);
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_serial(pool *p, X509 *xs)
+{
+ char *result;
+ BIO *bio;
+ int n;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ return NULL;
+ i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
+ n = BIO_pending(bio);
+ result = ap_pcalloc(p, n+1);
+ n = BIO_read(bio, result, n);
+ result[n] = NUL;
+ BIO_free(bio);
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_chain(pool *p, STACK_OF(X509) *sk, char *var)
+{
+ char *result;
+ X509 *xs;
+ int n;
+
+ result = NULL;
+
+ if (strspn(var, "0123456789") == strlen(var)) {
+ n = atoi(var);
+ if (n < sk_X509_num(sk)) {
+ xs = sk_X509_value(sk, n);
+ result = ssl_var_lookup_ssl_cert_PEM(p, xs);
+ }
+ }
+
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_PEM(pool *p, X509 *xs)
+{
+ char *result;
+ BIO *bio;
+ int n;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ return NULL;
+ PEM_write_bio_X509(bio, xs);
+ n = BIO_pending(bio);
+ result = ap_pcalloc(p, n+1);
+ n = BIO_read(bio, result, n);
+ result[n] = NUL;
+ BIO_free(bio);
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cert_verify(pool *p, conn_rec *c)
+{
+ char *result;
+ long vrc;
+ char *verr;
+ char *vinfo;
+ SSL *ssl;
+ X509 *xs;
+
+ result = NULL;
+ ssl = ap_ctx_get(c->client->ctx, "ssl");
+ verr = ap_ctx_get(c->client->ctx, "ssl::verify::error");
+ vinfo = ap_ctx_get(c->client->ctx, "ssl::verify::info");
+ vrc = SSL_get_verify_result(ssl);
+ xs = SSL_get_peer_certificate(ssl);
+
+ if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs == NULL)
+ /* no client verification done at all */
+ result = "NONE";
+ else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL)
+ /* client verification done successful */
+ result = "SUCCESS";
+ else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS"))
+ /* client verification done in generous way */
+ result = "GENEROUS";
+ else
+ /* client verification failed */
+ result = ap_psprintf(p, "FAILED:%s", verr);
+ return result;
+}
+
+static char *ssl_var_lookup_ssl_cipher(pool *p, conn_rec *c, char *var)
+{
+ char *result;
+ BOOL resdup;
+ int usekeysize, algkeysize;
+ SSL *ssl;
+
+ result = NULL;
+ resdup = TRUE;
+
+ ssl = ap_ctx_get(c->client->ctx, "ssl");
+ ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
+
+ if (strEQ(var, ""))
+ result = (ssl != NULL ? (char *)SSL_get_cipher_name(ssl) : NULL);
+ else if (strcEQ(var, "_EXPORT"))
+ result = (usekeysize < 56 ? "true" : "false");
+ else if (strcEQ(var, "_USEKEYSIZE")) {
+ result = ap_psprintf(p, "%d", usekeysize);
+ resdup = FALSE;
+ }
+ else if (strcEQ(var, "_ALGKEYSIZE")) {
+ result = ap_psprintf(p, "%d", algkeysize);
+ resdup = FALSE;
+ }
+
+ if (result != NULL && resdup)
+ result = ap_pstrdup(p, result);
+ return result;
+}
+
+static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
+{
+ SSL_CIPHER *cipher;
+
+ *usekeysize = 0;
+ *algkeysize = 0;
+ if (ssl != NULL)
+ if ((cipher = SSL_get_current_cipher(ssl)) != NULL)
+ *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize);
+ return;
+}
+
+static char *ssl_var_lookup_ssl_version(pool *p, char *var)
+{
+ char *result;
+ char *cp, *cp2;
+
+ result = NULL;
+
+ if (strEQ(var, "PRODUCT")) {
+#if defined(SSL_PRODUCT_NAME) && defined(SSL_PRODUCT_VERSION)
+ result = ap_psprintf(p, "%s/%s", SSL_PRODUCT_NAME, SSL_PRODUCT_VERSION);
+#else
+ result = NULL;
+#endif
+ }
+ else if (strEQ(var, "INTERFACE")) {
+ result = ap_psprintf(p, "mod_ssl/%s", MOD_SSL_VERSION);
+ }
+ else if (strEQ(var, "LIBRARY")) {
+ result = ap_pstrdup(p, SSL_LIBRARY_TEXT);
+ if ((cp = strchr(result, ' ')) != NULL) {
+ *cp = '/';
+ if ((cp2 = strchr(cp, ' ')) != NULL)
+ *cp2 = NUL;
+ }
+ }
+ return result;
+}
+
diff --git a/modules/ssl/ssl_expr.c b/modules/ssl/ssl_expr.c
new file mode 100644
index 0000000000..49ab873ded
--- /dev/null
+++ b/modules/ssl/ssl_expr.c
@@ -0,0 +1,119 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_expr.c
+** Expression Handling
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``It is hard to fly with
+ the eagles when you work
+ with the turkeys.''
+ -- Unknown */
+#include "mod_ssl.h"
+
+
+/* _________________________________________________________________
+**
+** Expression Handling
+** _________________________________________________________________
+*/
+
+ssl_expr_info_type ssl_expr_info;
+char *ssl_expr_error;
+
+ssl_expr *ssl_expr_comp(pool *p, char *expr)
+{
+ ssl_expr_info.pool = p;
+ ssl_expr_info.inputbuf = expr;
+ ssl_expr_info.inputlen = strlen(expr);
+ ssl_expr_info.inputptr = ssl_expr_info.inputbuf;
+ ssl_expr_info.expr = FALSE;
+
+ ssl_expr_error = NULL;
+ if (ssl_expr_yyparse())
+ return NULL;
+ return ssl_expr_info.expr;
+}
+
+char *ssl_expr_get_error(void)
+{
+ if (ssl_expr_error == NULL)
+ return "";
+ return ssl_expr_error;
+}
+
+ssl_expr *ssl_expr_make(ssl_expr_node_op op, void *a1, void *a2)
+{
+ ssl_expr *node;
+
+ node = (ssl_expr *)ap_palloc(ssl_expr_info.pool, sizeof(ssl_expr));
+ node->node_op = op;
+ node->node_arg1 = (char *)a1;
+ node->node_arg2 = (char *)a2;
+ return node;
+}
+
+int ssl_expr_exec(request_rec *r, ssl_expr *expr)
+{
+ BOOL rc;
+
+ rc = ssl_expr_eval(r, expr);
+ if (ssl_expr_error != NULL)
+ return (-1);
+ else
+ return (rc ? 1 : 0);
+}
+
diff --git a/modules/ssl/ssl_expr.h b/modules/ssl/ssl_expr.h
new file mode 100644
index 0000000000..419bb02192
--- /dev/null
+++ b/modules/ssl/ssl_expr.h
@@ -0,0 +1,139 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_expr.h
+** Expression Handling (Header)
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+ /* ``May all your PUSHes be POPed.'' */
+
+#ifndef SSL_EXPR_H
+#define SSL_EXPR_H
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE !FALSE
+#endif
+
+#ifndef YY_NULL
+#define YY_NULL 0
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#ifndef BOOL
+#define BOOL unsigned int
+#endif
+
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+#ifndef NUL
+#define NUL '\0'
+#endif
+
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+
+typedef enum {
+ op_NOP, op_ListElement,
+ op_True, op_False, op_Not, op_Or, op_And, op_Comp,
+ op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE, op_IN, op_REG, op_NRE,
+ op_Digit, op_String, op_Regex, op_Var, op_Func
+} ssl_expr_node_op;
+
+typedef struct {
+ ssl_expr_node_op node_op;
+ void *node_arg1;
+ void *node_arg2;
+} ssl_expr_node;
+
+typedef ssl_expr_node ssl_expr;
+
+typedef struct {
+ pool *pool;
+ char *inputbuf;
+ int inputlen;
+ char *inputptr;
+ ssl_expr *expr;
+} ssl_expr_info_type;
+
+extern ssl_expr_info_type ssl_expr_info;
+extern char *ssl_expr_error;
+
+#define yylval ssl_expr_yylval
+#define yyerror ssl_expr_yyerror
+#define yyinput ssl_expr_yyinput
+
+extern int ssl_expr_yyparse(void);
+extern int ssl_expr_yyerror(char *);
+extern int ssl_expr_yylex(void);
+
+extern ssl_expr *ssl_expr_comp(pool *, char *);
+extern int ssl_expr_exec(request_rec *, ssl_expr *);
+extern char *ssl_expr_get_error(void);
+extern ssl_expr *ssl_expr_make(ssl_expr_node_op, void *, void *);
+extern BOOL ssl_expr_eval(request_rec *, ssl_expr *);
+
+#endif /* SSL_EXPR_H */
diff --git a/modules/ssl/ssl_expr_eval.c b/modules/ssl/ssl_expr_eval.c
new file mode 100644
index 0000000000..d8c5ea5f9e
--- /dev/null
+++ b/modules/ssl/ssl_expr_eval.c
@@ -0,0 +1,282 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_expr_eval.c
+** Expression Evaluation
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``Make love,
+ not software!''
+ -- Unknown */
+#include "mod_ssl.h"
+
+
+/* _________________________________________________________________
+**
+** Expression Evaluation
+** _________________________________________________________________
+*/
+
+static BOOL ssl_expr_eval_comp(request_rec *, ssl_expr *);
+static char *ssl_expr_eval_word(request_rec *, ssl_expr *);
+static char *ssl_expr_eval_func_file(request_rec *, char *);
+static int ssl_expr_eval_strcmplex(char *, char *);
+
+BOOL ssl_expr_eval(request_rec *r, ssl_expr *node)
+{
+ switch (node->node_op) {
+ case op_True: {
+ return TRUE;
+ }
+ case op_False: {
+ return FALSE;
+ }
+ case op_Not: {
+ ssl_expr *e = (ssl_expr *)node->node_arg1;
+ return (!ssl_expr_eval(r, e));
+ }
+ case op_Or: {
+ ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+ ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+ return (ssl_expr_eval(r, e1) || ssl_expr_eval(r, e2));
+ }
+ case op_And: {
+ ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+ ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+ return (ssl_expr_eval(r, e1) && ssl_expr_eval(r, e2));
+ }
+ case op_Comp: {
+ ssl_expr *e = (ssl_expr *)node->node_arg1;
+ return ssl_expr_eval_comp(r, e);
+ }
+ default: {
+ ssl_expr_error = "Internal evaluation error: Unknown expression node";
+ return FALSE;
+ }
+ }
+}
+
+static BOOL ssl_expr_eval_comp(request_rec *r, ssl_expr *node)
+{
+ switch (node->node_op) {
+ case op_EQ: {
+ ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+ ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+ return (strcmp(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) == 0);
+ }
+ case op_NE: {
+ ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+ ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+ return (strcmp(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) != 0);
+ }
+ case op_LT: {
+ ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+ ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+ return (ssl_expr_eval_strcmplex(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) < 0);
+ }
+ case op_LE: {
+ ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+ ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+ return (ssl_expr_eval_strcmplex(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) <= 0);
+ }
+ case op_GT: {
+ ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+ ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+ return (ssl_expr_eval_strcmplex(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) > 0);
+ }
+ case op_GE: {
+ ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+ ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+ return (ssl_expr_eval_strcmplex(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) >= 0);
+ }
+ case op_IN: {
+ ssl_expr *e1 = (ssl_expr *)node->node_arg1;
+ ssl_expr *e2 = (ssl_expr *)node->node_arg2;
+ ssl_expr *e3;
+ char *w1 = ssl_expr_eval_word(r, e1);
+ BOOL found = FALSE;
+ do {
+ e3 = (ssl_expr *)e2->node_arg1;
+ e2 = (ssl_expr *)e2->node_arg2;
+ if (strcmp(w1, ssl_expr_eval_word(r, e3)) == 0) {
+ found = TRUE;
+ break;
+ }
+ } while (e2 != NULL);
+ return found;
+ }
+ case op_REG: {
+ ssl_expr *e1;
+ ssl_expr *e2;
+ char *word;
+ regex_t *regex;
+
+ e1 = (ssl_expr *)node->node_arg1;
+ e2 = (ssl_expr *)node->node_arg2;
+ word = ssl_expr_eval_word(r, e1);
+ regex = (regex_t *)(e2->node_arg1);
+ return (regexec(regex, word, 0, NULL, 0) == 0);
+ }
+ case op_NRE: {
+ ssl_expr *e1;
+ ssl_expr *e2;
+ char *word;
+ regex_t *regex;
+
+ e1 = (ssl_expr *)node->node_arg1;
+ e2 = (ssl_expr *)node->node_arg2;
+ word = ssl_expr_eval_word(r, e1);
+ regex = (regex_t *)(e2->node_arg1);
+ return !(regexec(regex, word, 0, NULL, 0) == 0);
+ }
+ default: {
+ ssl_expr_error = "Internal evaluation error: Unknown expression node";
+ return FALSE;
+ }
+ }
+}
+
+static char *ssl_expr_eval_word(request_rec *r, ssl_expr *node)
+{
+ switch (node->node_op) {
+ case op_Digit: {
+ char *string = (char *)node->node_arg1;
+ return string;
+ }
+ case op_String: {
+ char *string = (char *)node->node_arg1;
+ return string;
+ }
+ case op_Var: {
+ char *var = (char *)node->node_arg1;
+ char *val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
+ return (val == NULL ? "" : val);
+ }
+ case op_Func: {
+ char *name = (char *)node->node_arg1;
+ ssl_expr *args = (ssl_expr *)node->node_arg2;
+ if (strEQ(name, "file"))
+ return ssl_expr_eval_func_file(r, (char *)(args->node_arg1));
+ else {
+ ssl_expr_error = "Internal evaluation error: Unknown function name";
+ return "";
+ }
+ }
+ default: {
+ ssl_expr_error = "Internal evaluation error: Unknown expression node";
+ return FALSE;
+ }
+ }
+}
+
+static char *ssl_expr_eval_func_file(request_rec *r, char *filename)
+{
+ FILE *fp;
+ char *buf;
+ int len;
+
+ if ((fp = ap_pfopen(r->pool, filename, "r")) == NULL) {
+ ssl_expr_error = "Cannot open file";
+ return "";
+ }
+ fseek(fp, 0, SEEK_END);
+ len = ftell(fp);
+ if (len == 0) {
+ buf = (char *)ap_palloc(r->pool, sizeof(char) * 1);
+ *buf = NUL;
+ }
+ else {
+ if ((buf = (char *)ap_palloc(r->pool, sizeof(char) * len+1)) == NULL) {
+ ssl_expr_error = "Cannot allocate memory";
+ ap_pfclose(r->pool, fp);
+ return "";
+ }
+ fseek(fp, 0, SEEK_SET);
+ if (fread(buf, len, 1, fp) == 0) {
+ ssl_expr_error = "Cannot read from file";
+ fclose(fp);
+ return ("");
+ }
+ buf[len] = NUL;
+ }
+ ap_pfclose(r->pool, fp);
+ return buf;
+}
+
+/* a variant of strcmp(3) which works correctly also for number strings */
+static int ssl_expr_eval_strcmplex(char *cpNum1, char *cpNum2)
+{
+ int i, n1, n2;
+
+ if (cpNum1 == NULL)
+ return -1;
+ if (cpNum2 == NULL)
+ return +1;
+ n1 = strlen(cpNum1);
+ n2 = strlen(cpNum2);
+ if (n1 > n2)
+ return 1;
+ if (n1 < n2)
+ return -1;
+ for (i = 0; i < n1; i++) {
+ if (cpNum1[i] > cpNum2[i])
+ return 1;
+ if (cpNum1[i] < cpNum2[i])
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/modules/ssl/ssl_expr_parse.c b/modules/ssl/ssl_expr_parse.c
new file mode 100644
index 0000000000..8e35553a4e
--- /dev/null
+++ b/modules/ssl/ssl_expr_parse.c
@@ -0,0 +1,605 @@
+#ifndef lint
+static char const
+ssl_expr_yyrcsid[] = "$FreeBSD: src/usr.bin/yacc/skeleton.c,v 1.28 2000/01/17 02:04:06 bde Exp $";
+#endif
+#include <stdlib.h>
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define YYLEX ssl_expr_yylex()
+#define YYEMPTY -1
+#define ssl_expr_yyclearin (ssl_expr_yychar=(YYEMPTY))
+#define ssl_expr_yyerrok (ssl_expr_yyerrflag=0)
+#define YYRECOVERING() (ssl_expr_yyerrflag!=0)
+static int ssl_expr_yygrowstack();
+#define YYPREFIX "ssl_expr_yy"
+#line 72 "ssl_expr_parse.y"
+#include "mod_ssl.h"
+#line 75 "ssl_expr_parse.y"
+typedef union {
+ char *cpVal;
+ ssl_expr *exVal;
+} YYSTYPE;
+#line 24 "y.tab.c"
+#define YYERRCODE 256
+#define T_TRUE 257
+#define T_FALSE 258
+#define T_DIGIT 259
+#define T_ID 260
+#define T_STRING 261
+#define T_REGEX 262
+#define T_REGEX_I 263
+#define T_FUNC_FILE 264
+#define T_OP_EQ 265
+#define T_OP_NE 266
+#define T_OP_LT 267
+#define T_OP_LE 268
+#define T_OP_GT 269
+#define T_OP_GE 270
+#define T_OP_REG 271
+#define T_OP_NRE 272
+#define T_OP_IN 273
+#define T_OP_OR 274
+#define T_OP_AND 275
+#define T_OP_NOT 276
+const short ssl_expr_yylhs[] = { -1,
+ 0, 1, 1, 1, 1, 1, 1, 1, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 5, 5, 6,
+ 6, 6, 6, 4, 4, 3,
+};
+const short ssl_expr_yylen[] = { 2,
+ 1, 1, 1, 2, 3, 3, 1, 3, 3, 3,
+ 3, 3, 3, 3, 5, 3, 3, 1, 3, 1,
+ 1, 4, 1, 1, 1, 4,
+};
+const short ssl_expr_yydefred[] = { 0,
+ 2, 3, 20, 21, 0, 0, 0, 0, 0, 0,
+ 7, 23, 0, 0, 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
+ 0, 0, 6, 9, 10, 11, 12, 13, 14, 24,
+ 25, 16, 17, 0, 26, 22, 0, 18, 15, 0,
+ 19,
+};
+const short ssl_expr_yydgoto[] = { 9,
+ 10, 11, 12, 42, 47, 13,
+};
+const short ssl_expr_yysindex[] = { -37,
+ 0, 0, 0, 0, -35, -37, -37, -99, 0, -247,
+ 0, 0, -250, -229, 0, -39, -227, -37, -37, -33,
+ -33, -33, -33, -33, -33, -233, -233, -89, -6, 0,
+ -87, -239, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, -33, 0, 0, -38, 0, 0, -33,
+ 0,
+};
+const short ssl_expr_yyrindex[] = { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 39,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+};
+const short ssl_expr_yygindex[] = { 0,
+ 7, 0, 0, 13, 0, -13,
+};
+#define YYTABLESIZE 275
+const short ssl_expr_yytable[] = { 8,
+ 5, 30, 7, 8, 14, 50, 34, 35, 36, 37,
+ 38, 39, 15, 16, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 17, 32, 33, 18, 19, 40, 41,
+ 48, 29, 31, 44, 45, 19, 51, 46, 1, 43,
+ 0, 5, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 49, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 2, 3, 0, 4, 0, 3, 5, 4, 0, 0,
+ 5, 0, 0, 0, 18, 19, 0, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 5,
+};
+const short ssl_expr_yycheck[] = { 37,
+ 0, 41, 40, 37, 40, 44, 20, 21, 22, 23,
+ 24, 25, 6, 7, 265, 266, 267, 268, 269, 270,
+ 271, 272, 273, 123, 18, 19, 274, 275, 262, 263,
+ 44, 261, 260, 123, 41, 275, 50, 125, 0, 27,
+ -1, 41, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 125, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 257,
+ 258, 259, -1, 261, -1, 259, 264, 261, -1, -1,
+ 264, -1, -1, -1, 274, 275, -1, -1, 276, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 274,
+};
+#define YYFINAL 9
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 276
+#if YYDEBUG
+const char * const ssl_expr_yyname[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,"'%'",0,0,"'('","')'",0,0,"','",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'{'",0,"'}'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"T_TRUE",
+"T_FALSE","T_DIGIT","T_ID","T_STRING","T_REGEX","T_REGEX_I","T_FUNC_FILE",
+"T_OP_EQ","T_OP_NE","T_OP_LT","T_OP_LE","T_OP_GT","T_OP_GE","T_OP_REG",
+"T_OP_NRE","T_OP_IN","T_OP_OR","T_OP_AND","T_OP_NOT",
+};
+const char * const ssl_expr_yyrule[] = {
+"$accept : root",
+"root : expr",
+"expr : T_TRUE",
+"expr : T_FALSE",
+"expr : T_OP_NOT expr",
+"expr : expr T_OP_OR expr",
+"expr : expr T_OP_AND expr",
+"expr : comparison",
+"expr : '(' expr ')'",
+"comparison : word T_OP_EQ word",
+"comparison : word T_OP_NE word",
+"comparison : word T_OP_LT word",
+"comparison : word T_OP_LE word",
+"comparison : word T_OP_GT word",
+"comparison : word T_OP_GE word",
+"comparison : word T_OP_IN '{' words '}'",
+"comparison : word T_OP_REG regex",
+"comparison : word T_OP_NRE regex",
+"words : word",
+"words : words ',' word",
+"word : T_DIGIT",
+"word : T_STRING",
+"word : '%' '{' T_ID '}'",
+"word : funccall",
+"regex : T_REGEX",
+"regex : T_REGEX_I",
+"funccall : T_FUNC_FILE '(' T_STRING ')'",
+};
+#endif
+#if YYDEBUG
+#include <stdio.h>
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 10000
+#define YYMAXDEPTH 10000
+#endif
+#endif
+#define YYINITSTACKSIZE 200
+int ssl_expr_yydebug;
+int ssl_expr_yynerrs;
+int ssl_expr_yyerrflag;
+int ssl_expr_yychar;
+short *ssl_expr_yyssp;
+YYSTYPE *ssl_expr_yyvsp;
+YYSTYPE ssl_expr_yyval;
+YYSTYPE ssl_expr_yylval;
+short *ssl_expr_yyss;
+short *ssl_expr_yysslim;
+YYSTYPE *ssl_expr_yyvs;
+int ssl_expr_yystacksize;
+#line 180 "ssl_expr_parse.y"
+
+int ssl_expr_yyerror(char *s)
+{
+ ssl_expr_error = s;
+ return 2;
+}
+
+#line 230 "y.tab.c"
+/* allocate initial stack or double stack size, up to YYMAXDEPTH */
+static int ssl_expr_yygrowstack()
+{
+ int newsize, i;
+ short *newss;
+ YYSTYPE *newvs;
+
+ if ((newsize = ssl_expr_yystacksize) == 0)
+ newsize = YYINITSTACKSIZE;
+ else if (newsize >= YYMAXDEPTH)
+ return -1;
+ else if ((newsize *= 2) > YYMAXDEPTH)
+ newsize = YYMAXDEPTH;
+ i = ssl_expr_yyssp - ssl_expr_yyss;
+ newss = ssl_expr_yyss ? (short *)realloc(ssl_expr_yyss, newsize * sizeof *newss) :
+ (short *)malloc(newsize * sizeof *newss);
+ if (newss == NULL)
+ return -1;
+ ssl_expr_yyss = newss;
+ ssl_expr_yyssp = newss + i;
+ newvs = ssl_expr_yyvs ? (YYSTYPE *)realloc(ssl_expr_yyvs, newsize * sizeof *newvs) :
+ (YYSTYPE *)malloc(newsize * sizeof *newvs);
+ if (newvs == NULL)
+ return -1;
+ ssl_expr_yyvs = newvs;
+ ssl_expr_yyvsp = newvs + i;
+ ssl_expr_yystacksize = newsize;
+ ssl_expr_yysslim = ssl_expr_yyss + newsize - 1;
+ return 0;
+}
+
+#define YYABORT goto ssl_expr_yyabort
+#define YYREJECT goto ssl_expr_yyabort
+#define YYACCEPT goto ssl_expr_yyaccept
+#define YYERROR goto ssl_expr_yyerrlab
+
+#ifndef YYPARSE_PARAM
+#if defined(__cplusplus) || __STDC__
+#define YYPARSE_PARAM_ARG void
+#define YYPARSE_PARAM_DECL
+#else /* ! ANSI-C/C++ */
+#define YYPARSE_PARAM_ARG
+#define YYPARSE_PARAM_DECL
+#endif /* ANSI-C/C++ */
+#else /* YYPARSE_PARAM */
+#ifndef YYPARSE_PARAM_TYPE
+#define YYPARSE_PARAM_TYPE void *
+#endif
+#if defined(__cplusplus) || __STDC__
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM_TYPE YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#else /* ! ANSI-C/C++ */
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL YYPARSE_PARAM_TYPE YYPARSE_PARAM;
+#endif /* ANSI-C/C++ */
+#endif /* ! YYPARSE_PARAM */
+
+int
+ssl_expr_yyparse (YYPARSE_PARAM_ARG)
+ YYPARSE_PARAM_DECL
+{
+ register int ssl_expr_yym, ssl_expr_yyn, ssl_expr_yystate;
+#if YYDEBUG
+ register const char *ssl_expr_yys;
+
+ if ((ssl_expr_yys = getenv("YYDEBUG")))
+ {
+ ssl_expr_yyn = *ssl_expr_yys;
+ if (ssl_expr_yyn >= '0' && ssl_expr_yyn <= '9')
+ ssl_expr_yydebug = ssl_expr_yyn - '0';
+ }
+#endif
+
+ ssl_expr_yynerrs = 0;
+ ssl_expr_yyerrflag = 0;
+ ssl_expr_yychar = (-1);
+
+ if (ssl_expr_yyss == NULL && ssl_expr_yygrowstack()) goto ssl_expr_yyoverflow;
+ ssl_expr_yyssp = ssl_expr_yyss;
+ ssl_expr_yyvsp = ssl_expr_yyvs;
+ *ssl_expr_yyssp = ssl_expr_yystate = 0;
+
+ssl_expr_yyloop:
+ if ((ssl_expr_yyn = ssl_expr_yydefred[ssl_expr_yystate])) goto ssl_expr_yyreduce;
+ if (ssl_expr_yychar < 0)
+ {
+ if ((ssl_expr_yychar = ssl_expr_yylex()) < 0) ssl_expr_yychar = 0;
+#if YYDEBUG
+ if (ssl_expr_yydebug)
+ {
+ ssl_expr_yys = 0;
+ if (ssl_expr_yychar <= YYMAXTOKEN) ssl_expr_yys = ssl_expr_yyname[ssl_expr_yychar];
+ if (!ssl_expr_yys) ssl_expr_yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, ssl_expr_yystate, ssl_expr_yychar, ssl_expr_yys);
+ }
+#endif
+ }
+ if ((ssl_expr_yyn = ssl_expr_yysindex[ssl_expr_yystate]) && (ssl_expr_yyn += ssl_expr_yychar) >= 0 &&
+ ssl_expr_yyn <= YYTABLESIZE && ssl_expr_yycheck[ssl_expr_yyn] == ssl_expr_yychar)
+ {
+#if YYDEBUG
+ if (ssl_expr_yydebug)
+ printf("%sdebug: state %d, shifting to state %d\n",
+ YYPREFIX, ssl_expr_yystate, ssl_expr_yytable[ssl_expr_yyn]);
+#endif
+ if (ssl_expr_yyssp >= ssl_expr_yysslim && ssl_expr_yygrowstack())
+ {
+ goto ssl_expr_yyoverflow;
+ }
+ *++ssl_expr_yyssp = ssl_expr_yystate = ssl_expr_yytable[ssl_expr_yyn];
+ *++ssl_expr_yyvsp = ssl_expr_yylval;
+ ssl_expr_yychar = (-1);
+ if (ssl_expr_yyerrflag > 0) --ssl_expr_yyerrflag;
+ goto ssl_expr_yyloop;
+ }
+ if ((ssl_expr_yyn = ssl_expr_yyrindex[ssl_expr_yystate]) && (ssl_expr_yyn += ssl_expr_yychar) >= 0 &&
+ ssl_expr_yyn <= YYTABLESIZE && ssl_expr_yycheck[ssl_expr_yyn] == ssl_expr_yychar)
+ {
+ ssl_expr_yyn = ssl_expr_yytable[ssl_expr_yyn];
+ goto ssl_expr_yyreduce;
+ }
+ if (ssl_expr_yyerrflag) goto ssl_expr_yyinrecovery;
+#if defined(lint) || defined(__GNUC__)
+ goto ssl_expr_yynewerror;
+#endif
+ssl_expr_yynewerror:
+ ssl_expr_yyerror("syntax error");
+#if defined(lint) || defined(__GNUC__)
+ goto ssl_expr_yyerrlab;
+#endif
+ssl_expr_yyerrlab:
+ ++ssl_expr_yynerrs;
+ssl_expr_yyinrecovery:
+ if (ssl_expr_yyerrflag < 3)
+ {
+ ssl_expr_yyerrflag = 3;
+ for (;;)
+ {
+ if ((ssl_expr_yyn = ssl_expr_yysindex[*ssl_expr_yyssp]) && (ssl_expr_yyn += YYERRCODE) >= 0 &&
+ ssl_expr_yyn <= YYTABLESIZE && ssl_expr_yycheck[ssl_expr_yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (ssl_expr_yydebug)
+ printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *ssl_expr_yyssp, ssl_expr_yytable[ssl_expr_yyn]);
+#endif
+ if (ssl_expr_yyssp >= ssl_expr_yysslim && ssl_expr_yygrowstack())
+ {
+ goto ssl_expr_yyoverflow;
+ }
+ *++ssl_expr_yyssp = ssl_expr_yystate = ssl_expr_yytable[ssl_expr_yyn];
+ *++ssl_expr_yyvsp = ssl_expr_yylval;
+ goto ssl_expr_yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (ssl_expr_yydebug)
+ printf("%sdebug: error recovery discarding state %d\n",
+ YYPREFIX, *ssl_expr_yyssp);
+#endif
+ if (ssl_expr_yyssp <= ssl_expr_yyss) goto ssl_expr_yyabort;
+ --ssl_expr_yyssp;
+ --ssl_expr_yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (ssl_expr_yychar == 0) goto ssl_expr_yyabort;
+#if YYDEBUG
+ if (ssl_expr_yydebug)
+ {
+ ssl_expr_yys = 0;
+ if (ssl_expr_yychar <= YYMAXTOKEN) ssl_expr_yys = ssl_expr_yyname[ssl_expr_yychar];
+ if (!ssl_expr_yys) ssl_expr_yys = "illegal-symbol";
+ printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+ YYPREFIX, ssl_expr_yystate, ssl_expr_yychar, ssl_expr_yys);
+ }
+#endif
+ ssl_expr_yychar = (-1);
+ goto ssl_expr_yyloop;
+ }
+ssl_expr_yyreduce:
+#if YYDEBUG
+ if (ssl_expr_yydebug)
+ printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+ YYPREFIX, ssl_expr_yystate, ssl_expr_yyn, ssl_expr_yyrule[ssl_expr_yyn]);
+#endif
+ ssl_expr_yym = ssl_expr_yylen[ssl_expr_yyn];
+ ssl_expr_yyval = ssl_expr_yyvsp[1-ssl_expr_yym];
+ switch (ssl_expr_yyn)
+ {
+case 1:
+#line 118 "ssl_expr_parse.y"
+{ ssl_expr_info.expr = ssl_expr_yyvsp[0].exVal; }
+break;
+case 2:
+#line 121 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_True, NULL, NULL); }
+break;
+case 3:
+#line 122 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_False, NULL, NULL); }
+break;
+case 4:
+#line 123 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_Not, ssl_expr_yyvsp[0].exVal, NULL); }
+break;
+case 5:
+#line 124 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_Or, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 6:
+#line 125 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_And, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 7:
+#line 126 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_Comp, ssl_expr_yyvsp[0].exVal, NULL); }
+break;
+case 8:
+#line 127 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_yyvsp[-1].exVal; }
+break;
+case 9:
+#line 130 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_EQ, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 10:
+#line 131 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_NE, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 11:
+#line 132 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_LT, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 12:
+#line 133 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_LE, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 13:
+#line 134 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_GT, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 14:
+#line 135 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_GE, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 15:
+#line 136 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_IN, ssl_expr_yyvsp[-4].exVal, ssl_expr_yyvsp[-1].exVal); }
+break;
+case 16:
+#line 137 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_REG, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 17:
+#line 138 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_NRE, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); }
+break;
+case 18:
+#line 141 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_ListElement, ssl_expr_yyvsp[0].exVal, NULL); }
+break;
+case 19:
+#line 142 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_ListElement, ssl_expr_yyvsp[0].exVal, ssl_expr_yyvsp[-2].exVal); }
+break;
+case 20:
+#line 145 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_Digit, ssl_expr_yyvsp[0].cpVal, NULL); }
+break;
+case 21:
+#line 146 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_String, ssl_expr_yyvsp[0].cpVal, NULL); }
+break;
+case 22:
+#line 147 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_make(op_Var, ssl_expr_yyvsp[-1].cpVal, NULL); }
+break;
+case 23:
+#line 148 "ssl_expr_parse.y"
+{ ssl_expr_yyval.exVal = ssl_expr_yyvsp[0].exVal; }
+break;
+case 24:
+#line 151 "ssl_expr_parse.y"
+{
+ regex_t *regex;
+ if ((regex = ap_pregcomp(ssl_expr_info.pool, ssl_expr_yyvsp[0].cpVal,
+ REG_EXTENDED|REG_NOSUB)) == NULL) {
+ ssl_expr_error = "Failed to compile regular expression";
+ YYERROR;
+ regex = NULL;
+ }
+ ssl_expr_yyval.exVal = ssl_expr_make(op_Regex, regex, NULL);
+ }
+break;
+case 25:
+#line 161 "ssl_expr_parse.y"
+{
+ regex_t *regex;
+ if ((regex = ap_pregcomp(ssl_expr_info.pool, ssl_expr_yyvsp[0].cpVal,
+ REG_EXTENDED|REG_NOSUB|REG_ICASE)) == NULL) {
+ ssl_expr_error = "Failed to compile regular expression";
+ YYERROR;
+ regex = NULL;
+ }
+ ssl_expr_yyval.exVal = ssl_expr_make(op_Regex, regex, NULL);
+ }
+break;
+case 26:
+#line 173 "ssl_expr_parse.y"
+{
+ ssl_expr *args = ssl_expr_make(op_ListElement, ssl_expr_yyvsp[-1].cpVal, NULL);
+ ssl_expr_yyval.exVal = ssl_expr_make(op_Func, "file", args);
+ }
+break;
+#line 550 "y.tab.c"
+ }
+ ssl_expr_yyssp -= ssl_expr_yym;
+ ssl_expr_yystate = *ssl_expr_yyssp;
+ ssl_expr_yyvsp -= ssl_expr_yym;
+ ssl_expr_yym = ssl_expr_yylhs[ssl_expr_yyn];
+ if (ssl_expr_yystate == 0 && ssl_expr_yym == 0)
+ {
+#if YYDEBUG
+ if (ssl_expr_yydebug)
+ printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+ ssl_expr_yystate = YYFINAL;
+ *++ssl_expr_yyssp = YYFINAL;
+ *++ssl_expr_yyvsp = ssl_expr_yyval;
+ if (ssl_expr_yychar < 0)
+ {
+ if ((ssl_expr_yychar = ssl_expr_yylex()) < 0) ssl_expr_yychar = 0;
+#if YYDEBUG
+ if (ssl_expr_yydebug)
+ {
+ ssl_expr_yys = 0;
+ if (ssl_expr_yychar <= YYMAXTOKEN) ssl_expr_yys = ssl_expr_yyname[ssl_expr_yychar];
+ if (!ssl_expr_yys) ssl_expr_yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, YYFINAL, ssl_expr_yychar, ssl_expr_yys);
+ }
+#endif
+ }
+ if (ssl_expr_yychar == 0) goto ssl_expr_yyaccept;
+ goto ssl_expr_yyloop;
+ }
+ if ((ssl_expr_yyn = ssl_expr_yygindex[ssl_expr_yym]) && (ssl_expr_yyn += ssl_expr_yystate) >= 0 &&
+ ssl_expr_yyn <= YYTABLESIZE && ssl_expr_yycheck[ssl_expr_yyn] == ssl_expr_yystate)
+ ssl_expr_yystate = ssl_expr_yytable[ssl_expr_yyn];
+ else
+ ssl_expr_yystate = ssl_expr_yydgoto[ssl_expr_yym];
+#if YYDEBUG
+ if (ssl_expr_yydebug)
+ printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *ssl_expr_yyssp, ssl_expr_yystate);
+#endif
+ if (ssl_expr_yyssp >= ssl_expr_yysslim && ssl_expr_yygrowstack())
+ {
+ goto ssl_expr_yyoverflow;
+ }
+ *++ssl_expr_yyssp = ssl_expr_yystate;
+ *++ssl_expr_yyvsp = ssl_expr_yyval;
+ goto ssl_expr_yyloop;
+ssl_expr_yyoverflow:
+ ssl_expr_yyerror("yacc stack overflow");
+ssl_expr_yyabort:
+ return (1);
+ssl_expr_yyaccept:
+ return (0);
+}
diff --git a/modules/ssl/ssl_expr_parse.h b/modules/ssl/ssl_expr_parse.h
new file mode 100644
index 0000000000..618cacbe3b
--- /dev/null
+++ b/modules/ssl/ssl_expr_parse.h
@@ -0,0 +1,29 @@
+#ifndef YYERRCODE
+#define YYERRCODE 256
+#endif
+
+#define T_TRUE 257
+#define T_FALSE 258
+#define T_DIGIT 259
+#define T_ID 260
+#define T_STRING 261
+#define T_REGEX 262
+#define T_REGEX_I 263
+#define T_FUNC_FILE 264
+#define T_OP_EQ 265
+#define T_OP_NE 266
+#define T_OP_LT 267
+#define T_OP_LE 268
+#define T_OP_GT 269
+#define T_OP_GE 270
+#define T_OP_REG 271
+#define T_OP_NRE 272
+#define T_OP_IN 273
+#define T_OP_OR 274
+#define T_OP_AND 275
+#define T_OP_NOT 276
+typedef union {
+ char *cpVal;
+ ssl_expr *exVal;
+} YYSTYPE;
+extern YYSTYPE ssl_expr_yylval;
diff --git a/modules/ssl/ssl_expr_parse.y b/modules/ssl/ssl_expr_parse.y
new file mode 100644
index 0000000000..1e3ad6e513
--- /dev/null
+++ b/modules/ssl/ssl_expr_parse.y
@@ -0,0 +1,186 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| |
+** | '_ ` _ \ / _ \ / _` | / __/ __| |
+** | | | | | | (_) | (_| | \__ \__ \ | mod_ssl - Apache Interface to OpenSSL
+** |_| |_| |_|\___/ \__,_|___|___/___/_| http://www.modssl.org/
+** |_____|
+** ssl_expr_parse.y
+** Expression LR(1) Parser
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+ /* ``What you see is all you get.''
+ -- Brian Kernighan */
+
+/* _________________________________________________________________
+**
+** Expression Parser
+** _________________________________________________________________
+*/
+
+%{
+#include "mod_ssl.h"
+%}
+
+%union {
+ char *cpVal;
+ ssl_expr *exVal;
+}
+
+%token T_TRUE
+%token T_FALSE
+
+%token <cpVal> T_DIGIT
+%token <cpVal> T_ID
+%token <cpVal> T_STRING
+%token <cpVal> T_REGEX
+%token <cpVal> T_REGEX_I
+
+%token T_FUNC_FILE
+
+%token T_OP_EQ
+%token T_OP_NE
+%token T_OP_LT
+%token T_OP_LE
+%token T_OP_GT
+%token T_OP_GE
+%token T_OP_REG
+%token T_OP_NRE
+%token T_OP_IN
+
+%token T_OP_OR
+%token T_OP_AND
+%token T_OP_NOT
+
+%left T_OP_OR
+%left T_OP_AND
+%left T_OP_NOT
+
+%type <exVal> expr
+%type <exVal> comparison
+%type <exVal> funccall
+%type <exVal> regex
+%type <exVal> words
+%type <exVal> word
+
+%%
+
+root : expr { ssl_expr_info.expr = $1; }
+ ;
+
+expr : T_TRUE { $$ = ssl_expr_make(op_True, NULL, NULL); }
+ | T_FALSE { $$ = ssl_expr_make(op_False, NULL, NULL); }
+ | T_OP_NOT expr { $$ = ssl_expr_make(op_Not, $2, NULL); }
+ | expr T_OP_OR expr { $$ = ssl_expr_make(op_Or, $1, $3); }
+ | expr T_OP_AND expr { $$ = ssl_expr_make(op_And, $1, $3); }
+ | comparison { $$ = ssl_expr_make(op_Comp, $1, NULL); }
+ | '(' expr ')' { $$ = $2; }
+ ;
+
+comparison: word T_OP_EQ word { $$ = ssl_expr_make(op_EQ, $1, $3); }
+ | word T_OP_NE word { $$ = ssl_expr_make(op_NE, $1, $3); }
+ | word T_OP_LT word { $$ = ssl_expr_make(op_LT, $1, $3); }
+ | word T_OP_LE word { $$ = ssl_expr_make(op_LE, $1, $3); }
+ | word T_OP_GT word { $$ = ssl_expr_make(op_GT, $1, $3); }
+ | word T_OP_GE word { $$ = ssl_expr_make(op_GE, $1, $3); }
+ | word T_OP_IN '{' words '}' { $$ = ssl_expr_make(op_IN, $1, $4); }
+ | word T_OP_REG regex { $$ = ssl_expr_make(op_REG, $1, $3); }
+ | word T_OP_NRE regex { $$ = ssl_expr_make(op_NRE, $1, $3); }
+ ;
+
+words : word { $$ = ssl_expr_make(op_ListElement, $1, NULL); }
+ | words ',' word { $$ = ssl_expr_make(op_ListElement, $3, $1); }
+ ;
+
+word : T_DIGIT { $$ = ssl_expr_make(op_Digit, $1, NULL); }
+ | T_STRING { $$ = ssl_expr_make(op_String, $1, NULL); }
+ | '%' '{' T_ID '}' { $$ = ssl_expr_make(op_Var, $3, NULL); }
+ | funccall { $$ = $1; }
+ ;
+
+regex : T_REGEX {
+ regex_t *regex;
+ if ((regex = ap_pregcomp(ssl_expr_info.pool, $1,
+ REG_EXTENDED|REG_NOSUB)) == NULL) {
+ ssl_expr_error = "Failed to compile regular expression";
+ YYERROR;
+ regex = NULL;
+ }
+ $$ = ssl_expr_make(op_Regex, regex, NULL);
+ }
+ | T_REGEX_I {
+ regex_t *regex;
+ if ((regex = ap_pregcomp(ssl_expr_info.pool, $1,
+ REG_EXTENDED|REG_NOSUB|REG_ICASE)) == NULL) {
+ ssl_expr_error = "Failed to compile regular expression";
+ YYERROR;
+ regex = NULL;
+ }
+ $$ = ssl_expr_make(op_Regex, regex, NULL);
+ }
+ ;
+
+funccall : T_FUNC_FILE '(' T_STRING ')' {
+ ssl_expr *args = ssl_expr_make(op_ListElement, $3, NULL);
+ $$ = ssl_expr_make(op_Func, "file", args);
+ }
+ ;
+
+%%
+
+int yyerror(char *s)
+{
+ ssl_expr_error = s;
+ return 2;
+}
+
diff --git a/modules/ssl/ssl_expr_scan.c b/modules/ssl/ssl_expr_scan.c
new file mode 100644
index 0000000000..f3e9f9d1f5
--- /dev/null
+++ b/modules/ssl/ssl_expr_scan.c
@@ -0,0 +1,2002 @@
+#define yy_create_buffer ssl_expr_yy_create_buffer
+#define yy_delete_buffer ssl_expr_yy_delete_buffer
+#define yy_scan_buffer ssl_expr_yy_scan_buffer
+#define yy_scan_string ssl_expr_yy_scan_string
+#define yy_scan_bytes ssl_expr_yy_scan_bytes
+#define yy_flex_debug ssl_expr_yy_flex_debug
+#define yy_init_buffer ssl_expr_yy_init_buffer
+#define yy_flush_buffer ssl_expr_yy_flush_buffer
+#define yy_load_buffer_state ssl_expr_yy_load_buffer_state
+#define yy_switch_to_buffer ssl_expr_yy_switch_to_buffer
+#define yyin ssl_expr_yyin
+#define yyleng ssl_expr_yyleng
+#define yylex ssl_expr_yylex
+#define yyout ssl_expr_yyout
+#define yyrestart ssl_expr_yyrestart
+#define yytext ssl_expr_yytext
+
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+#include <stdio.h>
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator). This
+ * avoids problems with code like:
+ *
+ * if ( condition_holds )
+ * yyless( 5 );
+ * else
+ * do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ *yy_cp = yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+ };
+
+static YY_BUFFER_STATE yy_current_buffer = 0;
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart YY_PROTO(( FILE *input_file ));
+
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+
+#define yywrap() 1
+#define YY_SKIP_YYWRAP
+typedef unsigned char YY_CHAR;
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+typedef int yy_state_type;
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yytext_ptr = yy_bp; \
+ yyleng = (int) (yy_cp - yy_bp); \
+ yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 46
+#define YY_END_OF_BUFFER 47
+static yyconst short int yy_accept[86] =
+ { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 47, 45,
+ 1, 38, 2, 45, 43, 24, 45, 28, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44, 44, 45,
+ 13, 4, 3, 14, 16, 18, 17, 1, 22, 32,
+ 34, 43, 26, 20, 31, 30, 44, 44, 19, 44,
+ 44, 29, 27, 39, 25, 23, 15, 15, 21, 44,
+ 35, 44, 36, 13, 12, 5, 6, 10, 11, 7,
+ 8, 9, 33, 44, 44, 37, 44, 5, 6, 44,
+ 40, 41, 5, 42, 0
+ } ;
+
+static yyconst int yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 5, 1, 1, 1, 6, 1, 1,
+ 1, 1, 1, 1, 7, 1, 1, 8, 8, 8,
+ 8, 8, 8, 8, 8, 9, 9, 7, 1, 10,
+ 11, 12, 1, 1, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 1, 14, 1, 1, 7, 1, 15, 16, 13, 17,
+
+ 18, 19, 20, 13, 21, 13, 13, 22, 23, 24,
+ 25, 13, 26, 27, 28, 29, 30, 13, 13, 13,
+ 13, 13, 1, 31, 1, 32, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst int yy_meta[33] =
+ { 0,
+ 1, 1, 2, 1, 3, 1, 4, 4, 4, 1,
+ 1, 1, 4, 3, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 1, 1
+ } ;
+
+static yyconst short int yy_base[93] =
+ { 0,
+ 0, 0, 30, 31, 0, 0, 82, 81, 101, 142,
+ 35, 28, 142, 94, 32, 88, 31, 87, 0, 69,
+ 66, 28, 28, 67, 29, 63, 30, 63, 62, 57,
+ 0, 142, 142, 88, 142, 142, 142, 48, 142, 142,
+ 142, 44, 142, 142, 142, 142, 0, 70, 0, 64,
+ 63, 0, 0, 0, 0, 0, 142, 0, 0, 55,
+ 0, 46, 142, 0, 142, 53, 62, 142, 142, 142,
+ 142, 142, 0, 44, 48, 0, 41, 70, 72, 38,
+ 0, 0, 74, 0, 142, 117, 121, 125, 50, 129,
+ 133, 137
+
+ } ;
+
+static yyconst short int yy_def[93] =
+ { 0,
+ 85, 1, 86, 86, 87, 87, 88, 88, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 89, 89,
+ 89, 89, 89, 89, 89, 90, 89, 89, 89, 85,
+ 91, 85, 85, 92, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 85, 89, 89, 89,
+ 89, 89, 85, 91, 85, 85, 85, 85, 85, 85,
+ 85, 85, 89, 89, 89, 89, 89, 85, 85, 89,
+ 89, 89, 85, 89, 0, 85, 85, 85, 85, 85,
+ 85, 85
+
+ } ;
+
+static yyconst short int yy_nxt[175] =
+ { 0,
+ 10, 11, 11, 12, 13, 14, 10, 15, 15, 16,
+ 17, 18, 19, 10, 20, 19, 19, 21, 22, 23,
+ 24, 25, 26, 27, 28, 19, 19, 19, 29, 19,
+ 30, 10, 32, 32, 33, 33, 38, 38, 39, 42,
+ 42, 44, 50, 34, 34, 52, 55, 59, 51, 38,
+ 38, 42, 42, 47, 60, 84, 53, 56, 82, 40,
+ 78, 79, 45, 57, 57, 81, 57, 57, 57, 79,
+ 79, 80, 57, 57, 57, 77, 57, 83, 79, 79,
+ 79, 79, 79, 76, 75, 74, 73, 63, 62, 61,
+ 54, 49, 48, 57, 57, 66, 67, 46, 43, 41,
+
+ 85, 37, 37, 68, 85, 85, 69, 85, 85, 85,
+ 85, 70, 85, 85, 71, 85, 72, 31, 31, 31,
+ 31, 35, 35, 35, 35, 36, 36, 36, 36, 58,
+ 85, 58, 58, 64, 85, 85, 64, 65, 65, 65,
+ 65, 9, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85
+ } ;
+
+static yyconst short int yy_chk[175] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 3, 4, 3, 4, 11, 11, 12, 15,
+ 15, 17, 22, 3, 4, 23, 25, 27, 22, 38,
+ 38, 42, 42, 89, 27, 80, 23, 25, 77, 12,
+ 66, 66, 17, 26, 26, 75, 26, 26, 26, 67,
+ 67, 74, 26, 26, 26, 62, 26, 78, 78, 79,
+ 79, 83, 83, 60, 51, 50, 48, 30, 29, 28,
+ 24, 21, 20, 26, 26, 34, 34, 18, 16, 14,
+
+ 9, 8, 7, 34, 0, 0, 34, 0, 0, 0,
+ 0, 34, 0, 0, 34, 0, 34, 86, 86, 86,
+ 86, 87, 87, 87, 87, 88, 88, 88, 88, 90,
+ 0, 90, 90, 91, 0, 0, 91, 92, 92, 92,
+ 92, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "ssl_expr_scan.l"
+#define INITIAL 0
+/* _ _
+** _ __ ___ ___ __| | ___ ___| |
+** | '_ ` _ \ / _ \ / _` | / __/ __| |
+** | | | | | | (_) | (_| | \__ \__ \ | mod_ssl - Apache Interface to OpenSSL
+** |_| |_| |_|\___/ \__,_|___|___/___/_| http://www.modssl.org/
+** |_____|
+** ssl_expr_scan.l
+** Expression Scanner
+*/
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+/* ``Killing for peace is
+like fucking for virginity.''
+-- Unknown */
+/* _________________________________________________________________
+**
+** Expression Scanner
+** _________________________________________________________________
+*/
+#line 73 "ssl_expr_scan.l"
+#include "mod_ssl.h"
+
+#include "ssl_expr_parse.h"
+
+#define YY_NO_UNPUT 1
+int yyinput(char *buf, int max_size);
+
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ (result = yyinput(buf, max_size))
+
+#define MAX_STR_LEN 2048
+/* %option stack */
+#define YY_NEVER_INTERACTIVE 1
+#define str 1
+
+#define regex 2
+#define regex_flags 3
+
+#line 537 "lex.ssl_expr_yy.c"
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YY_PROTO(( yyconst char * ));
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines. This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( yy_current_buffer->yy_is_interactive ) \
+ { \
+ int c = '*', n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \
+ && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" );
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL int yylex YY_PROTO(( void ))
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+YY_DECL
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 94 "ssl_expr_scan.l"
+
+
+ char caStr[MAX_STR_LEN];
+ char *cpStr = NULL;
+ char caRegex[MAX_STR_LEN];
+ char *cpRegex = NULL;
+ char cRegexDel = NUL;
+
+ /*
+ * Whitespaces
+ */
+#line 700 "lex.ssl_expr_yy.c"
+
+ if ( yy_init )
+ {
+ yy_init = 0;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yy_start )
+ yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! yy_current_buffer )
+ yy_current_buffer =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_load_buffer_state();
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yy_start;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 86 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_current_state != 85 );
+ yy_cp = yy_last_accepting_cpos;
+ yy_current_state = yy_last_accepting_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+
+do_action: /* This label is used only to access EOF actions. */
+
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yy_hold_char;
+ yy_cp = yy_last_accepting_cpos;
+ yy_current_state = yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 105 "ssl_expr_scan.l"
+{
+ /* NOP */
+}
+ YY_BREAK
+/*
+ * C-style strings ("...")
+ */
+case 2:
+YY_RULE_SETUP
+#line 112 "ssl_expr_scan.l"
+{
+ cpStr = caStr;
+ BEGIN(str);
+}
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 116 "ssl_expr_scan.l"
+{
+ BEGIN(INITIAL);
+ *cpStr = NUL;
+ yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caStr);
+ return T_STRING;
+}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 122 "ssl_expr_scan.l"
+{
+ yyerror("Unterminated string");
+}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 125 "ssl_expr_scan.l"
+{
+ int result;
+
+ (void)sscanf(yytext+1, "%o", &result);
+ if (result > 0xff)
+ yyerror("Escape sequence out of bound");
+ else
+ *cpStr++ = result;
+}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 134 "ssl_expr_scan.l"
+{
+ yyerror("Bad escape sequence");
+}
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 137 "ssl_expr_scan.l"
+{ *cpStr++ = '\n'; }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 138 "ssl_expr_scan.l"
+{ *cpStr++ = '\r'; }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 139 "ssl_expr_scan.l"
+{ *cpStr++ = '\t'; }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 140 "ssl_expr_scan.l"
+{ *cpStr++ = '\b'; }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 141 "ssl_expr_scan.l"
+{ *cpStr++ = '\f'; }
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 142 "ssl_expr_scan.l"
+{
+ *cpStr++ = yytext[1];
+}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 145 "ssl_expr_scan.l"
+{
+ char *cp = yytext;
+ while (*cp != NUL)
+ *cpStr++ = *cp++;
+}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 150 "ssl_expr_scan.l"
+{
+ *cpStr++ = yytext[1];
+}
+ YY_BREAK
+/*
+ * Regular Expression
+ */
+case 15:
+YY_RULE_SETUP
+#line 157 "ssl_expr_scan.l"
+{
+ cRegexDel = yytext[1];
+ cpRegex = caRegex;
+ BEGIN(regex);
+}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 162 "ssl_expr_scan.l"
+{
+ if (yytext[0] == cRegexDel) {
+ *cpRegex = NUL;
+ BEGIN(regex_flags);
+ }
+ else {
+ *cpRegex++ = yytext[0];
+ }
+}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 171 "ssl_expr_scan.l"
+{
+ yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex);
+ BEGIN(INITIAL);
+ return T_REGEX_I;
+}
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 176 "ssl_expr_scan.l"
+{
+ yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex);
+ yyless(0);
+ BEGIN(INITIAL);
+ return T_REGEX;
+}
+ YY_BREAK
+case YY_STATE_EOF(regex_flags):
+#line 182 "ssl_expr_scan.l"
+{
+ yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex);
+ BEGIN(INITIAL);
+ return T_REGEX;
+}
+ YY_BREAK
+/*
+ * Operators
+ */
+case 19:
+YY_RULE_SETUP
+#line 191 "ssl_expr_scan.l"
+{ return T_OP_EQ; }
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 192 "ssl_expr_scan.l"
+{ return T_OP_EQ; }
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 193 "ssl_expr_scan.l"
+{ return T_OP_NE; }
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 194 "ssl_expr_scan.l"
+{ return T_OP_NE; }
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 195 "ssl_expr_scan.l"
+{ return T_OP_LT; }
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 196 "ssl_expr_scan.l"
+{ return T_OP_LT; }
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 197 "ssl_expr_scan.l"
+{ return T_OP_LE; }
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 198 "ssl_expr_scan.l"
+{ return T_OP_LE; }
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 199 "ssl_expr_scan.l"
+{ return T_OP_GT; }
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 200 "ssl_expr_scan.l"
+{ return T_OP_GT; }
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 201 "ssl_expr_scan.l"
+{ return T_OP_GE; }
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 202 "ssl_expr_scan.l"
+{ return T_OP_GE; }
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 203 "ssl_expr_scan.l"
+{ return T_OP_REG; }
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 204 "ssl_expr_scan.l"
+{ return T_OP_NRE; }
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 205 "ssl_expr_scan.l"
+{ return T_OP_AND; }
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 206 "ssl_expr_scan.l"
+{ return T_OP_AND; }
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 207 "ssl_expr_scan.l"
+{ return T_OP_OR; }
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 208 "ssl_expr_scan.l"
+{ return T_OP_OR; }
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 209 "ssl_expr_scan.l"
+{ return T_OP_NOT; }
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 210 "ssl_expr_scan.l"
+{ return T_OP_NOT; }
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 211 "ssl_expr_scan.l"
+{ return T_OP_IN; }
+ YY_BREAK
+/*
+ * Functions
+ */
+case 40:
+YY_RULE_SETUP
+#line 216 "ssl_expr_scan.l"
+{ return T_FUNC_FILE; }
+ YY_BREAK
+/*
+ * Specials
+ */
+case 41:
+YY_RULE_SETUP
+#line 221 "ssl_expr_scan.l"
+{ return T_TRUE; }
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 222 "ssl_expr_scan.l"
+{ return T_FALSE; }
+ YY_BREAK
+/*
+ * Digits
+ */
+case 43:
+YY_RULE_SETUP
+#line 227 "ssl_expr_scan.l"
+{
+ yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, yytext);
+ return T_DIGIT;
+}
+ YY_BREAK
+/*
+ * Identifiers
+ */
+case 44:
+YY_RULE_SETUP
+#line 235 "ssl_expr_scan.l"
+{
+ yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, yytext);
+ return T_ID;
+}
+ YY_BREAK
+/*
+ * Anything else is returned as is...
+ */
+case 45:
+YY_RULE_SETUP
+#line 243 "ssl_expr_scan.l"
+{
+ return yytext[0];
+}
+ YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 247 "ssl_expr_scan.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+#line 1100 "lex.ssl_expr_yy.c"
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(str):
+case YY_STATE_EOF(regex):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between yy_current_buffer and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yy_current_buffer->yy_input_file = yyin;
+ yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yy_last_accepting_cpos;
+ yy_current_state = yy_last_accepting_state;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap() )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p =
+ yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yy_c_buf_p =
+ &yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of yylex */
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+
+static int yy_get_next_buffer()
+ {
+ register char *dest = yy_current_buffer->yy_ch_buf;
+ register char *source = yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( yy_current_buffer->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ yy_current_buffer->yy_n_chars = yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+ YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = yy_current_buffer;
+
+ int yy_c_buf_p_offset =
+ (int) (yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yy_flex_realloc( (void *) b->yy_ch_buf,
+ b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = yy_current_buffer->yy_buf_size -
+ number_to_move - 1;
+#endif
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+ yy_n_chars, num_to_read );
+
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ if ( yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart( yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ yy_current_buffer->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yy_n_chars += number_to_move;
+ yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yytext_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+ return ret_val;
+ }
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+static yy_state_type yy_get_previous_state()
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = yy_start;
+
+ for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 86 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+ }
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+ {
+ register int yy_is_jam;
+ register char *yy_cp = yy_c_buf_p;
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 86 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 85);
+
+ return yy_is_jam ? 0 : yy_current_state;
+ }
+
+
+#ifndef YY_NO_UNPUT
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+ {
+ register char *yy_cp = yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yy_hold_char;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yy_n_chars + 2;
+ register char *dest = &yy_current_buffer->yy_ch_buf[
+ yy_current_buffer->yy_buf_size + 2];
+ register char *source =
+ &yy_current_buffer->yy_ch_buf[number_to_move];
+
+ while ( source > yy_current_buffer->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ yy_current_buffer->yy_n_chars =
+ yy_n_chars = yy_current_buffer->yy_buf_size;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+
+ yytext_ptr = yy_bp;
+ yy_hold_char = *yy_cp;
+ yy_c_buf_p = yy_cp;
+ }
+#endif /* ifndef YY_NO_UNPUT */
+
+
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+ {
+ int c;
+
+ *yy_c_buf_p = yy_hold_char;
+
+ if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ /* This was really a NUL. */
+ *yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = yy_c_buf_p - yytext_ptr;
+ ++yy_c_buf_p;
+
+ switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart( yyin );
+
+ /* fall through */
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap() )
+ return EOF;
+
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p = yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */
+ *yy_c_buf_p = '\0'; /* preserve yytext */
+ yy_hold_char = *++yy_c_buf_p;
+
+
+ return c;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+ {
+ if ( ! yy_current_buffer )
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_init_buffer( yy_current_buffer, input_file );
+ yy_load_buffer_state();
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+ {
+ if ( yy_current_buffer == new_buffer )
+ return;
+
+ if ( yy_current_buffer )
+ {
+ /* Flush out information for old buffer. */
+ *yy_c_buf_p = yy_hold_char;
+ yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ yy_current_buffer = new_buffer;
+ yy_load_buffer_state();
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yy_did_buffer_switch_on_eof = 1;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+ {
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+ yyin = yy_current_buffer->yy_input_file;
+ yy_hold_char = *yy_c_buf_p;
+ }
+
+
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+ {
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer( b, file );
+
+ return b;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+ {
+ if ( ! b )
+ return;
+
+ if ( b == yy_current_buffer )
+ yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yy_flex_free( (void *) b->yy_ch_buf );
+
+ yy_flex_free( (void *) b );
+ }
+
+
+#ifndef YY_ALWAYS_INTERACTIVE
+#ifndef YY_NEVER_INTERACTIVE
+extern int isatty YY_PROTO(( int ));
+#endif
+#endif
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+
+
+ {
+ yy_flush_buffer( b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+#if YY_ALWAYS_INTERACTIVE
+ b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+ b->yy_is_interactive = 0;
+#else
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+ {
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == yy_current_buffer )
+ yy_load_buffer_state();
+ }
+
+
+#ifndef YY_NO_SCAN_BUFFER
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+ {
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer( b );
+
+ return b;
+ }
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str )
+#else
+YY_BUFFER_STATE yy_scan_string( yy_str )
+yyconst char *yy_str;
+#endif
+ {
+ int len;
+ for ( len = 0; yy_str[len]; ++len )
+ ;
+
+ return yy_scan_bytes( yy_str, len );
+ }
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+ {
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = len + 2;
+ buf = (char *) yy_flex_alloc( n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < len; ++i )
+ buf[i] = bytes[i];
+
+ buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+ }
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+ {
+ if ( yy_start_stack_ptr >= yy_start_stack_depth )
+ {
+ yy_size_t new_size;
+
+ yy_start_stack_depth += YY_START_STACK_INCR;
+ new_size = yy_start_stack_depth * sizeof( int );
+
+ if ( ! yy_start_stack )
+ yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+ else
+ yy_start_stack = (int *) yy_flex_realloc(
+ (void *) yy_start_stack, new_size );
+
+ if ( ! yy_start_stack )
+ YY_FATAL_ERROR(
+ "out of memory expanding start-condition stack" );
+ }
+
+ yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+ BEGIN(new_state);
+ }
+#endif
+
+
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state()
+ {
+ if ( --yy_start_stack_ptr < 0 )
+ YY_FATAL_ERROR( "start-condition stack underflow" );
+
+ BEGIN(yy_start_stack[yy_start_stack_ptr]);
+ }
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state()
+ {
+ return yy_start_stack[yy_start_stack_ptr - 1];
+ }
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+ {
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+ }
+
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ yytext[yyleng] = yy_hold_char; \
+ yy_c_buf_p = yytext + n; \
+ yy_hold_char = *yy_c_buf_p; \
+ *yy_c_buf_p = '\0'; \
+ yyleng = n; \
+ } \
+ while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+ {
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+ }
+#endif
+
+#ifdef YY_NEED_STRLEN
+#ifdef YY_USE_PROTOS
+static int yy_flex_strlen( yyconst char *s )
+#else
+static int yy_flex_strlen( s )
+yyconst char *s;
+#endif
+ {
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+ }
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+ {
+ return (void *) malloc( size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+ {
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+ {
+ free( ptr );
+ }
+
+#if YY_MAIN
+int main()
+ {
+ yylex();
+ return 0;
+ }
+#endif
+#line 247 "ssl_expr_scan.l"
+
+
+int yyinput(char *buf, int max_size)
+{
+ int n;
+
+ if ((n = MIN(max_size, ssl_expr_info.inputbuf
+ + ssl_expr_info.inputlen
+ - ssl_expr_info.inputptr)) <= 0)
+ return YY_NULL;
+ memcpy(buf, ssl_expr_info.inputptr, n);
+ ssl_expr_info.inputptr += n;
+ return n;
+}
+
diff --git a/modules/ssl/ssl_expr_scan.l b/modules/ssl/ssl_expr_scan.l
new file mode 100644
index 0000000000..a0db7cccde
--- /dev/null
+++ b/modules/ssl/ssl_expr_scan.l
@@ -0,0 +1,261 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| |
+** | '_ ` _ \ / _ \ / _` | / __/ __| |
+** | | | | | | (_) | (_| | \__ \__ \ | mod_ssl - Apache Interface to OpenSSL
+** |_| |_| |_|\___/ \__,_|___|___/___/_| http://www.modssl.org/
+** |_____|
+** ssl_expr_scan.l
+** Expression Scanner
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+ /* ``Killing for peace is
+ like fucking for virginity.''
+ -- Unknown */
+
+/* _________________________________________________________________
+**
+** Expression Scanner
+** _________________________________________________________________
+*/
+
+%{
+#include "mod_ssl.h"
+
+#include "ssl_expr_parse.h"
+
+#define YY_NO_UNPUT 1
+int yyinput(char *buf, int max_size);
+
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ (result = yyinput(buf, max_size))
+
+#define MAX_STR_LEN 2048
+%}
+
+%pointer
+/* %option stack */
+%option never-interactive
+%option noyywrap
+%x str
+%x regex regex_flags
+
+%%
+
+ char caStr[MAX_STR_LEN];
+ char *cpStr = NULL;
+ char caRegex[MAX_STR_LEN];
+ char *cpRegex = NULL;
+ char cRegexDel = NUL;
+
+ /*
+ * Whitespaces
+ */
+[ \t\n]+ {
+ /* NOP */
+}
+
+ /*
+ * C-style strings ("...")
+ */
+\" {
+ cpStr = caStr;
+ BEGIN(str);
+}
+<str>\" {
+ BEGIN(INITIAL);
+ *cpStr = NUL;
+ yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caStr);
+ return T_STRING;
+}
+<str>\n {
+ yyerror("Unterminated string");
+}
+<str>\\[0-7]{1,3} {
+ int result;
+
+ (void)sscanf(yytext+1, "%o", &result);
+ if (result > 0xff)
+ yyerror("Escape sequence out of bound");
+ else
+ *cpStr++ = result;
+}
+<str>\\[0-9]+ {
+ yyerror("Bad escape sequence");
+}
+<str>\\n { *cpStr++ = '\n'; }
+<str>\\r { *cpStr++ = '\r'; }
+<str>\\t { *cpStr++ = '\t'; }
+<str>\\b { *cpStr++ = '\b'; }
+<str>\\f { *cpStr++ = '\f'; }
+<str>\\(.|\n) {
+ *cpStr++ = yytext[1];
+}
+<str>[^\\\n\"]+ {
+ char *cp = yytext;
+ while (*cp != NUL)
+ *cpStr++ = *cp++;
+}
+<str>. {
+ *cpStr++ = yytext[1];
+}
+
+ /*
+ * Regular Expression
+ */
+"m". {
+ cRegexDel = yytext[1];
+ cpRegex = caRegex;
+ BEGIN(regex);
+}
+<regex>.|\n {
+ if (yytext[0] == cRegexDel) {
+ *cpRegex = NUL;
+ BEGIN(regex_flags);
+ }
+ else {
+ *cpRegex++ = yytext[0];
+ }
+}
+<regex_flags>i {
+ yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex);
+ BEGIN(INITIAL);
+ return T_REGEX_I;
+}
+<regex_flags>.|\n {
+ yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex);
+ yyless(0);
+ BEGIN(INITIAL);
+ return T_REGEX;
+}
+<regex_flags><<EOF>> {
+ yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex);
+ BEGIN(INITIAL);
+ return T_REGEX;
+}
+
+ /*
+ * Operators
+ */
+"eq" { return T_OP_EQ; }
+"==" { return T_OP_EQ; }
+"ne" { return T_OP_NE; }
+"!=" { return T_OP_NE; }
+"lt" { return T_OP_LT; }
+"<" { return T_OP_LT; }
+"le" { return T_OP_LE; }
+"<=" { return T_OP_LE; }
+"gt" { return T_OP_GT; }
+">" { return T_OP_GT; }
+"ge" { return T_OP_GE; }
+">=" { return T_OP_GE; }
+"=~" { return T_OP_REG; }
+"!~" { return T_OP_NRE; }
+"and" { return T_OP_AND; }
+"&&" { return T_OP_AND; }
+"or" { return T_OP_OR; }
+"||" { return T_OP_OR; }
+"not" { return T_OP_NOT; }
+"!" { return T_OP_NOT; }
+"in" { return T_OP_IN; }
+
+ /*
+ * Functions
+ */
+"file" { return T_FUNC_FILE; }
+
+ /*
+ * Specials
+ */
+"true" { return T_TRUE; }
+"false" { return T_FALSE; }
+
+ /*
+ * Digits
+ */
+[0-9]+ {
+ yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, yytext);
+ return T_DIGIT;
+}
+
+ /*
+ * Identifiers
+ */
+[a-zA-Z][a-zA-Z0-9_:-]* {
+ yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, yytext);
+ return T_ID;
+}
+
+ /*
+ * Anything else is returned as is...
+ */
+.|\n {
+ return yytext[0];
+}
+
+%%
+
+int yyinput(char *buf, int max_size)
+{
+ int n;
+
+ if ((n = MIN(max_size, ssl_expr_info.inputbuf
+ + ssl_expr_info.inputlen
+ - ssl_expr_info.inputptr)) <= 0)
+ return YY_NULL;
+ memcpy(buf, ssl_expr_info.inputptr, n);
+ ssl_expr_info.inputptr += n;
+ return n;
+}
+
diff --git a/modules/ssl/ssl_scache.c b/modules/ssl/ssl_scache.c
new file mode 100644
index 0000000000..139c7865fe
--- /dev/null
+++ b/modules/ssl/ssl_scache.c
@@ -0,0 +1,204 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_scache.c
+** Session Cache Abstraction
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``Open-Source Software: generous
+ programmers from around the world all
+ join forces to help you shoot
+ yourself in the foot for free.''
+ -- Unknown */
+#include "mod_ssl.h"
+
+/* _________________________________________________________________
+**
+** Session Cache: Common Abstraction Layer
+** _________________________________________________________________
+*/
+
+void ssl_scache_init(server_rec *s, pool *p)
+{
+ SSLModConfigRec *mc = myModConfig();
+
+ if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+ ssl_scache_dbm_init(s, p);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+ ssl_scache_shmht_init(s, p);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+ ssl_scache_shmcb_init(s, p);
+#ifdef SSL_VENDOR
+ else
+ ap_hook_use("ap::mod_ssl::vendor::scache_init",
+ AP_HOOK_SIG3(void,ptr,ptr), AP_HOOK_ALL, s, p);
+#endif
+ return;
+}
+
+void ssl_scache_kill(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig();
+
+ if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+ ssl_scache_dbm_kill(s);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+ ssl_scache_shmht_kill(s);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+ ssl_scache_shmcb_kill(s);
+#ifdef SSL_VENDOR
+ else
+ ap_hook_use("ap::mod_ssl::vendor::scache_kill",
+ AP_HOOK_SIG2(void,ptr), AP_HOOK_ALL, s);
+#endif
+ return;
+}
+
+BOOL ssl_scache_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess)
+{
+ SSLModConfigRec *mc = myModConfig();
+ BOOL rv = FALSE;
+
+ if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+ rv = ssl_scache_dbm_store(s, id, idlen, expiry, sess);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+ rv = ssl_scache_shmht_store(s, id, idlen, expiry, sess);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+ rv = ssl_scache_shmcb_store(s, id, idlen, expiry, sess);
+#ifdef SSL_VENDOR
+ else
+ ap_hook_use("ap::mod_ssl::vendor::scache_store",
+ AP_HOOK_SIG6(int,ptr,ptr,int,int,ptr), AP_HOOK_ALL,
+ (int *)&rv, s, id, idlen, (int)expiry, sess);
+#endif
+ return rv;
+}
+
+SSL_SESSION *ssl_scache_retrieve(server_rec *s, UCHAR *id, int idlen)
+{
+ SSLModConfigRec *mc = myModConfig();
+ SSL_SESSION *sess = NULL;
+
+ if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+ sess = ssl_scache_dbm_retrieve(s, id, idlen);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+ sess = ssl_scache_shmht_retrieve(s, id, idlen);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+ sess = ssl_scache_shmcb_retrieve(s, id, idlen);
+#ifdef SSL_VENDOR
+ else
+ ap_hook_use("ap::mod_ssl::vendor::scache_retrieve",
+ AP_HOOK_SIG4(ptr,ptr,ptr,int), AP_HOOK_ALL,
+ &sess, s, id, idlen);
+#endif
+ return sess;
+}
+
+void ssl_scache_remove(server_rec *s, UCHAR *id, int idlen)
+{
+ SSLModConfigRec *mc = myModConfig();
+
+ if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+ ssl_scache_dbm_remove(s, id, idlen);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+ ssl_scache_shmht_remove(s, id, idlen);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+ ssl_scache_shmcb_remove(s, id, idlen);
+#ifdef SSL_VENDOR
+ else
+ ap_hook_use("ap::mod_ssl::vendor::scache_remove",
+ AP_HOOK_SIG4(void,ptr,ptr,int), AP_HOOK_ALL, s, id, idlen);
+#endif
+ return;
+}
+
+void ssl_scache_status(server_rec *s, pool *p, void (*func)(char *, void *), void *arg)
+{
+ SSLModConfigRec *mc = myModConfig();
+
+ if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+ ssl_scache_dbm_status(s, p, func, arg);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+ ssl_scache_shmht_status(s, p, func, arg);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+ ssl_scache_shmcb_status(s, p, func, arg);
+#ifdef SSL_VENDOR
+ else
+ ap_hook_use("ap::mod_ssl::vendor::scache_status",
+ AP_HOOK_SIG5(void,ptr,ptr,ptr,ptr), AP_HOOK_ALL,
+ s, p, func, arg);
+#endif
+ return;
+}
+
+void ssl_scache_expire(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig();
+
+ if (mc->nSessionCacheMode == SSL_SCMODE_DBM)
+ ssl_scache_dbm_expire(s);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT)
+ ssl_scache_shmht_expire(s);
+ else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB)
+ ssl_scache_shmcb_expire(s);
+#ifdef SSL_VENDOR
+ else
+ ap_hook_use("ap::mod_ssl::vendor::scache_expire",
+ AP_HOOK_SIG2(void,ptr), AP_HOOK_ALL, s);
+#endif
+ return;
+}
+
diff --git a/modules/ssl/ssl_scache_dbm.c b/modules/ssl/ssl_scache_dbm.c
new file mode 100644
index 0000000000..323c612991
--- /dev/null
+++ b/modules/ssl/ssl_scache_dbm.c
@@ -0,0 +1,440 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_scache_dbm.c
+** Session Cache via DBM
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include "mod_ssl.h"
+
+void ssl_scache_dbm_init(server_rec *s, pool *p)
+{
+ SSLModConfigRec *mc = myModConfig();
+ DBM *dbm;
+
+ /* for the DBM we need the data file */
+ if (mc->szSessionCacheDataFile == NULL) {
+ ssl_log(s, SSL_LOG_ERROR, "SSLSessionCache required");
+ ssl_die();
+ }
+
+ /* open it once to create it and to make sure it _can_ be created */
+ ssl_mutex_on(s);
+ if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+ O_RDWR|O_CREAT, SSL_DBM_FILE_MODE)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Cannot create SSLSessionCache DBM file `%s'",
+ mc->szSessionCacheDataFile);
+ ssl_mutex_off(s);
+ return;
+ }
+ ssl_dbm_close(dbm);
+
+#if !defined(OS2) && !defined(WIN32)
+ /*
+ * We have to make sure the Apache child processes have access to
+ * the DBM file. But because there are brain-dead platforms where we
+ * cannot exactly determine the suffixes we try all possibilities.
+ */
+ if (geteuid() == 0 /* is superuser */) {
+ chown(mc->szSessionCacheDataFile, ap_user_id, -1 /* no gid change */);
+ if (chown(ap_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_DIR, NULL),
+ ap_user_id, -1) == -1) {
+ if (chown(ap_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL),
+ ap_user_id, -1) == -1)
+ chown(ap_pstrcat(p, mc->szSessionCacheDataFile, ".dir", NULL),
+ ap_user_id, -1);
+ }
+ if (chown(ap_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_PAG, NULL),
+ ap_user_id, -1) == -1) {
+ if (chown(ap_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL),
+ ap_user_id, -1) == -1)
+ chown(ap_pstrcat(p, mc->szSessionCacheDataFile, ".pag", NULL),
+ ap_user_id, -1);
+ }
+ }
+#endif
+ ssl_mutex_off(s);
+ ssl_scache_dbm_expire(s);
+ return;
+}
+
+void ssl_scache_dbm_kill(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig();
+ pool *p;
+
+ if ((p = ap_make_sub_pool(NULL)) != NULL) {
+ /* the correct way */
+ unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_DIR, NULL));
+ unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_PAG, NULL));
+ /* the additional ways to be sure */
+ unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, ".dir", NULL));
+ unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, ".pag", NULL));
+ unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL));
+ unlink(mc->szSessionCacheDataFile);
+ ap_destroy_pool(p);
+ }
+ return;
+}
+
+BOOL ssl_scache_dbm_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess)
+{
+ SSLModConfigRec *mc = myModConfig();
+ DBM *dbm;
+ datum dbmkey;
+ datum dbmval;
+ UCHAR ucaData[SSL_SESSION_MAX_DER];
+ int nData;
+ UCHAR *ucp;
+
+ /* streamline session data */
+ ucp = ucaData;
+ nData = i2d_SSL_SESSION(sess, &ucp);
+
+ /* be careful: do not try to store too much bytes in a DBM file! */
+#ifdef SSL_USE_SDBM
+ if ((idlen + nData) >= PAIRMAX)
+ return FALSE;
+#else
+ if ((idlen + nData) >= 950 /* at least less than approx. 1KB */)
+ return FALSE;
+#endif
+
+ /* create DBM key */
+ dbmkey.dptr = (char *)id;
+ dbmkey.dsize = idlen;
+
+ /* create DBM value */
+ dbmval.dsize = sizeof(time_t) + nData;
+ dbmval.dptr = (char *)malloc(dbmval.dsize);
+ if (dbmval.dptr == NULL)
+ return FALSE;
+ memcpy((char *)dbmval.dptr, &expiry, sizeof(time_t));
+ memcpy((char *)dbmval.dptr+sizeof(time_t), ucaData, nData);
+
+ /* and store it to the DBM file */
+ ssl_mutex_on(s);
+ if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+ O_RDWR, SSL_DBM_FILE_MODE)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Cannot open SSLSessionCache DBM file `%s' for writing (store)",
+ mc->szSessionCacheDataFile);
+ ssl_mutex_off(s);
+ free(dbmval.dptr);
+ return FALSE;
+ }
+ if (ssl_dbm_store(dbm, dbmkey, dbmval, DBM_INSERT) < 0) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Cannot store SSL session to DBM file `%s'",
+ mc->szSessionCacheDataFile);
+ ssl_dbm_close(dbm);
+ ssl_mutex_off(s);
+ free(dbmval.dptr);
+ return FALSE;
+ }
+ ssl_dbm_close(dbm);
+ ssl_mutex_off(s);
+
+ /* free temporary buffers */
+ free(dbmval.dptr);
+
+ /* allow the regular expiring to occur */
+ ssl_scache_dbm_expire(s);
+
+ return TRUE;
+}
+
+SSL_SESSION *ssl_scache_dbm_retrieve(server_rec *s, UCHAR *id, int idlen)
+{
+ SSLModConfigRec *mc = myModConfig();
+ DBM *dbm;
+ datum dbmkey;
+ datum dbmval;
+ SSL_SESSION *sess = NULL;
+ UCHAR *ucpData;
+ int nData;
+ time_t expiry;
+ time_t now;
+
+ /* allow the regular expiring to occur */
+ ssl_scache_dbm_expire(s);
+
+ /* create DBM key and values */
+ dbmkey.dptr = (char *)id;
+ dbmkey.dsize = idlen;
+
+ /* and fetch it from the DBM file */
+ ssl_mutex_on(s);
+ if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+ O_RDONLY, SSL_DBM_FILE_MODE)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Cannot open SSLSessionCache DBM file `%s' for reading (fetch)",
+ mc->szSessionCacheDataFile);
+ ssl_mutex_off(s);
+ return NULL;
+ }
+ dbmval = ssl_dbm_fetch(dbm, dbmkey);
+ ssl_dbm_close(dbm);
+ ssl_mutex_off(s);
+
+ /* immediately return if not found */
+ if (dbmval.dptr == NULL || dbmval.dsize <= sizeof(time_t))
+ return NULL;
+
+ /* parse resulting data */
+ nData = dbmval.dsize-sizeof(time_t);
+ ucpData = (UCHAR *)malloc(nData);
+ if (ucpData == NULL)
+ return NULL;
+ memcpy(ucpData, (char *)dbmval.dptr+sizeof(time_t), nData);
+ memcpy(&expiry, dbmval.dptr, sizeof(time_t));
+
+ /* make sure the stuff is still not expired */
+ now = time(NULL);
+ if (expiry <= now) {
+ ssl_scache_dbm_remove(s, id, idlen);
+ return NULL;
+ }
+
+ /* unstreamed SSL_SESSION */
+ sess = d2i_SSL_SESSION(NULL, &ucpData, nData);
+
+ return sess;
+}
+
+void ssl_scache_dbm_remove(server_rec *s, UCHAR *id, int idlen)
+{
+ SSLModConfigRec *mc = myModConfig();
+ DBM *dbm;
+ datum dbmkey;
+
+ /* create DBM key and values */
+ dbmkey.dptr = (char *)id;
+ dbmkey.dsize = idlen;
+
+ /* and delete it from the DBM file */
+ ssl_mutex_on(s);
+ if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+ O_RDWR, SSL_DBM_FILE_MODE)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Cannot open SSLSessionCache DBM file `%s' for writing (delete)",
+ mc->szSessionCacheDataFile);
+ ssl_mutex_off(s);
+ return;
+ }
+ ssl_dbm_delete(dbm, dbmkey);
+ ssl_dbm_close(dbm);
+ ssl_mutex_off(s);
+
+ return;
+}
+
+void ssl_scache_dbm_expire(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig();
+ SSLSrvConfigRec *sc = mySrvConfig(s);
+ static time_t tLast = 0;
+ DBM *dbm;
+ datum dbmkey;
+ datum dbmval;
+ pool *p;
+ time_t tExpiresAt;
+ int nElements = 0;
+ int nDeleted = 0;
+ int bDelete;
+ datum *keylist;
+ int keyidx;
+ int i;
+ time_t tNow;
+
+ /*
+ * make sure the expiration for still not-accessed session
+ * cache entries is done only from time to time
+ */
+ tNow = time(NULL);
+ if (tNow < tLast+sc->nSessionCacheTimeout)
+ return;
+ tLast = tNow;
+
+ /*
+ * Here we have to be very carefully: Not all DBM libraries are
+ * smart enough to allow one to iterate over the elements and at the
+ * same time delete expired ones. Some of them get totally crazy
+ * while others have no problems. So we have to do it the slower but
+ * more safe way: we first iterate over all elements and remember
+ * those which have to be expired. Then in a second pass we delete
+ * all those expired elements. Additionally we reopen the DBM file
+ * to be really safe in state.
+ */
+
+#define KEYMAX 1024
+
+ ssl_mutex_on(s);
+ for (;;) {
+ /* allocate the key array in a memory sub pool */
+ if ((p = ap_make_sub_pool(NULL)) == NULL)
+ break;
+ if ((keylist = ap_palloc(p, sizeof(dbmkey)*KEYMAX)) == NULL) {
+ ap_destroy_pool(p);
+ break;
+ }
+
+ /* pass 1: scan DBM database */
+ keyidx = 0;
+ if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+ O_RDWR, SSL_DBM_FILE_MODE)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Cannot open SSLSessionCache DBM file `%s' for scanning",
+ mc->szSessionCacheDataFile);
+ ap_destroy_pool(p);
+ break;
+ }
+ dbmkey = ssl_dbm_firstkey(dbm);
+ while (dbmkey.dptr != NULL) {
+ nElements++;
+ bDelete = FALSE;
+ dbmval = ssl_dbm_fetch(dbm, dbmkey);
+ if (dbmval.dsize <= sizeof(time_t) || dbmval.dptr == NULL)
+ bDelete = TRUE;
+ else {
+ memcpy(&tExpiresAt, dbmval.dptr, sizeof(time_t));
+ if (tExpiresAt <= tNow)
+ bDelete = TRUE;
+ }
+ if (bDelete) {
+ if ((keylist[keyidx].dptr = ap_palloc(p, dbmkey.dsize)) != NULL) {
+ memcpy(keylist[keyidx].dptr, dbmkey.dptr, dbmkey.dsize);
+ keylist[keyidx].dsize = dbmkey.dsize;
+ keyidx++;
+ if (keyidx == KEYMAX)
+ break;
+ }
+ }
+ dbmkey = ssl_dbm_nextkey(dbm);
+ }
+ ssl_dbm_close(dbm);
+
+ /* pass 2: delete expired elements */
+ if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+ O_RDWR, SSL_DBM_FILE_MODE)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Cannot re-open SSLSessionCache DBM file `%s' for expiring",
+ mc->szSessionCacheDataFile);
+ ap_destroy_pool(p);
+ break;
+ }
+ for (i = 0; i < keyidx; i++) {
+ ssl_dbm_delete(dbm, keylist[i]);
+ nDeleted++;
+ }
+ ssl_dbm_close(dbm);
+
+ /* destroy temporary pool */
+ ap_destroy_pool(p);
+
+ if (keyidx < KEYMAX)
+ break;
+ }
+ ssl_mutex_off(s);
+
+ ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache (DBM) Expiry: "
+ "old: %d, new: %d, removed: %d", nElements, nElements-nDeleted, nDeleted);
+ return;
+}
+
+void ssl_scache_dbm_status(server_rec *s, pool *p, void (*func)(char *, void *), void *arg)
+{
+ SSLModConfigRec *mc = myModConfig();
+ DBM *dbm;
+ datum dbmkey;
+ datum dbmval;
+ int nElem;
+ int nSize;
+ int nAverage;
+
+ nElem = 0;
+ nSize = 0;
+ ssl_mutex_on(s);
+ if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile,
+ O_RDONLY, SSL_DBM_FILE_MODE)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
+ "Cannot open SSLSessionCache DBM file `%s' for status retrival",
+ mc->szSessionCacheDataFile);
+ ssl_mutex_off(s);
+ return;
+ }
+ dbmkey = ssl_dbm_firstkey(dbm);
+ for ( ; dbmkey.dptr != NULL; dbmkey = ssl_dbm_nextkey(dbm)) {
+ dbmval = ssl_dbm_fetch(dbm, dbmkey);
+ if (dbmval.dptr == NULL)
+ continue;
+ nElem += 1;
+ nSize += dbmval.dsize;
+ }
+ ssl_dbm_close(dbm);
+ ssl_mutex_off(s);
+ if (nSize > 0 && nElem > 0)
+ nAverage = nSize / nElem;
+ else
+ nAverage = 0;
+ func(ap_psprintf(p, "cache type: <b>DBM</b>, maximum size: <b>unlimited</b><br>"), arg);
+ func(ap_psprintf(p, "current sessions: <b>%d</b>, current size: <b>%d</b> bytes<br>", nElem, nSize), arg);
+ func(ap_psprintf(p, "average session size: <b>%d</b> bytes<br>", nAverage), arg);
+ return;
+}
+
diff --git a/modules/ssl/ssl_scache_shmcb.c b/modules/ssl/ssl_scache_shmcb.c
new file mode 100644
index 0000000000..e588f0a5d1
--- /dev/null
+++ b/modules/ssl/ssl_scache_shmcb.c
@@ -0,0 +1,1343 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_scache_shmcb.c
+** Session Cache via Shared Memory (Cyclic Buffer Variant)
+*/
+
+/* ====================================================================
+ * Copyright (c) 2000-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include "mod_ssl.h"
+
+/*
+ * This shared memory based SSL session cache implementation was
+ * originally written by Geoff Thorpe <geoff@eu.c2.net> for C2Net Europe
+ * and as a contribution to Ralf Engelschall's mod_ssl project.
+ */
+
+/*
+ * The shared-memory segment header can be cast to and from the
+ * SHMCBHeader type, all other structures need to be initialised by
+ * utility functions.
+ *
+ * The "header" looks like this;
+ *
+ * data applying to the overall structure:
+ * - division_offset (unsigned int):
+ * how far into the shared memory segment the first division is.
+ * - division_size (unsigned int):
+ * how many bytes each division occupies.
+ * (NB: This includes the queue and the cache)
+ * - division_mask (unsigned char):
+ * the "mask" in the next line. Add one to this,
+ * and that's the number of divisions.
+ *
+ * data applying to within each division:
+ * - queue_size (unsigned int):
+ * how big each "queue" is. NB: The queue is the first block in each
+ * division and is followed immediately by the cache itself so so
+ * there's no cache_offset value.
+ *
+ * data applying to within each queue:
+ * - index_num (unsigned char):
+ * how many indexes in each cache's queue
+ * - index_offset (unsigned char):
+ * how far into the queue the first index is.
+ * - index_size:
+ * how big each index is.
+ *
+ * data applying to within each cache:
+ * - cache_data_offset (unsigned int):
+ * how far into the cache the session-data array is stored.
+ * - cache_data_size (unsigned int):
+ * how big each cache's data block is.
+ *
+ * statistics data (this will eventually be per-division but right now
+ * there's only one mutex):
+ * - stores (unsigned long):
+ * how many stores have been performed in the cache.
+ * - expiries (unsigned long):
+ * how many session have been expired from the cache.
+ * - scrolled (unsigned long):
+ * how many sessions have been scrolled out of full cache during a
+ * "store" operation. This is different to the "removes" stats as
+ * they are requested by mod_ssl/Apache, these are done because of
+ * cache logistics. (NB: Also, this value should be deducible from
+ * the others if my code has no bugs, but I count it anyway - plus
+ * it helps debugging :-).
+ * - retrieves_hit (unsigned long):
+ * how many session-retrieves have succeeded.
+ * - retrieves_miss (unsigned long):
+ * how many session-retrieves have failed.
+ * - removes_hit (unsigned long):
+ * - removes_miss (unsigned long):
+ *
+ * Following immediately after the header is an array of "divisions".
+ * Each division is simply a "queue" immediately followed by its
+ * corresponding "cache". Each division handles some pre-defined band
+ * of sessions by using the "division_mask" in the header. Eg. if
+ * division_mask=0x1f then there are 32 divisions, the first of which
+ * will store sessions whose least-significant 5 bits are 0, the second
+ * stores session whose LS 5 bits equal 1, etc. A queue is an indexing
+ * structure referring to its corresponding cache.
+ *
+ * A "queue" looks like this;
+ *
+ * - first_pos (unsigned int):
+ * the location within the array of indexes where the virtual
+ * "left-hand-edge" of the cyclic buffer is.
+ * - pos_count (unsigned int):
+ * the number of indexes occupied from first_pos onwards.
+ *
+ * ...followed by an array of indexes, each of which can be
+ * memcpy'd to and from an SHMCBIndex, and look like this;
+ *
+ * - expires (time_t):
+ * the time() value at which this session expires.
+ * - offset (unsigned int):
+ * the offset within the cache data block where the corresponding
+ * session is stored.
+ * - s_id2 (unsigned char):
+ * the second byte of the session_id, stored as an optimisation to
+ * reduce the number of d2i_SSL_SESSION calls that are made when doing
+ * a lookup.
+ * - removed (unsigned char):
+ * a byte used to indicate whether a session has been "passively"
+ * removed. Ie. it is still in the cache but is to be disregarded by
+ * any "retrieve" operation.
+ *
+ * A "cache" looks like this;
+ *
+ * - first_pos (unsigned int):
+ * the location within the data block where the virtual
+ * "left-hand-edge" of the cyclic buffer is.
+ * - pos_count (unsigned int):
+ * the number of bytes used in the data block from first_pos onwards.
+ *
+ * ...followed by the data block in which actual DER-encoded SSL
+ * sessions are stored.
+ */
+
+/*
+ * Header - can be memcpy'd to and from the front of the shared
+ * memory segment. NB: The first copy (commented out) has the
+ * elements in a meaningful order, but due to data-alignment
+ * braindeadness, the second (uncommented) copy has the types grouped
+ * so as to decrease "struct-bloat". sigh.
+ */
+typedef struct {
+#if 0
+ unsigned char division_mask;
+ unsigned int division_offset;
+ unsigned int division_size;
+ unsigned int queue_size;
+ unsigned char index_num;
+ unsigned char index_offset;
+ unsigned char index_size;
+ unsigned int cache_data_offset;
+ unsigned int cache_data_size;
+ unsigned long num_stores;
+ unsigned long num_expiries;
+ unsigned long num_scrolled;
+ unsigned long num_retrieves_hit;
+ unsigned long num_retrieves_miss;
+ unsigned long num_removes_hit;
+ unsigned long num_removes_miss;
+#else
+ unsigned long num_stores;
+ unsigned long num_expiries;
+ unsigned long num_scrolled;
+ unsigned long num_retrieves_hit;
+ unsigned long num_retrieves_miss;
+ unsigned long num_removes_hit;
+ unsigned long num_removes_miss;
+ unsigned int division_offset;
+ unsigned int division_size;
+ unsigned int queue_size;
+ unsigned int cache_data_offset;
+ unsigned int cache_data_size;
+ unsigned char division_mask;
+ unsigned char index_num;
+ unsigned char index_offset;
+ unsigned char index_size;
+#endif
+} SHMCBHeader;
+
+/*
+ * Index - can be memcpy'd to and from an index inside each
+ * queue's index array.
+ */
+typedef struct {
+ time_t expires;
+ unsigned int offset;
+ unsigned char s_id2;
+ unsigned char removed;
+} SHMCBIndex;
+
+/*
+ * Queue - must be populated by a call to shmcb_get_division
+ * and the structure's pointers are used for updating (ie.
+ * the structure doesn't need any "set" to update values).
+ */
+typedef struct {
+ SHMCBHeader *header;
+ unsigned int *first_pos;
+ unsigned int *pos_count;
+ SHMCBIndex *indexes;
+} SHMCBQueue;
+
+/*
+ * Cache - same comment as for Queue. 'Queue's are in a 1-1
+ * correspondance with 'Cache's and are usually carried round
+ * in a pair, they are only seperated for clarity.
+ */
+typedef struct {
+ SHMCBHeader *header;
+ unsigned int *first_pos;
+ unsigned int *pos_count;
+ unsigned char *data;
+} SHMCBCache;
+
+/*
+ * Forward function prototypes.
+ */
+
+/* Functions for working around data-alignment-picky systems (sparcs,
+ Irix, etc). These use "memcpy" as a way of foxing these systems into
+ treating the composite types as byte-arrays rather than higher-level
+ primitives that it prefers to have 4-(or 8-)byte aligned. I don't
+ envisage this being a performance issue as a couple of 2 or 4 byte
+ memcpys can hardly make a dent on the massive memmove operations this
+ cache technique avoids, nor the overheads of ASN en/decoding. */
+static unsigned int shmcb_get_safe_uint(unsigned int *);
+static void shmcb_set_safe_uint(unsigned int *, unsigned int);
+#if 0 /* Unused so far */
+static unsigned long shmcb_get_safe_ulong(unsigned long *);
+static void shmcb_set_safe_ulong(unsigned long *, unsigned long);
+#endif
+static time_t shmcb_get_safe_time(time_t *);
+static void shmcb_set_safe_time(time_t *, time_t);
+
+/* Underlying functions for session-caching */
+static BOOL shmcb_init_memory(server_rec *, void *, unsigned int);
+static BOOL shmcb_store_session(server_rec *, void *, UCHAR *, int, SSL_SESSION *, time_t);
+static SSL_SESSION *shmcb_retrieve_session(server_rec *, void *, UCHAR *, int);
+static BOOL shmcb_remove_session(server_rec *, void *, UCHAR *, int);
+
+/* Utility functions for manipulating the structures */
+static void shmcb_get_header(void *, SHMCBHeader **);
+static BOOL shmcb_get_division(SHMCBHeader *, SHMCBQueue *, SHMCBCache *, unsigned int);
+static SHMCBIndex *shmcb_get_index(const SHMCBQueue *, unsigned int);
+static unsigned int shmcb_expire_division(server_rec *, SHMCBQueue *, SHMCBCache *);
+static BOOL shmcb_insert_encoded_session(server_rec *, SHMCBQueue *, SHMCBCache *, unsigned char *, unsigned int, unsigned char *, time_t);
+static SSL_SESSION *shmcb_lookup_session_id(server_rec *, SHMCBQueue *, SHMCBCache *, UCHAR *, int);
+static BOOL shmcb_remove_session_id(server_rec *, SHMCBQueue *, SHMCBCache *, UCHAR *, int);
+
+/*
+ * Data-alignment functions (a.k.a. avoidance tactics)
+ *
+ * NB: On HPUX (and possibly others) there is a *very* mischievous little
+ * "optimisation" in the compilers where it will convert the following;
+ * memcpy(dest_ptr, &source, sizeof(unsigned int));
+ * (where dest_ptr is of type (unsigned int *) and source is (unsigned int))
+ * into;
+ * *dest_ptr = source; (or *dest_ptr = *(&source), not sure).
+ * Either way, it completely destroys the whole point of these _safe_
+ * functions, because the assignment operation will fall victim to the
+ * architecture's byte-alignment dictations, whereas the memcpy (as a
+ * byte-by-byte copy) should not. sigh. So, if you're wondering about the
+ * apparently unnecessary conversions to (unsigned char *) in these
+ * functions, you now have an explanation. Don't just revert them back and
+ * say "ooh look, it still works" - if you try it on HPUX (well, 32-bit
+ * HPUX 11.00 at least) you may find it fails with a SIGBUS. :-(
+ */
+
+static unsigned int shmcb_get_safe_uint(unsigned int *ptr)
+{
+ unsigned char *from;
+ unsigned int ret;
+
+ from = (unsigned char *)ptr;
+ memcpy(&ret, from, sizeof(unsigned int));
+ return ret;
+}
+
+static void shmcb_set_safe_uint(unsigned int *ptr, unsigned int val)
+{
+ unsigned char *to, *from;
+
+ to = (unsigned char *)ptr;
+ from = (unsigned char *)(&val);
+ memcpy(to, from, sizeof(unsigned int));
+}
+
+#if 0 /* Unused so far */
+static unsigned long shmcb_get_safe_ulong(unsigned long *ptr)
+{
+ unsigned char *from;
+ unsigned long ret;
+
+ from = (unsigned char *)ptr;
+ memcpy(&ret, from, sizeof(unsigned long));
+ return ret;
+}
+
+static void shmcb_set_safe_ulong(unsigned long *ptr, unsigned long val)
+{
+ unsigned char *to, *from;
+
+ to = (unsigned char *)ptr;
+ from = (unsigned char *)(&val);
+ memcpy(to, from, sizeof(unsigned long));
+}
+#endif
+
+static time_t shmcb_get_safe_time(time_t * ptr)
+{
+ unsigned char *from;
+ time_t ret;
+
+ from = (unsigned char *)ptr;
+ memcpy(&ret, from, sizeof(time_t));
+ return ret;
+}
+
+static void shmcb_set_safe_time(time_t * ptr, time_t val)
+{
+ unsigned char *to, *from;
+
+ to = (unsigned char *)ptr;
+ from = (unsigned char *)(&val);
+ memcpy(to, from, sizeof(time_t));
+}
+
+/*
+**
+** High-Level "handlers" as per ssl_scache.c
+**
+*/
+
+static void *shmcb_malloc(size_t size)
+{
+ SSLModConfigRec *mc = myModConfig();
+ return ap_mm_malloc(mc->pSessionCacheDataMM, size);
+}
+
+void ssl_scache_shmcb_init(server_rec *s, pool *p)
+{
+ SSLModConfigRec *mc = myModConfig();
+ AP_MM *mm;
+ void *shm_segment = NULL;
+ int avail, avail_orig;
+
+ /*
+ * Create shared memory segment
+ */
+ if (mc->szSessionCacheDataFile == NULL) {
+ ssl_log(s, SSL_LOG_ERROR, "SSLSessionCache required");
+ ssl_die();
+ }
+ if ((mm = ap_mm_create(mc->nSessionCacheDataSize,
+ mc->szSessionCacheDataFile)) == NULL) {
+ ssl_log(s, SSL_LOG_ERROR,
+ "Cannot allocate shared memory: %s", ap_mm_error());
+ ssl_die();
+ }
+ mc->pSessionCacheDataMM = mm;
+
+ /*
+ * Make sure the child processes have access to the underlying files
+ */
+ ap_mm_permission(mm, SSL_MM_FILE_MODE, ap_user_id, -1);
+
+ /*
+ * Create cache inside the shared memory segment
+ */
+ avail = avail_orig = ap_mm_available(mm);
+ ssl_log(s, SSL_LOG_TRACE, "Shared-memory segment has %u available",
+ avail);
+
+ /*
+ * For some reason to do with MM's internal management, I can't
+ * allocate the full amount. Implement a reasonable form of trial
+ * and error and output trace information.
+ */
+ while ((shm_segment == NULL) && ((avail_orig - avail) * 100 < avail_orig)) {
+ shm_segment = shmcb_malloc(avail);
+ if (shm_segment == NULL) {
+ ssl_log(s, SSL_LOG_TRACE,
+ "shmcb_malloc attempt for %u bytes failed", avail);
+ avail -= 2;
+ }
+ }
+ if (shm_segment == NULL) {
+ ssl_log(s, SSL_LOG_ERROR,
+ "Cannot allocate memory for the 'shmcb' session cache\n");
+ ssl_die();
+ }
+ ssl_log(s, SSL_LOG_TRACE, "shmcb_init allocated %u bytes of shared "
+ "memory", avail);
+ if (!shmcb_init_memory(s, shm_segment, avail)) {
+ ssl_log(s, SSL_LOG_ERROR,
+ "Failure initialising 'shmcb' shared memory");
+ ssl_die();
+ }
+ ssl_log(s, SSL_LOG_INFO, "Shared memory session cache initialised");
+
+ /*
+ * Success ... we hack the memory block into place by cheating for
+ * now and stealing a member variable the original shared memory
+ * cache was using. :-)
+ */
+ mc->tSessionCacheDataTable = (table_t *) shm_segment;
+ return;
+}
+
+void ssl_scache_shmcb_kill(server_rec *s)
+{
+ SSLModConfigRec *mc = myModConfig();
+
+ if (mc->pSessionCacheDataMM != NULL) {
+ ap_mm_destroy(mc->pSessionCacheDataMM);
+ mc->pSessionCacheDataMM = NULL;
+ }
+ return;
+}
+
+BOOL ssl_scache_shmcb_store(server_rec *s, UCHAR * id, int idlen,
+ time_t timeout, SSL_SESSION * pSession)
+{
+ SSLModConfigRec *mc = myModConfig();
+ void *shm_segment;
+ BOOL to_return = FALSE;
+
+ /* We've kludged our pointer into the other cache's member variable. */
+ shm_segment = (void *) mc->tSessionCacheDataTable;
+ ssl_mutex_on(s);
+ if (!shmcb_store_session(s, shm_segment, id, idlen, pSession, timeout))
+ /* in this cache engine, "stores" should never fail. */
+ ssl_log(s, SSL_LOG_ERROR, "'shmcb' code was unable to store a "
+ "session in the cache.");
+ else {
+ ssl_log(s, SSL_LOG_TRACE, "shmcb_store successful");
+ to_return = TRUE;
+ }
+ ssl_mutex_off(s);
+ return to_return;
+}
+
+SSL_SESSION *ssl_scache_shmcb_retrieve(server_rec *s, UCHAR * id, int idlen)
+{
+ SSLModConfigRec *mc = myModConfig();
+ void *shm_segment;
+ SSL_SESSION *pSession;
+
+ /* We've kludged our pointer into the other cache's member variable. */
+ shm_segment = (void *) mc->tSessionCacheDataTable;
+ ssl_mutex_on(s);
+ pSession = shmcb_retrieve_session(s, shm_segment, id, idlen);
+ ssl_mutex_off(s);
+ if (pSession)
+ ssl_log(s, SSL_LOG_TRACE, "shmcb_retrieve had a hit");
+ else {
+ ssl_log(s, SSL_LOG_TRACE, "shmcb_retrieve had a miss");
+ ssl_log(s, SSL_LOG_INFO, "Client requested a 'session-resume' but "
+ "we have no such session.");
+ }
+ return pSession;
+}
+
+void ssl_scache_shmcb_remove(server_rec *s, UCHAR * id, int idlen)
+{
+ SSLModConfigRec *mc = myModConfig();
+ void *shm_segment;
+
+ /* We've kludged our pointer into the other cache's member variable. */
+ shm_segment = (void *) mc->tSessionCacheDataTable;
+ shmcb_remove_session(s, shm_segment, id, idlen);
+}
+
+void ssl_scache_shmcb_expire(server_rec *s)
+{
+ /* NOP */
+ return;
+}
+
+void ssl_scache_shmcb_status(server_rec *s, pool *p,
+ void (*func) (char *, void *), void *arg)
+{
+ SSLModConfigRec *mc = myModConfig();
+ SHMCBHeader *header;
+ SHMCBQueue queue;
+ SHMCBCache cache;
+ SHMCBIndex *idx;
+ void *shm_segment;
+ unsigned int loop, total, cache_total, non_empty_divisions;
+ int index_pct, cache_pct;
+ double expiry_total;
+ time_t average_expiry, now, max_expiry, min_expiry, idxexpiry;
+
+ ssl_log(s, SSL_LOG_TRACE, "inside ssl_scache_shmcb_status");
+
+ /* We've kludged our pointer into the other cache's member variable. */
+ shm_segment = (void *) mc->tSessionCacheDataTable;
+
+ /* Get the header structure. */
+ shmcb_get_header(shm_segment, &header);
+ total = cache_total = non_empty_divisions = 0;
+ average_expiry = max_expiry = min_expiry = 0;
+ expiry_total = 0;
+
+ /* It may seem strange to grab "now" at this point, but in theory
+ * we should never have a negative threshold but grabbing "now" after
+ * the loop (which performs expiries) could allow that chance. */
+ now = time(NULL);
+ for (loop = 0; loop <= header->division_mask; loop++) {
+ if (shmcb_get_division(header, &queue, &cache, loop)) {
+ shmcb_expire_division(s, &queue, &cache);
+ total += shmcb_get_safe_uint(queue.pos_count);
+ cache_total += shmcb_get_safe_uint(cache.pos_count);
+ if (shmcb_get_safe_uint(queue.pos_count) > 0) {
+ idx = shmcb_get_index(&queue,
+ shmcb_get_safe_uint(queue.first_pos));
+ non_empty_divisions++;
+ idxexpiry = shmcb_get_safe_time(&(idx->expires));
+ expiry_total += (double) idxexpiry;
+ max_expiry = (idxexpiry > max_expiry ? idxexpiry :
+ max_expiry);
+ if (min_expiry == 0)
+ min_expiry = idxexpiry;
+ else
+ min_expiry = (idxexpiry < min_expiry ? idxexpiry :
+ min_expiry);
+ }
+ }
+ }
+ index_pct = (100 * total) / (header->index_num * (header->division_mask + 1));
+ cache_pct = (100 * cache_total) / (header->cache_data_size * (header->division_mask + 1));
+ func(ap_psprintf(p, "cache type: <b>SHMCB</b>, shared memory: <b>%d</b> "
+ "bytes, current sessions: <b>%d</b><br>",
+ mc->nSessionCacheDataSize, total), arg);
+ func(ap_psprintf(p, "sub-caches: <b>%d</b>, indexes per sub-cache: "
+ "<b>%d</b><br>", (int) header->division_mask + 1,
+ (int) header->index_num), arg);
+ if (non_empty_divisions != 0) {
+ average_expiry = (time_t)(expiry_total / (double)non_empty_divisions);
+ func(ap_psprintf(p, "time left on oldest entries' SSL sessions: "), arg);
+ if (now < average_expiry)
+ func(ap_psprintf(p, "avg: <b>%d</b> seconds, (range: %d...%d)<br>",
+ (int)(average_expiry - now), (int) (min_expiry - now),
+ (int)(max_expiry - now)), arg);
+ else
+ func(ap_psprintf(p, "expiry threshold: <b>Calculation Error!</b>"
+ "<br>"), arg);
+
+ }
+ func(ap_psprintf(p, "index usage: <b>%d%%</b>, cache usage: <b>%d%%</b>"
+ "<br>", index_pct, cache_pct), arg);
+ func(ap_psprintf(p, "total sessions stored since starting: <b>%lu</b><br>",
+ header->num_stores), arg);
+ func(ap_psprintf(p, "total sessions expired since starting: <b>%lu</b><br>",
+ header->num_expiries), arg);
+ func(ap_psprintf(p, "total (pre-expiry) sessions scrolled out of the "
+ "cache: <b>%lu</b><br>", header->num_scrolled), arg);
+ func(ap_psprintf(p, "total retrieves since starting: <b>%lu</b> hit, "
+ "<b>%lu</b> miss<br>", header->num_retrieves_hit,
+ header->num_retrieves_miss), arg);
+ func(ap_psprintf(p, "total removes since starting: <b>%lu</b> hit, "
+ "<b>%lu</b> miss<br>", header->num_removes_hit,
+ header->num_removes_miss), arg);
+ ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_status");
+ return;
+}
+
+/*
+**
+** Memory manipulation and low-level cache operations
+**
+*/
+
+static BOOL shmcb_init_memory(
+ server_rec *s, void *shm_mem,
+ unsigned int shm_mem_size)
+{
+ SHMCBHeader *header;
+ SHMCBQueue queue;
+ SHMCBCache cache;
+ unsigned int temp, loop, granularity;
+
+ ssl_log(s, SSL_LOG_TRACE, "entered shmcb_init_memory()");
+
+ /* Calculate some sizes... */
+ temp = sizeof(SHMCBHeader);
+
+ /* If the segment is ridiculously too small, bail out */
+ if (shm_mem_size < (2*temp)) {
+ ssl_log(s, SSL_LOG_ERROR, "shared memory segment too small");
+ return FALSE;
+ }
+
+ /* Make temp the amount of memory without the header */
+ temp = shm_mem_size - temp;
+
+ /* Work on the basis that you need 10 bytes index for each session
+ * (approx 150 bytes), which is to divide temp by 160 - and then
+ * make sure we err on having too index space to burn even when
+ * the cache is full, which is a lot less stupid than having
+ * having not enough index space to utilise the whole cache!. */
+ temp /= 120;
+ ssl_log(s, SSL_LOG_TRACE, "for %u bytes, recommending %u indexes",
+ shm_mem_size, temp);
+
+ /* We should divide these indexes evenly amongst the queues. Try
+ * to get it so that there are roughly half the number of divisions
+ * as there are indexes in each division. */
+ granularity = 256;
+ while ((temp / granularity) < (2 * granularity))
+ granularity /= 2;
+
+ /* So we have 'granularity' divisions, set 'temp' equal to the
+ * number of indexes in each division. */
+ temp /= granularity;
+
+ /* Too small? Bail ... */
+ if (temp < 5) {
+ ssl_log(s, SSL_LOG_ERROR, "shared memory segment too small");
+ return FALSE;
+ }
+
+ /* OK, we're sorted - from here on in, the return should be TRUE */
+ header = (SHMCBHeader *)shm_mem;
+ header->division_mask = (unsigned char)(granularity - 1);
+ header->division_offset = sizeof(SHMCBHeader);
+ header->index_num = temp;
+ header->index_offset = (2 * sizeof(unsigned int));
+ header->index_size = sizeof(SHMCBIndex);
+ header->queue_size = header->index_offset +
+ (header->index_num * header->index_size);
+
+ /* Now calculate the space for each division */
+ temp = shm_mem_size - header->division_offset;
+ header->division_size = temp / granularity;
+
+ /* Calculate the space left in each division for the cache */
+ temp -= header->queue_size;
+ header->cache_data_offset = (2 * sizeof(unsigned int));
+ header->cache_data_size = header->division_size -
+ header->queue_size - header->cache_data_offset;
+
+ /* Output trace info */
+ ssl_log(s, SSL_LOG_TRACE, "shmcb_init_memory choices follow");
+ ssl_log(s, SSL_LOG_TRACE, "division_mask = 0x%02X", header->division_mask);
+ ssl_log(s, SSL_LOG_TRACE, "division_offset = %u", header->division_offset);
+ ssl_log(s, SSL_LOG_TRACE, "division_size = %u", header->division_size);
+ ssl_log(s, SSL_LOG_TRACE, "queue_size = %u", header->queue_size);
+ ssl_log(s, SSL_LOG_TRACE, "index_num = %u", header->index_num);
+ ssl_log(s, SSL_LOG_TRACE, "index_offset = %u", header->index_offset);
+ ssl_log(s, SSL_LOG_TRACE, "index_size = %u", header->index_size);
+ ssl_log(s, SSL_LOG_TRACE, "cache_data_offset = %u", header->cache_data_offset);
+ ssl_log(s, SSL_LOG_TRACE, "cache_data_size = %u", header->cache_data_size);
+
+ /* The header is done, make the caches empty */
+ for (loop = 0; loop < granularity; loop++) {
+ if (!shmcb_get_division(header, &queue, &cache, loop))
+ ssl_log(s, SSL_LOG_ERROR, "shmcb_init_memory, " "internal error");
+ shmcb_set_safe_uint(cache.first_pos, 0);
+ shmcb_set_safe_uint(cache.pos_count, 0);
+ shmcb_set_safe_uint(queue.first_pos, 0);
+ shmcb_set_safe_uint(queue.pos_count, 0);
+ }
+
+ ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_init_memory()");
+ return TRUE;
+}
+
+static BOOL shmcb_store_session(
+ server_rec *s, void *shm_segment, UCHAR * id,
+ int idlen, SSL_SESSION * pSession,
+ time_t timeout)
+{
+ SHMCBHeader *header;
+ SHMCBQueue queue;
+ SHMCBCache cache;
+ unsigned char masked_index;
+ unsigned char encoded[SSL_SESSION_MAX_DER];
+ unsigned char *ptr_encoded;
+ unsigned int len_encoded;
+ time_t expiry_time;
+
+ ssl_log(s, SSL_LOG_TRACE, "inside shmcb_store_session");
+
+ /* Get the header structure, which division this session will fall into etc. */
+ shmcb_get_header(shm_segment, &header);
+ masked_index = pSession->session_id[0] & header->division_mask;
+ ssl_log(s, SSL_LOG_TRACE, "session_id[0]=%u, masked index=%u",
+ pSession->session_id[0], masked_index);
+ if (!shmcb_get_division(header, &queue, &cache, (unsigned int)masked_index)) {
+ ssl_log(s, SSL_LOG_ERROR, "shmcb_store_session, " "internal error");
+ return FALSE;
+ }
+
+ /* Serialise the session, work out how much we're dealing
+ * with. NB: This check could be removed if we're not paranoid
+ * or we find some assurance that it will never be necessary. */
+ len_encoded = i2d_SSL_SESSION(pSession, NULL);
+ if (len_encoded > SSL_SESSION_MAX_DER) {
+ ssl_log(s, SSL_LOG_ERROR, "session is too big (%u bytes)",
+ len_encoded);
+ return FALSE;
+ }
+ ptr_encoded = encoded;
+ len_encoded = i2d_SSL_SESSION(pSession, &ptr_encoded);
+ expiry_time = timeout;
+ if (!shmcb_insert_encoded_session(s, &queue, &cache, encoded,
+ len_encoded, pSession->session_id,
+ expiry_time)) {
+ ssl_log(s, SSL_LOG_ERROR, "can't store a session!");
+ return FALSE;
+ }
+ ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_store successfully");
+ header->num_stores++;
+ return TRUE;
+}
+
+static SSL_SESSION *shmcb_retrieve_session(
+ server_rec *s, void *shm_segment,
+ UCHAR * id, int idlen)
+{
+ SHMCBHeader *header;
+ SHMCBQueue queue;
+ SHMCBCache cache;
+ unsigned char masked_index;
+ SSL_SESSION *pSession;
+
+ ssl_log(s, SSL_LOG_TRACE, "inside shmcb_retrieve_session");
+ if (idlen < 2) {
+ ssl_log(s, SSL_LOG_ERROR, "unusably short session_id provided "
+ "(%u bytes)", idlen);
+ return FALSE;
+ }
+
+ /* Get the header structure, which division this session lookup
+ * will come from etc. */
+ shmcb_get_header(shm_segment, &header);
+ masked_index = id[0] & header->division_mask;
+ ssl_log(s, SSL_LOG_TRACE, "id[0]=%u, masked index=%u", id[0],
+ masked_index);
+ if (!shmcb_get_division(header, &queue, &cache, (unsigned int) masked_index)) {
+ ssl_log(s, SSL_LOG_ERROR, "shmcb_retrieve_session, " "internal error");
+ header->num_retrieves_miss++;
+ return FALSE;
+ }
+
+ /* Get the session corresponding to the session_id or NULL if it
+ * doesn't exist (or is flagged as "removed"). */
+ pSession = shmcb_lookup_session_id(s, &queue, &cache, id, idlen);
+ if (pSession)
+ header->num_retrieves_hit++;
+ else
+ header->num_retrieves_miss++;
+ ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_retrieve_session");
+ return pSession;
+}
+
+static BOOL shmcb_remove_session(
+ server_rec *s, void *shm_segment,
+ UCHAR * id, int idlen)
+{
+ SHMCBHeader *header;
+ SHMCBQueue queue;
+ SHMCBCache cache;
+ unsigned char masked_index;
+ BOOL res;
+
+ ssl_log(s, SSL_LOG_TRACE, "inside shmcb_remove_session");
+ if (id == NULL) {
+ ssl_log(s, SSL_LOG_ERROR, "remove called with NULL session_id!");
+ return FALSE;
+ }
+
+ /* Get the header structure, which division this session remove
+ * will happen in etc. */
+ shmcb_get_header(shm_segment, &header);
+ masked_index = id[0] & header->division_mask;
+ ssl_log(s, SSL_LOG_TRACE, "id[0]=%u, masked index=%u",
+ id[0], masked_index);
+ if (!shmcb_get_division(header, &queue, &cache, (unsigned int)masked_index)) {
+ ssl_log(s, SSL_LOG_ERROR, "shmcb_remove_session, internal error");
+ header->num_removes_miss++;
+ return FALSE;
+ }
+ res = shmcb_remove_session_id(s, &queue, &cache, id, idlen);
+ if (res)
+ header->num_removes_hit++;
+ else
+ header->num_removes_miss++;
+ ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_remove_session");
+ return res;
+}
+
+
+/*
+**
+** Weirdo cyclic buffer functions
+**
+*/
+
+/* This gets used in the cyclic "index array" (in the 'Queue's) and
+ * in the cyclic 'Cache's too ... you provide the "width" of the
+ * cyclic store, the starting position and how far to move (with
+ * wrapping if necessary). Basically it's addition modulo buf_size. */
+static unsigned int shmcb_cyclic_increment(
+ unsigned int buf_size,
+ unsigned int start_pos,
+ unsigned int to_add)
+{
+ start_pos += to_add;
+ while (start_pos >= buf_size)
+ start_pos -= buf_size;
+ return start_pos;
+}
+
+/* Given two positions in a cyclic buffer, calculate the "distance".
+ * This is to cover the case ("non-trivial") where the 'next' offset
+ * is to the left of the 'start' offset. NB: This calculates the
+ * space inclusive of one end-point but not the other. There is an
+ * ambiguous case (which is why we use the <start_pos,offset>
+ * coordinate system rather than <start_pos,end_pos> one) when 'start'
+ * is the same as 'next'. It could indicate the buffer is full or it
+ * can indicate the buffer is empty ... I choose the latter as it's
+ * easier and usually necessary to check if the buffer is full anyway
+ * before doing incremental logic (which is this useful for), but we
+ * definitely need the empty case handled - in fact it's our starting
+ * state!! */
+static unsigned int shmcb_cyclic_space(
+ unsigned int buf_size,
+ unsigned int start_offset,
+ unsigned int next_offset)
+{
+ /* Is it the trivial case? */
+ if (start_offset <= next_offset)
+ return (next_offset - start_offset); /* yes */
+ else
+ return ((buf_size - start_offset) + next_offset); /* no */
+}
+
+/* A "normal-to-cyclic" memcpy ... this takes a linear block of
+ * memory and copies it onto a cyclic buffer. The purpose and
+ * function of this is pretty obvious, you need to cover the case
+ * that the destination (cyclic) buffer has to wrap round. */
+static void shmcb_cyclic_ntoc_memcpy(
+ unsigned int buf_size,
+ unsigned char *data,
+ unsigned int dest_offset,
+ unsigned char *src, unsigned int src_len)
+{
+ /* Can it be copied all in one go? */
+ if (dest_offset + src_len < buf_size)
+ /* yes */
+ memcpy(data + dest_offset, src, src_len);
+ else {
+ /* no */
+ memcpy(data + dest_offset, src, buf_size - dest_offset);
+ memcpy(data, src + buf_size - dest_offset,
+ src_len + dest_offset - buf_size);
+ }
+ return;
+}
+
+/* A "cyclic-to-normal" memcpy ... given the last function, this
+ * one's purpose is clear, it copies out of a cyclic buffer handling
+ * wrapping. */
+static void shmcb_cyclic_cton_memcpy(
+ unsigned int buf_size,
+ unsigned char *dest,
+ unsigned char *data,
+ unsigned int src_offset,
+ unsigned int src_len)
+{
+ /* Can it be copied all in one go? */
+ if (src_offset + src_len < buf_size)
+ /* yes */
+ memcpy(dest, data + src_offset, src_len);
+ else {
+ /* no */
+ memcpy(dest, data + src_offset, buf_size - src_offset);
+ memcpy(dest + buf_size - src_offset, data,
+ src_len + src_offset - buf_size);
+ }
+ return;
+}
+
+/* Here's the cool hack that makes it all work ... by simply
+ * making the first collection of bytes *be* our header structure
+ * (casting it into the C structure), we have the perfect way to
+ * maintain state in a shared-memory session cache from one call
+ * (and process) to the next, use the shared memory itself! The
+ * original mod_ssl shared-memory session cache uses variables
+ * inside the context, but we simply use that for storing the
+ * pointer to the shared memory itself. And don't forget, after
+ * Apache's initialisation, this "header" is constant/read-only
+ * so we can read it outside any locking.
+ * <grin> - sometimes I just *love* coding y'know?! */
+static void shmcb_get_header(void *shm_mem, SHMCBHeader **header)
+{
+ *header = (SHMCBHeader *)shm_mem;
+ return;
+}
+
+/* This is what populates our "interesting" structures. Given a
+ * pointer to the header, and an index into the appropriate
+ * division (this must have already been masked using the
+ * division_mask by the caller!), we can populate the provided
+ * SHMCBQueue and SHMCBCache structures with values and
+ * pointers to the underlying shared memory. Upon returning
+ * (if not FALSE), the caller can meddle with the pointer
+ * values and they will map into the shared-memory directly,
+ * as such there's no need to "free" or "set" the Queue or
+ * Cache values, they were themselves references to the *real*
+ * data. */
+static BOOL shmcb_get_division(
+ SHMCBHeader *header, SHMCBQueue *queue,
+ SHMCBCache *cache, unsigned int idx)
+{
+ unsigned char *pQueue;
+ unsigned char *pCache;
+
+ /* bounds check */
+ if (idx > (unsigned int) header->division_mask)
+ return FALSE;
+
+ /* Locate the blocks of memory storing the corresponding data */
+ pQueue = ((unsigned char *) header) + header->division_offset +
+ (idx * header->division_size);
+ pCache = pQueue + header->queue_size;
+
+ /* Populate the structures with appropriate pointers */
+ queue->first_pos = (unsigned int *) pQueue;
+
+ /* Our structures stay packed, no matter what the system's
+ * data-alignment regime is. */
+ queue->pos_count = (unsigned int *) (pQueue + sizeof(unsigned int));
+ queue->indexes = (SHMCBIndex *) (pQueue + (2 * sizeof(unsigned int)));
+ cache->first_pos = (unsigned int *) pCache;
+ cache->pos_count = (unsigned int *) (pCache + sizeof(unsigned int));
+ cache->data = (unsigned char *) (pCache + (2 * sizeof(unsigned int)));
+ queue->header = cache->header = header;
+
+ return TRUE;
+}
+
+/* This returns a pointer to the piece of shared memory containing
+ * a specified 'Index'. SHMCBIndex, like SHMCBHeader, is a fixed
+ * width non-referencing structure of primitive types that can be
+ * cast onto the corresponding block of shared memory. Thus, by
+ * returning a cast pointer to that section of shared memory, the
+ * caller can read and write values to and from the "structure" and
+ * they are actually reading and writing the underlying shared
+ * memory. */
+static SHMCBIndex *shmcb_get_index(
+ const SHMCBQueue *queue, unsigned int idx)
+{
+ /* bounds check */
+ if (idx > (unsigned int) queue->header->index_num)
+ return NULL;
+
+ /* Return a pointer to the index. NB: I am being horribly pendantic
+ * here so as to avoid any potential data-alignment assumptions being
+ * placed on the pointer arithmetic by the compiler (sigh). */
+ return (SHMCBIndex *)(((unsigned char *) queue->indexes) +
+ (idx * sizeof(SHMCBIndex)));
+}
+
+/* This functions rolls expired cache (and index) entries off the front
+ * of the cyclic buffers in a division. The function returns the number
+ * of expired sessions. */
+static unsigned int shmcb_expire_division(
+ server_rec *s, SHMCBQueue *queue, SHMCBCache *cache)
+{
+ SHMCBIndex *idx;
+ time_t now;
+ unsigned int loop, index_num, pos_count, new_pos;
+ SHMCBHeader *header;
+
+ ssl_log(s, SSL_LOG_TRACE, "entering shmcb_expire_division");
+
+ /* We must calculate num and space ourselves based on expiry times. */
+ now = time(NULL);
+ loop = 0;
+ new_pos = shmcb_get_safe_uint(queue->first_pos);
+
+ /* Cache useful values */
+ header = queue->header;
+ index_num = header->index_num;
+ pos_count = shmcb_get_safe_uint(queue->pos_count);
+ while (loop < pos_count) {
+ idx = shmcb_get_index(queue, new_pos);
+ if (shmcb_get_safe_time(&(idx->expires)) > now)
+ /* it hasn't expired yet, we're done iterating */
+ break;
+ /* This one should be expired too. Shift to the next entry. */
+ loop++;
+ new_pos = shmcb_cyclic_increment(index_num, new_pos, 1);
+ }
+
+ /* Find the new_offset and make the expiries happen. */
+ if (loop > 0) {
+ ssl_log(s, SSL_LOG_TRACE, "will be expiring %u sessions", loop);
+ /* We calculate the new_offset by "peeking" (or in the
+ * case it's the last entry, "sneaking" ;-). */
+ if (loop == pos_count) {
+ /* We are expiring everything! This is easy to do... */
+ shmcb_set_safe_uint(queue->pos_count, 0);
+ shmcb_set_safe_uint(cache->pos_count, 0);
+ }
+ else {
+ /* The Queue is easy to adjust */
+ shmcb_set_safe_uint(queue->pos_count,
+ shmcb_get_safe_uint(queue->pos_count) - loop);
+ shmcb_set_safe_uint(queue->first_pos, new_pos);
+ /* peek to the start of the next session */
+ idx = shmcb_get_index(queue, new_pos);
+ /* We can use shmcb_cyclic_space because we've guaranteed
+ * we don't fit the ambiguous full/empty case. */
+ shmcb_set_safe_uint(cache->pos_count,
+ shmcb_get_safe_uint(cache->pos_count) -
+ shmcb_cyclic_space(header->cache_data_size,
+ shmcb_get_safe_uint(cache->first_pos),
+ shmcb_get_safe_uint(&(idx->offset))));
+ shmcb_set_safe_uint(cache->first_pos, shmcb_get_safe_uint(&(idx->offset)));
+ }
+ ssl_log(s, SSL_LOG_TRACE, "we now have %u sessions",
+ shmcb_get_safe_uint(queue->pos_count));
+ }
+ header->num_expiries += loop;
+ return loop;
+}
+
+/* Inserts a new encoded session into a queue/cache pair - expiring
+ * (early or otherwise) any leading sessions as necessary to ensure
+ * there is room. An error return (FALSE) should only happen in the
+ * event of surreal values being passed on, or ridiculously small
+ * cache sizes. NB: For tracing purposes, this function is also given
+ * the server_rec to allow "ssl_log()". */
+static BOOL shmcb_insert_encoded_session(
+ server_rec *s, SHMCBQueue * queue,
+ SHMCBCache * cache,
+ unsigned char *encoded,
+ unsigned int encoded_len,
+ unsigned char *session_id,
+ time_t expiry_time)
+{
+ SHMCBHeader *header;
+ SHMCBIndex *idx = NULL;
+ unsigned int gap, new_pos, loop, new_offset;
+ int need;
+
+ ssl_log(s, SSL_LOG_TRACE, "entering shmcb_insert_encoded_session, "
+ "*queue->pos_count = %u", shmcb_get_safe_uint(queue->pos_count));
+
+ /* If there's entries to expire, ditch them first thing. */
+ shmcb_expire_division(s, queue, cache);
+ header = cache->header;
+ gap = header->cache_data_size - shmcb_get_safe_uint(cache->pos_count);
+ if (gap < encoded_len) {
+ new_pos = shmcb_get_safe_uint(queue->first_pos);
+ loop = 0;
+ need = (int) encoded_len - (int) gap;
+ while ((need > 0) && (loop + 1 < shmcb_get_safe_uint(queue->pos_count))) {
+ new_pos = shmcb_cyclic_increment(header->index_num, new_pos, 1);
+ loop += 1;
+ idx = shmcb_get_index(queue, new_pos);
+ need = (int) encoded_len - (int) gap -
+ shmcb_cyclic_space(header->cache_data_size,
+ shmcb_get_safe_uint(cache->first_pos),
+ shmcb_get_safe_uint(&(idx->offset)));
+ }
+ if (loop > 0) {
+ ssl_log(s, SSL_LOG_TRACE, "about to scroll %u sessions from %u",
+ loop, shmcb_get_safe_uint(queue->pos_count));
+ /* We are removing "loop" items from the cache. */
+ shmcb_set_safe_uint(cache->pos_count,
+ shmcb_get_safe_uint(cache->pos_count) -
+ shmcb_cyclic_space(header->cache_data_size,
+ shmcb_get_safe_uint(cache->first_pos),
+ shmcb_get_safe_uint(&(idx->offset))));
+ shmcb_set_safe_uint(cache->first_pos, shmcb_get_safe_uint(&(idx->offset)));
+ shmcb_set_safe_uint(queue->pos_count, shmcb_get_safe_uint(queue->pos_count) - loop);
+ shmcb_set_safe_uint(queue->first_pos, new_pos);
+ ssl_log(s, SSL_LOG_TRACE, "now only have %u sessions",
+ shmcb_get_safe_uint(queue->pos_count));
+ /* Update the stats!!! */
+ header->num_scrolled += loop;
+ }
+ }
+
+ /* probably unecessary checks, but I'll leave them until this code
+ * is verified. */
+ if (shmcb_get_safe_uint(cache->pos_count) + encoded_len >
+ header->cache_data_size) {
+ ssl_log(s, SSL_LOG_ERROR, "shmcb_insert_encoded_session, "
+ "internal error");
+ return FALSE;
+ }
+ if (shmcb_get_safe_uint(queue->pos_count) == header->index_num) {
+ ssl_log(s, SSL_LOG_ERROR, "shmcb_insert_encoded_session, "
+ "internal error");
+ return FALSE;
+ }
+ ssl_log(s, SSL_LOG_TRACE, "we have %u bytes and %u indexes free - "
+ "enough", header->cache_data_size -
+ shmcb_get_safe_uint(cache->pos_count), header->index_num -
+ shmcb_get_safe_uint(queue->pos_count));
+
+
+ /* HERE WE ASSUME THAT THE NEW SESSION SHOULD GO ON THE END! I'M NOT
+ * CHECKING WHETHER IT SHOULD BE GENUINELY "INSERTED" SOMEWHERE.
+ *
+ * We either fix that, or find out at a "higher" (read "mod_ssl")
+ * level whether it is possible to have distinct session caches for
+ * any attempted tomfoolery to do with different session timeouts.
+ * Knowing in advance that we can have a cache-wide constant timeout
+ * would make this stuff *MUCH* more efficient. Mind you, it's very
+ * efficient right now because I'm ignoring this problem!!!
+ */
+
+ /* Increment to the first unused byte */
+ new_offset = shmcb_cyclic_increment(header->cache_data_size,
+ shmcb_get_safe_uint(cache->first_pos),
+ shmcb_get_safe_uint(cache->pos_count));
+ /* Copy the DER-encoded session into place */
+ shmcb_cyclic_ntoc_memcpy(header->cache_data_size, cache->data,
+ new_offset, encoded, encoded_len);
+ /* Get the new index that this session is stored in. */
+ new_pos = shmcb_cyclic_increment(header->index_num,
+ shmcb_get_safe_uint(queue->first_pos),
+ shmcb_get_safe_uint(queue->pos_count));
+ ssl_log(s, SSL_LOG_TRACE, "storing in index %u, at offset %u", new_pos,
+ new_offset);
+ idx = shmcb_get_index(queue, new_pos);
+ if (idx == NULL) {
+ ssl_log(s, SSL_LOG_ERROR, "shmcb_insert_encoded_session, "
+ "internal error");
+ return FALSE;
+ }
+ memset(idx, 0, sizeof(SHMCBIndex));
+ shmcb_set_safe_time(&(idx->expires), expiry_time);
+ shmcb_set_safe_uint(&(idx->offset), new_offset);
+
+ /* idx->removed = (unsigned char)0; */ /* Not needed given the memset above. */
+ idx->s_id2 = session_id[1];
+ ssl_log(s, SSL_LOG_TRACE, "session_id[0]=%u, idx->s_id2=%u",
+ session_id[0], session_id[1]);
+
+ /* All that remains is to adjust the cache's and queue's "pos_count"s. */
+ shmcb_set_safe_uint(cache->pos_count,
+ shmcb_get_safe_uint(cache->pos_count) + encoded_len);
+ shmcb_set_safe_uint(queue->pos_count,
+ shmcb_get_safe_uint(queue->pos_count) + 1);
+
+ /* And just for good debugging measure ... */
+ ssl_log(s, SSL_LOG_TRACE, "leaving now with %u bytes in the cache and "
+ "%u indexes", shmcb_get_safe_uint(cache->pos_count),
+ shmcb_get_safe_uint(queue->pos_count));
+ ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_insert_encoded_session");
+ return TRUE;
+}
+
+/* Performs a lookup into a queue/cache pair for a
+ * session_id. If found, the session is deserialised
+ * and returned, otherwise NULL. */
+static SSL_SESSION *shmcb_lookup_session_id(
+ server_rec *s, SHMCBQueue *queue,
+ SHMCBCache *cache, UCHAR *id,
+ int idlen)
+{
+ unsigned char tempasn[SSL_SESSION_MAX_DER];
+ SHMCBIndex *idx;
+ SHMCBHeader *header;
+ SSL_SESSION *pSession = NULL;
+ unsigned int curr_pos, loop, count;
+ unsigned char *ptr;
+ time_t now;
+
+ ssl_log(s, SSL_LOG_TRACE, "entering shmcb_lookup_session_id");
+
+ /* If there are entries to expire, ditch them first thing. */
+ shmcb_expire_division(s, queue, cache);
+ now = time(NULL);
+ curr_pos = shmcb_get_safe_uint(queue->first_pos);
+ count = shmcb_get_safe_uint(queue->pos_count);
+ header = queue->header;
+ for (loop = 0; loop < count; loop++) {
+ ssl_log(s, SSL_LOG_TRACE, "loop=%u, count=%u, curr_pos=%u",
+ loop, count, curr_pos);
+ idx = shmcb_get_index(queue, curr_pos);
+ ssl_log(s, SSL_LOG_TRACE, "idx->s_id2=%u, id[1]=%u, offset=%u",
+ idx->s_id2, id[1], shmcb_get_safe_uint(&(idx->offset)));
+ /* Only look into the session further if;
+ * (a) the second byte of the session_id matches,
+ * (b) the "removed" flag isn't set,
+ * (c) the session hasn't expired yet.
+ * We do (c) like this so that it saves us having to
+ * do natural expiries ... naturally expired sessions
+ * scroll off the front anyway when the cache is full and
+ * "rotating", the only real issue that remains is the
+ * removal or disabling of forcibly killed sessions. */
+ if ((idx->s_id2 == id[1]) && !idx->removed &&
+ (shmcb_get_safe_time(&(idx->expires)) > now)) {
+ ssl_log(s, SSL_LOG_TRACE, "at index %u, found possible "
+ "session match", curr_pos);
+ shmcb_cyclic_cton_memcpy(header->cache_data_size,
+ tempasn, cache->data,
+ shmcb_get_safe_uint(&(idx->offset)),
+ SSL_SESSION_MAX_DER);
+ ptr = tempasn;
+ pSession = d2i_SSL_SESSION(NULL, &ptr, SSL_SESSION_MAX_DER);
+ if (pSession == NULL) {
+ ssl_log(s, SSL_LOG_ERROR, "scach2_lookup_"
+ "session_id, internal error");
+ return NULL;
+ }
+ if ((pSession->session_id_length == idlen) &&
+ (memcmp(pSession->session_id, id, idlen) == 0)) {
+ ssl_log(s, SSL_LOG_TRACE, "a match!");
+ return pSession;
+ }
+ ssl_log(s, SSL_LOG_TRACE, "not a match");
+ SSL_SESSION_free(pSession);
+ pSession = NULL;
+ }
+ curr_pos = shmcb_cyclic_increment(header->index_num, curr_pos, 1);
+ }
+ ssl_log(s, SSL_LOG_TRACE, "no matching sessions were found");
+ return NULL;
+}
+
+static BOOL shmcb_remove_session_id(
+ server_rec *s, SHMCBQueue *queue,
+ SHMCBCache *cache, UCHAR *id, int idlen)
+{
+ unsigned char tempasn[SSL_SESSION_MAX_DER];
+ SSL_SESSION *pSession = NULL;
+ SHMCBIndex *idx;
+ SHMCBHeader *header;
+ unsigned int curr_pos, loop, count;
+ unsigned char *ptr;
+ BOOL to_return = FALSE;
+
+ ssl_log(s, SSL_LOG_TRACE, "entering shmcb_remove_session_id");
+
+ /* If there's entries to expire, ditch them first thing. */
+ /* shmcb_expire_division(s, queue, cache); */
+
+ /* Regarding the above ... hmmm ... I know my expiry code is slightly
+ * "faster" than all this remove stuff ... but if the higher level
+ * code calls a "remove" operation (and this *only* seems to happen
+ * when it has spotted an expired session before we had a chance to)
+ * then it should get credit for a remove (stats-wise). Also, in the
+ * off-chance that the server *requests* a renegotiate and wants to
+ * wipe the session clean we should give that priority over our own
+ * routine expiry handling. So I've moved the expiry check to *after*
+ * this general remove stuff. */
+ curr_pos = shmcb_get_safe_uint(queue->first_pos);
+ count = shmcb_get_safe_uint(queue->pos_count);
+ header = cache->header;
+ for (loop = 0; loop < count; loop++) {
+ ssl_log(s, SSL_LOG_TRACE, "loop=%u, count=%u, curr_pos=%u",
+ loop, count, curr_pos);
+ idx = shmcb_get_index(queue, curr_pos);
+ ssl_log(s, SSL_LOG_TRACE, "idx->s_id2=%u, id[1]=%u", idx->s_id2,
+ id[1]);
+ /* Only look into the session further if the second byte of the
+ * session_id matches. */
+ if (idx->s_id2 == id[1]) {
+ ssl_log(s, SSL_LOG_TRACE, "at index %u, found possible "
+ "session match", curr_pos);
+ shmcb_cyclic_cton_memcpy(header->cache_data_size,
+ tempasn, cache->data,
+ shmcb_get_safe_uint(&(idx->offset)),
+ SSL_SESSION_MAX_DER);
+ ptr = tempasn;
+ pSession = d2i_SSL_SESSION(NULL, &ptr, SSL_SESSION_MAX_DER);
+ if (pSession == NULL) {
+ ssl_log(s, SSL_LOG_ERROR, "shmcb_remove_session_id, "
+ "internal error");
+ goto end;
+ }
+ if ((pSession->session_id_length == idlen)
+ && (memcmp(id, pSession->session_id, idlen) == 0)) {
+ ssl_log(s, SSL_LOG_TRACE, "a match!");
+ /* Scrub out this session "quietly" */
+ idx->removed = (unsigned char) 1;
+ SSL_SESSION_free(pSession);
+ to_return = TRUE;
+ goto end;
+ }
+ ssl_log(s, SSL_LOG_TRACE, "not a match");
+ SSL_SESSION_free(pSession);
+ pSession = NULL;
+ }
+ curr_pos = shmcb_cyclic_increment(header->index_num, curr_pos, 1);
+ }
+ ssl_log(s, SSL_LOG_TRACE, "no matching sessions were found");
+
+ /* If there's entries to expire, ditch them now. */
+ shmcb_expire_division(s, queue, cache);
+end:
+ ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_remove_session_id");
+ return to_return;
+}
+
diff --git a/modules/ssl/ssl_util.c b/modules/ssl/ssl_util.c
new file mode 100644
index 0000000000..af4a9672f2
--- /dev/null
+++ b/modules/ssl/ssl_util.c
@@ -0,0 +1,437 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_util.c
+** Utility Functions
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+/* ====================================================================
+ * Copyright (c) 1995-1999 Ben Laurie. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by Ben Laurie
+ * for use in the Apache-SSL HTTP server project."
+ *
+ * 4. The name "Apache-SSL Server" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Ben Laurie
+ * for use in the Apache-SSL HTTP server project."
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN LAURIE ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BEN LAURIE OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+ /* ``Every day of my life
+ I am forced to add another
+ name to the list of people
+ who piss me off!''
+ -- Calvin */
+#include "mod_ssl.h"
+
+
+/* _________________________________________________________________
+**
+** Utility Functions
+** _________________________________________________________________
+*/
+
+char *ssl_util_server_root_relative(pool *p, char *what, char *arg)
+{
+ char *rv = NULL;
+
+#ifdef SSL_VENDOR
+ ap_hook_use("ap::mod_ssl::vendor::ssl_server_root_relative",
+ AP_HOOK_SIG4(ptr,ptr,ptr,ptr), AP_HOOK_ALL, &rv, p, what, arg);
+ if (rv != NULL)
+ return rv;
+#endif
+ rv = ap_server_root_relative(p, arg);
+ return rv;
+}
+
+char *ssl_util_vhostid(pool *p, server_rec *s)
+{
+ char *id;
+ SSLSrvConfigRec *sc;
+ char *host;
+ unsigned int port;
+
+ host = s->server_hostname;
+ if (s->port != 0)
+ port = s->port;
+ else {
+ sc = mySrvConfig(s);
+ if (sc->bEnabled)
+ port = DEFAULT_HTTPS_PORT;
+ else
+ port = DEFAULT_HTTP_PORT;
+ }
+ id = ap_psprintf(p, "%s:%u", host, port);
+ return id;
+}
+
+void ssl_util_strupper(char *s)
+{
+ for (; *s; ++s)
+ *s = toupper(*s);
+ return;
+}
+
+static const char ssl_util_uuencode_six2pr[64+1] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+void ssl_util_uuencode(char *szTo, const char *szFrom, BOOL bPad)
+{
+ ssl_util_uuencode_binary((unsigned char *)szTo,
+ (const unsigned char *)szFrom,
+ strlen(szFrom), bPad);
+}
+
+void ssl_util_uuencode_binary(
+ unsigned char *szTo, const unsigned char *szFrom, int nLength, BOOL bPad)
+{
+ const unsigned char *s;
+ int nPad = 0;
+
+ for (s = szFrom; nLength > 0; s += 3) {
+ *szTo++ = ssl_util_uuencode_six2pr[s[0] >> 2];
+ *szTo++ = ssl_util_uuencode_six2pr[(s[0] << 4 | s[1] >> 4) & 0x3f];
+ if (--nLength == 0) {
+ nPad = 2;
+ break;
+ }
+ *szTo++ = ssl_util_uuencode_six2pr[(s[1] << 2 | s[2] >> 6) & 0x3f];
+ if (--nLength == 0) {
+ nPad = 1;
+ break;
+ }
+ *szTo++ = ssl_util_uuencode_six2pr[s[2] & 0x3f];
+ --nLength;
+ }
+ while(bPad && nPad--)
+ *szTo++ = NUL;
+ *szTo = NUL;
+ return;
+}
+
+FILE *ssl_util_ppopen(server_rec *s, pool *p, char *cmd)
+{
+ FILE *fpout;
+ int rc;
+
+ fpout = NULL;
+ rc = ap_spawn_child(p, ssl_util_ppopen_child,
+ (void *)cmd, kill_after_timeout,
+ NULL, &fpout, NULL);
+ if (rc == 0 || fpout == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, s,
+ "ssl_util_ppopen: could not run: %s", cmd);
+ return NULL;
+ }
+ return (fpout);
+}
+
+int ssl_util_ppopen_child(void *cmd, child_info *pinfo)
+{
+ int child_pid = 1;
+
+ /*
+ * Prepare for exec
+ */
+ ap_cleanup_for_exec();
+#ifdef SIGHUP
+ signal(SIGHUP, SIG_IGN);
+#endif
+
+ /*
+ * Exec() the child program
+ */
+#if defined(WIN32)
+ /* MS Windows */
+ {
+ char pCommand[MAX_STRING_LEN];
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ ap_snprintf(pCommand, sizeof(pCommand), "%s /C %s", SHELL_PATH, cmd);
+
+ memset(&si, 0, sizeof(si));
+ memset(&pi, 0, sizeof(pi));
+
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+ si.wShowWindow = SW_HIDE;
+ si.hStdInput = pinfo->hPipeInputRead;
+ si.hStdOutput = pinfo->hPipeOutputWrite;
+ si.hStdError = pinfo->hPipeErrorWrite;
+
+ if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, 0,
+ environ, NULL, &si, &pi)) {
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ child_pid = pi.dwProcessId;
+ }
+ }
+#elif defined(OS2)
+ /* IBM OS/2 */
+ spawnl(P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+#else
+ /* Standard Unix */
+ execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+#endif
+ return (child_pid);
+}
+
+void ssl_util_ppclose(server_rec *s, pool *p, FILE *fp)
+{
+ ap_pfclose(p, fp);
+ return;
+}
+
+/*
+ * Run a filter program and read the first line of its stdout output
+ */
+char *ssl_util_readfilter(server_rec *s, pool *p, char *cmd)
+{
+ static char buf[MAX_STRING_LEN];
+ FILE *fp;
+ char c;
+ int k;
+
+ if ((fp = ssl_util_ppopen(s, p, cmd)) == NULL)
+ return NULL;
+ for (k = 0; read(fileno(fp), &c, 1) == 1
+ && (k < MAX_STRING_LEN-1) ; ) {
+ if (c == '\n' || c == '\r')
+ break;
+ buf[k++] = c;
+ }
+ buf[k] = NUL;
+ ssl_util_ppclose(s, p, fp);
+
+ return buf;
+}
+
+BOOL ssl_util_path_check(ssl_pathcheck_t pcm, char *path)
+{
+ struct stat sb;
+
+ if (path == NULL)
+ return FALSE;
+ if (pcm & SSL_PCM_EXISTS && stat(path, &sb) != 0)
+ return FALSE;
+ if (pcm & SSL_PCM_ISREG && !S_ISREG(sb.st_mode))
+ return FALSE;
+ if (pcm & SSL_PCM_ISDIR && !S_ISDIR(sb.st_mode))
+ return FALSE;
+ if (pcm & SSL_PCM_ISNONZERO && sb.st_mode <= 0)
+ return FALSE;
+ return TRUE;
+}
+
+ssl_algo_t ssl_util_algotypeof(X509 *pCert, EVP_PKEY *pKey)
+{
+ ssl_algo_t t;
+
+ t = SSL_ALGO_UNKNOWN;
+ if (pCert != NULL)
+ pKey = X509_get_pubkey(pCert);
+ if (pKey != NULL) {
+ switch (EVP_PKEY_type(pKey->type)) {
+ case EVP_PKEY_RSA:
+ t = SSL_ALGO_RSA;
+ break;
+ case EVP_PKEY_DSA:
+ t = SSL_ALGO_DSA;
+ break;
+ default:
+ break;
+ }
+ }
+ return t;
+}
+
+char *ssl_util_algotypestr(ssl_algo_t t)
+{
+ char *cp;
+
+ cp = "UNKNOWN";
+ switch (t) {
+ case SSL_ALGO_RSA:
+ cp = "RSA";
+ break;
+ case SSL_ALGO_DSA:
+ cp = "DSA";
+ break;
+ default:
+ break;
+ }
+ return cp;
+}
+
+char *ssl_util_ptxtsub(
+ pool *p, const char *cpLine, const char *cpMatch, char *cpSubst)
+{
+#define MAX_PTXTSUB 100
+ char *cppMatch[MAX_PTXTSUB];
+ char *cpResult;
+ int nResult;
+ int nLine;
+ int nSubst;
+ int nMatch;
+ char *cpI;
+ char *cpO;
+ char *cp;
+ int i;
+
+ /*
+ * Pass 1: find substitution locations and calculate sizes
+ */
+ nLine = strlen(cpLine);
+ nMatch = strlen(cpMatch);
+ nSubst = strlen(cpSubst);
+ for (cpI = (char *)cpLine, i = 0, nResult = 0;
+ cpI < cpLine+nLine && i < MAX_PTXTSUB; ) {
+ if ((cp = strstr(cpI, cpMatch)) != NULL) {
+ cppMatch[i++] = cp;
+ nResult += ((cp-cpI)+nSubst);
+ cpI = (cp+nMatch);
+ }
+ else {
+ nResult += strlen(cpI);
+ break;
+ }
+ }
+ cppMatch[i] = NULL;
+ if (i == 0)
+ return NULL;
+
+ /*
+ * Pass 2: allocate memory and assemble result
+ */
+ cpResult = ap_pcalloc(p, nResult+1);
+ for (cpI = (char *)cpLine, cpO = cpResult, i = 0; cppMatch[i] != NULL; i++) {
+ ap_cpystrn(cpO, cpI, cppMatch[i]-cpI+1);
+ cpO += (cppMatch[i]-cpI);
+ ap_cpystrn(cpO, cpSubst, nSubst+1);
+ cpO += nSubst;
+ cpI = (cppMatch[i]+nMatch);
+ }
+ ap_cpystrn(cpO, cpI, cpResult+nResult-cpO+1);
+
+ return cpResult;
+}
+
+/* _________________________________________________________________
+**
+** Special Functions for Win32/OpenSSL
+** _________________________________________________________________
+*/
+
+#ifdef WIN32
+static HANDLE lock_cs[CRYPTO_NUM_LOCKS];
+
+static void win32_locking_callback(int mode, int type, char* file, int line)
+{
+ if (mode & CRYPTO_LOCK)
+ WaitForSingleObject(lock_cs[type], INFINITE);
+ else
+ ReleaseMutex(lock_cs[type]);
+ return;
+}
+#endif /* WIN32 */
+
+void ssl_util_thread_setup(void)
+{
+#ifdef WIN32
+ int i;
+
+ for (i = 0; i < CRYPTO_NUM_LOCKS; i++)
+ lock_cs[i] = CreateMutex(NULL, FALSE, NULL);
+ CRYPTO_set_locking_callback((void(*)(int, int, const char *, int))
+ win32_locking_callback);
+#endif /* WIN32 */
+ return;
+}
+
diff --git a/modules/ssl/ssl_util_ssl.c b/modules/ssl/ssl_util_ssl.c
new file mode 100644
index 0000000000..19f6bd3629
--- /dev/null
+++ b/modules/ssl/ssl_util_ssl.c
@@ -0,0 +1,544 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_util_ssl.c
+** Additional Utility Functions for OpenSSL
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include "mod_ssl.h"
+
+
+/* _________________________________________________________________
+**
+** Additional High-Level Functions for OpenSSL
+** _________________________________________________________________
+*/
+
+int SSL_get_app_data2_idx(void)
+{
+ static int app_data2_idx = -1;
+
+ if (app_data2_idx < 0) {
+ app_data2_idx = SSL_get_ex_new_index(0,
+ "Second Application Data for SSL", NULL, NULL, NULL);
+ app_data2_idx = SSL_get_ex_new_index(0,
+ "Second Application Data for SSL", NULL, NULL, NULL);
+ }
+ return(app_data2_idx);
+}
+
+void *SSL_get_app_data2(SSL *ssl)
+{
+ return (void *)SSL_get_ex_data(ssl, SSL_get_app_data2_idx());
+}
+
+void SSL_set_app_data2(SSL *ssl, void *arg)
+{
+ SSL_set_ex_data(ssl, SSL_get_app_data2_idx(), (char *)arg);
+ return;
+}
+
+/* _________________________________________________________________
+**
+** High-Level Certificate / Private Key Loading
+** _________________________________________________________________
+*/
+
+X509 *SSL_read_X509(FILE *fp, X509 **x509, int (*cb)())
+{
+ X509 *rc;
+ BIO *bioS;
+ BIO *bioF;
+
+ /* 1. try PEM (= DER+Base64+headers) */
+#if SSL_LIBRARY_VERSION < 0x00904000
+ rc = PEM_read_X509(fp, x509, cb);
+#else
+ rc = PEM_read_X509(fp, x509, cb, NULL);
+#endif
+ if (rc == NULL) {
+ /* 2. try DER+Base64 */
+ fseek(fp, 0L, SEEK_SET);
+ if ((bioS = BIO_new(BIO_s_fd())) == NULL)
+ return NULL;
+ BIO_set_fd(bioS, fileno(fp), BIO_NOCLOSE);
+ if ((bioF = BIO_new(BIO_f_base64())) == NULL) {
+ BIO_free(bioS);
+ return NULL;
+ }
+ bioS = BIO_push(bioF, bioS);
+ rc = d2i_X509_bio(bioS, NULL);
+ BIO_free_all(bioS);
+ if (rc == NULL) {
+ /* 3. try plain DER */
+ fseek(fp, 0L, SEEK_SET);
+ if ((bioS = BIO_new(BIO_s_fd())) == NULL)
+ return NULL;
+ BIO_set_fd(bioS, fileno(fp), BIO_NOCLOSE);
+ rc = d2i_X509_bio(bioS, NULL);
+ BIO_free(bioS);
+ }
+ }
+ if (rc != NULL && x509 != NULL) {
+ if (*x509 != NULL)
+ X509_free(*x509);
+ *x509 = rc;
+ }
+ return rc;
+}
+
+#if SSL_LIBRARY_VERSION <= 0x00904100
+static EVP_PKEY *d2i_PrivateKey_bio(BIO *bio, EVP_PKEY **key)
+{
+ return ((EVP_PKEY *)ASN1_d2i_bio(
+ (char *(*)())EVP_PKEY_new,
+ (char *(*)())d2i_PrivateKey,
+ (bio), (unsigned char **)(key)));
+}
+#endif
+
+EVP_PKEY *SSL_read_PrivateKey(FILE *fp, EVP_PKEY **key, int (*cb)())
+{
+ EVP_PKEY *rc;
+ BIO *bioS;
+ BIO *bioF;
+
+ /* 1. try PEM (= DER+Base64+headers) */
+#if SSL_LIBRARY_VERSION < 0x00904000
+ rc = PEM_read_PrivateKey(fp, key, cb);
+#else
+ rc = PEM_read_PrivateKey(fp, key, cb, NULL);
+#endif
+ if (rc == NULL) {
+ /* 2. try DER+Base64 */
+ fseek(fp, 0L, SEEK_SET);
+ if ((bioS = BIO_new(BIO_s_fd())) == NULL)
+ return NULL;
+ BIO_set_fd(bioS, fileno(fp), BIO_NOCLOSE);
+ if ((bioF = BIO_new(BIO_f_base64())) == NULL) {
+ BIO_free(bioS);
+ return NULL;
+ }
+ bioS = BIO_push(bioF, bioS);
+ rc = d2i_PrivateKey_bio(bioS, NULL);
+ BIO_free_all(bioS);
+ if (rc == NULL) {
+ /* 3. try plain DER */
+ fseek(fp, 0L, SEEK_SET);
+ if ((bioS = BIO_new(BIO_s_fd())) == NULL)
+ return NULL;
+ BIO_set_fd(bioS, fileno(fp), BIO_NOCLOSE);
+ rc = d2i_PrivateKey_bio(bioS, NULL);
+ BIO_free(bioS);
+ }
+ }
+ if (rc != NULL && key != NULL) {
+ if (*key != NULL)
+ EVP_PKEY_free(*key);
+ *key = rc;
+ }
+ return rc;
+}
+
+/* _________________________________________________________________
+**
+** Smart shutdown
+** _________________________________________________________________
+*/
+
+int SSL_smart_shutdown(SSL *ssl)
+{
+ int i;
+ int rc;
+
+ /*
+ * Repeat the calls, because SSL_shutdown internally dispatches through a
+ * little state machine. Usually only one or two interation should be
+ * needed, so we restrict the total number of restrictions in order to
+ * avoid process hangs in case the client played bad with the socket
+ * connection and OpenSSL cannot recognize it.
+ */
+ rc = 0;
+ for (i = 0; i < 4 /* max 2x pending + 2x data = 4 */; i++) {
+ if ((rc = SSL_shutdown(ssl)))
+ break;
+ }
+ return rc;
+}
+
+/* _________________________________________________________________
+**
+** Certificate Revocation List (CRL) Storage
+** _________________________________________________________________
+*/
+
+X509_STORE *SSL_X509_STORE_create(char *cpFile, char *cpPath)
+{
+ X509_STORE *pStore;
+ X509_LOOKUP *pLookup;
+
+ if (cpFile == NULL && cpPath == NULL)
+ return NULL;
+ if ((pStore = X509_STORE_new()) == NULL)
+ return NULL;
+ if (cpFile != NULL) {
+ if ((pLookup = X509_STORE_add_lookup(pStore, X509_LOOKUP_file())) == NULL) {
+ X509_STORE_free(pStore);
+ return NULL;
+ }
+ X509_LOOKUP_load_file(pLookup, cpFile, X509_FILETYPE_PEM);
+ }
+ if (cpPath != NULL) {
+ if ((pLookup = X509_STORE_add_lookup(pStore, X509_LOOKUP_hash_dir())) == NULL) {
+ X509_STORE_free(pStore);
+ return NULL;
+ }
+ X509_LOOKUP_add_dir(pLookup, cpPath, X509_FILETYPE_PEM);
+ }
+ return pStore;
+}
+
+int SSL_X509_STORE_lookup(X509_STORE *pStore, int nType,
+ X509_NAME *pName, X509_OBJECT *pObj)
+{
+ X509_STORE_CTX pStoreCtx;
+ int rc;
+
+ X509_STORE_CTX_init(&pStoreCtx, pStore, NULL, NULL);
+ rc = X509_STORE_get_by_subject(&pStoreCtx, nType, pName, pObj);
+ X509_STORE_CTX_cleanup(&pStoreCtx);
+ return rc;
+}
+
+/* _________________________________________________________________
+**
+** Cipher Suite Spec String Creation
+** _________________________________________________________________
+*/
+
+char *SSL_make_ciphersuite(pool *p, SSL *ssl)
+{
+ STACK_OF(SSL_CIPHER) *sk;
+ SSL_CIPHER *c;
+ int i;
+ int l;
+ char *cpCipherSuite;
+ char *cp;
+
+ if (ssl == NULL)
+ return "";
+ if ((sk = SSL_get_ciphers(ssl)) == NULL)
+ return "";
+ l = 0;
+ for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
+ c = sk_SSL_CIPHER_value(sk, i);
+ l += strlen(c->name)+2+1;
+ }
+ if (l == 0)
+ return "";
+ cpCipherSuite = (char *)ap_palloc(p, l+1);
+ cp = cpCipherSuite;
+ for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
+ c = sk_SSL_CIPHER_value(sk, i);
+ l = strlen(c->name);
+ memcpy(cp, c->name, l);
+ cp += l;
+ *cp++ = '/';
+ *cp++ = (c->valid == 1 ? '1' : '0');
+ *cp++ = ':';
+ }
+ *(cp-1) = NUL;
+ return cpCipherSuite;
+}
+
+/* _________________________________________________________________
+**
+** Certificate Checks
+** _________________________________________________________________
+*/
+
+/* check whether cert contains extended key usage with a SGC tag */
+BOOL SSL_X509_isSGC(X509 *cert)
+{
+ X509_EXTENSION *ext;
+ int ext_nid;
+ STACK *sk;
+ BOOL is_sgc;
+ int idx;
+ int i;
+
+ is_sgc = FALSE;
+ idx = X509_get_ext_by_NID(cert, NID_ext_key_usage, -1);
+ if (idx >= 0) {
+ ext = X509_get_ext(cert, idx);
+ if ((sk = (STACK *)X509V3_EXT_d2i(ext)) != NULL) {
+ for (i = 0; i < sk_num(sk); i++) {
+ ext_nid = OBJ_obj2nid((ASN1_OBJECT *)sk_value(sk, i));
+ if (ext_nid == NID_ms_sgc || ext_nid == NID_ns_sgc) {
+ is_sgc = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ return is_sgc;
+}
+
+/* retrieve basic constraints ingredients */
+BOOL SSL_X509_getBC(X509 *cert, int *ca, int *pathlen)
+{
+ X509_EXTENSION *ext;
+ BASIC_CONSTRAINTS *bc;
+ int idx;
+ BIGNUM *bn = NULL;
+ char *cp;
+
+ if ((idx = X509_get_ext_by_NID(cert, NID_basic_constraints, -1)) < 0)
+ return FALSE;
+ ext = X509_get_ext(cert, idx);
+ if (ext == NULL)
+ return FALSE;
+ if ((bc = (BASIC_CONSTRAINTS *)X509V3_EXT_d2i(ext)) == NULL)
+ return FALSE;
+ *ca = bc->ca;
+ *pathlen = -1 /* unlimited */;
+ if (bc->pathlen != NULL) {
+ if ((bn = ASN1_INTEGER_to_BN(bc->pathlen, NULL)) == NULL)
+ return FALSE;
+ if ((cp = BN_bn2dec(bn)) == NULL)
+ return FALSE;
+ *pathlen = atoi(cp);
+ free(cp);
+ BN_free(bn);
+ }
+ BASIC_CONSTRAINTS_free(bc);
+ return TRUE;
+}
+
+/* retrieve subject CommonName of certificate */
+BOOL SSL_X509_getCN(pool *p, X509 *xs, char **cppCN)
+{
+ X509_NAME *xsn;
+ X509_NAME_ENTRY *xsne;
+ int i, nid;
+
+ xsn = X509_get_subject_name(xs);
+ for (i = 0; i < sk_X509_NAME_ENTRY_num(xsn->entries); i++) {
+ xsne = sk_X509_NAME_ENTRY_value(xsn->entries, i);
+ nid = OBJ_obj2nid(xsne->object);
+ if (nid == NID_commonName) {
+ *cppCN = ap_palloc(p, xsne->value->length+1);
+ ap_cpystrn(*cppCN, (char *)xsne->value->data, xsne->value->length+1);
+ (*cppCN)[xsne->value->length] = NUL;
+#ifdef CHARSET_EBCDIC
+ ascii2ebcdic(*cppCN, *cppCN, strlen(*cppCN));
+#endif
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/* _________________________________________________________________
+**
+** Low-Level CA Certificate Loading
+** _________________________________________________________________
+*/
+
+#ifdef SSL_EXPERIMENTAL_PROXY
+
+BOOL SSL_load_CrtAndKeyInfo_file(pool *p, STACK_OF(X509_INFO) *sk, char *filename)
+{
+ BIO *in;
+
+ if ((in = BIO_new(BIO_s_file())) == NULL)
+ return FALSE;
+ if (BIO_read_filename(in, filename) <= 0) {
+ BIO_free(in);
+ return FALSE;
+ }
+ ERR_clear_error();
+#if SSL_LIBRARY_VERSION < 0x00904000
+ PEM_X509_INFO_read_bio(in, sk, NULL);
+#else
+ PEM_X509_INFO_read_bio(in, sk, NULL, NULL);
+#endif
+ BIO_free(in);
+ return TRUE;
+}
+
+BOOL SSL_load_CrtAndKeyInfo_path(pool *p, STACK_OF(X509_INFO) *sk, char *pathname)
+{
+ struct stat st;
+ DIR *dir;
+ pool *sp;
+ struct dirent *nextent;
+ char *fullname;
+ BOOL ok;
+
+ sp = ap_make_sub_pool(p);
+ if ((dir = ap_popendir(sp, pathname)) == NULL) {
+ ap_destroy_pool(sp);
+ return FALSE;
+ }
+ ok = FALSE;
+ while ((nextent = readdir(dir)) != NULL) {
+ fullname = ap_pstrcat(sp, pathname, "/", nextent->d_name, NULL);
+ if (stat(fullname, &st) != 0)
+ continue;
+ if (!S_ISREG(st.st_mode))
+ continue;
+ if (SSL_load_CrtAndKeyInfo_file(sp, sk, fullname))
+ ok = TRUE;
+ }
+ ap_pclosedir(p, dir);
+ ap_destroy_pool(sp);
+ return ok;
+}
+
+#endif /* SSL_EXPERIMENTAL_PROXY */
+
+/* _________________________________________________________________
+**
+** Extra Server Certificate Chain Support
+** _________________________________________________________________
+*/
+
+/*
+ * Read a file that optionally contains the server certificate in PEM
+ * format, possibly followed by a sequence of CA certificates that
+ * should be sent to the peer in the SSL Certificate message.
+ */
+int SSL_CTX_use_certificate_chain(
+ SSL_CTX *ctx, char *file, int skipfirst, int (*cb)())
+{
+ BIO *bio;
+ X509 *x509;
+ unsigned long err;
+ int n;
+
+ if ((bio = BIO_new(BIO_s_file_internal())) == NULL)
+ return -1;
+ if (BIO_read_filename(bio, file) <= 0) {
+ BIO_free(bio);
+ return -1;
+ }
+ /* optionally skip a leading server certificate */
+ if (skipfirst) {
+#if SSL_LIBRARY_VERSION < 0x00904000
+ if ((x509 = PEM_read_bio_X509(bio, NULL, cb)) == NULL) {
+#else
+ if ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) == NULL) {
+#endif
+ BIO_free(bio);
+ return -1;
+ }
+ X509_free(x509);
+ }
+ /* free a perhaps already configured extra chain */
+ if (ctx->extra_certs != NULL) {
+ sk_X509_pop_free(ctx->extra_certs, X509_free);
+ ctx->extra_certs = NULL;
+ }
+ /* create new extra chain by loading the certs */
+ n = 0;
+#if SSL_LIBRARY_VERSION < 0x00904000
+ while ((x509 = PEM_read_bio_X509(bio, NULL, cb)) != NULL) {
+#else
+ while ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) != NULL) {
+#endif
+ if (!SSL_CTX_add_extra_chain_cert(ctx, x509)) {
+ X509_free(x509);
+ BIO_free(bio);
+ return -1;
+ }
+ n++;
+ }
+ /* Make sure that only the error is just an EOF */
+ if ((err = ERR_peek_error()) > 0) {
+ if (!( ERR_GET_LIB(err) == ERR_LIB_PEM
+ && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
+ BIO_free(bio);
+ return -1;
+ }
+ while (ERR_get_error() > 0) ;
+ }
+ BIO_free(bio);
+ return n;
+}
+
+/* _________________________________________________________________
+**
+** Session Stuff
+** _________________________________________________________________
+*/
+
+char *SSL_SESSION_id2sz(unsigned char *id, int idlen)
+{
+ static char str[(SSL_MAX_SSL_SESSION_ID_LENGTH+1)*2];
+ char *cp;
+ int n;
+
+ cp = str;
+ for (n = 0; n < idlen && n < SSL_MAX_SSL_SESSION_ID_LENGTH; n++) {
+ ap_snprintf(cp, sizeof(str)-(cp-str), "%02X", id[n]);
+ cp += 2;
+ }
+ *cp = NUL;
+ return str;
+}
+
diff --git a/modules/ssl/ssl_util_ssl.h b/modules/ssl/ssl_util_ssl.h
new file mode 100644
index 0000000000..23aaaaa40d
--- /dev/null
+++ b/modules/ssl/ssl_util_ssl.h
@@ -0,0 +1,115 @@
+/* _ _
+** _ __ ___ ___ __| | ___ ___| | mod_ssl
+** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
+** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
+** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
+** |_____|
+** ssl_util_ssl.h
+** Additional Utility Functions for OpenSSL
+*/
+
+/* ====================================================================
+ * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * 4. The names "mod_ssl" must not be used to endorse or promote
+ * products derived from this software without prior written
+ * permission. For written permission, please contact
+ * rse@engelschall.com.
+ *
+ * 5. Products derived from this software may not be called "mod_ssl"
+ * nor may "mod_ssl" appear in their names without prior
+ * written permission of Ralf S. Engelschall.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by
+ * Ralf S. Engelschall <rse@engelschall.com> for use in the
+ * mod_ssl project (http://www.modssl.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
+ * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#ifndef SSL_UTIL_SSL_H
+#define SSL_UTIL_SSL_H
+
+/*
+ * Determine SSL library version number
+ */
+#ifdef OPENSSL_VERSION_NUMBER
+#define SSL_LIBRARY_VERSION OPENSSL_VERSION_NUMBER
+#define SSL_LIBRARY_NAME "OpenSSL"
+#define SSL_LIBRARY_TEXT OPENSSL_VERSION_TEXT
+#else
+#define SSL_LIBRARY_VERSION 0x0000
+#define SSL_LIBRARY_NAME "OtherSSL"
+#define SSL_LIBRARY_TEXT "OtherSSL 0.0.0 00 XXX 0000"
+#endif
+
+/*
+ * Support for retrieving/overriding states
+ */
+#ifndef SSL_get_state
+#define SSL_get_state(ssl) SSL_state(ssl)
+#endif
+#define SSL_set_state(ssl,val) (ssl)->state = val
+
+/*
+ * Maximum length of a DER encoded session.
+ * FIXME: There is no define in OpenSSL, but OpenSSL uses 1024*10,
+ * so this value should be ok. Although we have no warm feeling.
+ */
+#define SSL_SESSION_MAX_DER 1024*10
+
+/*
+ * Additional Functions
+ */
+int SSL_get_app_data2_idx(void);
+void *SSL_get_app_data2(SSL *);
+void SSL_set_app_data2(SSL *, void *);
+X509 *SSL_read_X509(FILE *, X509 **, int (*)());
+EVP_PKEY *SSL_read_PrivateKey(FILE *, EVP_PKEY **, int (*)());
+int SSL_smart_shutdown(SSL *ssl);
+X509_STORE *SSL_X509_STORE_create(char *, char *);
+int SSL_X509_STORE_lookup(X509_STORE *, int, X509_NAME *, X509_OBJECT *);
+char *SSL_make_ciphersuite(pool *, SSL *);
+BOOL SSL_X509_isSGC(X509 *);
+BOOL SSL_X509_getBC(X509 *, int *, int *);
+BOOL SSL_X509_getCN(pool *, X509 *, char **);
+#ifdef SSL_EXPERIMENTAL_PROXY
+BOOL SSL_load_CrtAndKeyInfo_file(pool *, STACK_OF(X509_INFO) *, char *);
+BOOL SSL_load_CrtAndKeyInfo_path(pool *, STACK_OF(X509_INFO) *, char *);
+#endif /* SSL_EXPERIMENTAL_PROXY */
+int SSL_CTX_use_certificate_chain(SSL_CTX *, char *, int, int (*)());
+char *SSL_SESSION_id2sz(unsigned char *, int);
+
+#endif /* SSL_UTIL_SSL_H */
diff --git a/modules/test/mod_optional_fn_export.c b/modules/test/mod_optional_fn_export.c
new file mode 100644
index 0000000000..b214a36621
--- /dev/null
+++ b/modules/test/mod_optional_fn_export.c
@@ -0,0 +1,86 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "mod_optional_fn_export.h"
+
+/* The alert will note a strange mirror-image style resemblance to
+ * mod_generic_hook_import.c. Yes, I _did_ mean import. Think about it.
+ */
+
+static int TestOptionalFn(const char *szStr)
+{
+ ap_log_error(APLOG_MARK,APLOG_ERR,OK,NULL,
+ "Optional function test said: %s",szStr);
+
+ return OK;
+}
+
+static void ExportRegisterHooks(apr_pool_t *p)
+{
+ APR_REGISTER_OPTIONAL_FN(TestOptionalFn);
+}
+
+module optional_fn_export_module=
+{
+ STANDARD20_MODULE_STUFF,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ExportRegisterHooks
+};
diff --git a/modules/test/mod_optional_fn_export.h b/modules/test/mod_optional_fn_export.h
new file mode 100644
index 0000000000..800ccab4f6
--- /dev/null
+++ b/modules/test/mod_optional_fn_export.h
@@ -0,0 +1,3 @@
+#include "apr_optional.h"
+
+APR_DECLARE_OPTIONAL_FN(int,TestOptionalFn,(const char *))
diff --git a/modules/test/mod_optional_fn_import.c b/modules/test/mod_optional_fn_import.c
new file mode 100644
index 0000000000..2ad313ef9a
--- /dev/null
+++ b/modules/test/mod_optional_fn_import.c
@@ -0,0 +1,93 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "mod_optional_fn_export.h"
+#include "http_protocol.h"
+
+/* The alert will note a strange mirror-image style resemblance to
+ * mod_generic_hook_export.c. Yes, I _did_ mean export. Think about it.
+ */
+
+static APR_OPTIONAL_FN_TYPE(TestOptionalFn) *pfn;
+
+static int ImportLogTransaction(request_rec *r)
+{
+ if(pfn)
+ return pfn(r->the_request);
+ return DECLINED;
+}
+
+void ImportFnRetrieve(void)
+{
+ pfn=APR_RETRIEVE_OPTIONAL_FN(TestOptionalFn);
+}
+
+static void ImportRegisterHooks(apr_pool_t *p)
+{
+ ap_hook_log_transaction(ImportLogTransaction,NULL,NULL,APR_HOOK_MIDDLE);
+ ap_hook_optional_fn_retrieve(ImportFnRetrieve,NULL,NULL,APR_HOOK_MIDDLE);
+}
+
+module optional_fn_import_module =
+{
+ STANDARD20_MODULE_STUFF,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ImportRegisterHooks
+};
diff --git a/os/beos/.cvsignore b/os/beos/.cvsignore
new file mode 100644
index 0000000000..bbc8ba00d1
--- /dev/null
+++ b/os/beos/.cvsignore
@@ -0,0 +1,5 @@
+Makefile
+.deps
+.libs
+*.lo
+*.la
diff --git a/os/os2/core.mk b/os/os2/core.mk
new file mode 100644
index 0000000000..aac4356532
--- /dev/null
+++ b/os/os2/core.mk
@@ -0,0 +1,7 @@
+# Some rules for making a shared core dll on OS/2
+
+os2core: httpd.dll $(CORE_IMPLIB)
+ $(LIBTOOL) --mode=link gcc $(EXTRA_LDFLAGS) -o httpd $(CORE_IMPLIB)
+
+httpd.dll: $(PROGRAM_DEPENDENCIES) $(CORE_IMPLIB)
+ $(LINK) -Zdll $(EXTRA_LDFLAGS) -s -o $@ server/exports.lo modules.lo $(PROGRAM_DEPENDENCIES) $(EXTRA_LIBS) server/ApacheCoreOS2.def
diff --git a/os/os2/core_header.def b/os/os2/core_header.def
new file mode 100644
index 0000000000..85e02fbd85
--- /dev/null
+++ b/os/os2/core_header.def
@@ -0,0 +1,11 @@
+LIBRARY httpd INITINSTANCE
+DESCRIPTION "Apache Server Core"
+EXPORTS
+ "main"
+ "ap_my_generation"
+ "ap_restart_time"
+ "ap_extended_status"
+ "ap_scoreboard_image"
+ "ap_conftree"
+ "ap_server_root"
+ "ap_top_module"
diff --git a/os/tpf/.cvsignore b/os/tpf/.cvsignore
new file mode 100644
index 0000000000..bd5fe06963
--- /dev/null
+++ b/os/tpf/.cvsignore
@@ -0,0 +1,2 @@
+Makefile
+.deps
diff --git a/server/core.c b/server/core.c
new file mode 100644
index 0000000000..070c4d1e09
--- /dev/null
+++ b/server/core.c
@@ -0,0 +1,3267 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_lib.h"
+#include "apr_fnmatch.h"
+#include "apr_thread_proc.h" /* for RLIMIT stuff */
+
+#define APR_WANT_IOVEC
+#define APR_WANT_STRFUNC
+#define APR_WANT_MEMFUNC
+#include "apr_want.h"
+
+#define CORE_PRIVATE
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h" /* For index_of_response(). Grump. */
+#include "http_request.h"
+#include "http_vhost.h"
+#include "http_main.h" /* For the default_handler below... */
+#include "http_log.h"
+#include "rfc1413.h"
+#include "util_md5.h"
+#include "http_connection.h"
+#include "apr_buckets.h"
+#include "util_filter.h"
+#include "util_ebcdic.h"
+#include "mpm.h"
+
+#include "mod_core.h"
+
+
+/* LimitXMLRequestBody handling */
+#define AP_LIMIT_UNSET ((long) -1)
+#define AP_DEFAULT_LIMIT_XML_BODY ((size_t)1000000)
+
+#define AP_MIN_SENDFILE_BYTES (256)
+
+/* Server core module... This module provides support for really basic
+ * server operations, including options and commands which control the
+ * operation of other modules. Consider this the bureaucracy module.
+ *
+ * The core module also defines handlers, etc., do handle just enough
+ * to allow a server with the core module ONLY to actually serve documents
+ * (though it slaps DefaultType on all of 'em); this was useful in testing,
+ * but may not be worth preserving.
+ *
+ * This file could almost be mod_core.c, except for the stuff which affects
+ * the http_conf_globals.
+ */
+
+static void *create_core_dir_config(apr_pool_t *a, char *dir)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)apr_pcalloc(a, sizeof(core_dir_config));
+ if (!dir || dir[strlen(dir) - 1] == '/') {
+ conf->d = dir;
+ }
+ else if (strncmp(dir, "proxy:", 6) == 0) {
+ conf->d = apr_pstrdup(a, dir);
+ }
+ else {
+ conf->d = apr_pstrcat(a, dir, "/", NULL);
+ }
+ conf->d_is_fnmatch = conf->d ? (apr_is_fnmatch(conf->d) != 0) : 0;
+ conf->d_components = conf->d ? ap_count_dirs(conf->d) : 0;
+
+ conf->opts = dir ? OPT_UNSET : OPT_UNSET|OPT_ALL;
+ conf->opts_add = conf->opts_remove = OPT_NONE;
+ conf->override = dir ? OR_UNSET : OR_UNSET|OR_ALL;
+
+ conf->content_md5 = 2;
+
+ conf->use_canonical_name = USE_CANONICAL_NAME_UNSET;
+
+ conf->hostname_lookups = HOSTNAME_LOOKUP_UNSET;
+ conf->do_rfc1413 = DEFAULT_RFC1413 | 2; /* set bit 1 to indicate default */
+ conf->satisfy = SATISFY_NOSPEC;
+
+#ifdef RLIMIT_CPU
+ conf->limit_cpu = NULL;
+#endif
+#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
+ conf->limit_mem = NULL;
+#endif
+#ifdef RLIMIT_NPROC
+ conf->limit_nproc = NULL;
+#endif
+
+ conf->limit_req_body = 0;
+ conf->limit_xml_body = AP_LIMIT_UNSET;
+ conf->sec = apr_array_make(a, 2, sizeof(ap_conf_vector_t *));
+#ifdef WIN32
+ conf->script_interpreter_source = INTERPRETER_SOURCE_UNSET;
+#endif
+
+ conf->server_signature = srv_sig_unset;
+
+ conf->add_default_charset = ADD_DEFAULT_CHARSET_UNSET;
+ conf->add_default_charset_name = DEFAULT_ADD_DEFAULT_CHARSET_NAME;
+
+ conf->output_filters = apr_array_make(a, 2, sizeof(void *));
+ conf->input_filters = apr_array_make(a, 2, sizeof(void *));
+ return (void *)conf;
+}
+
+static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv)
+{
+ core_dir_config *base = (core_dir_config *)basev;
+ core_dir_config *new = (core_dir_config *)newv;
+ core_dir_config *conf;
+ int i;
+
+ conf = (core_dir_config *)apr_palloc(a, sizeof(core_dir_config));
+ memcpy((char *)conf, (const char *)base, sizeof(core_dir_config));
+ if (base->response_code_strings) {
+ conf->response_code_strings =
+ apr_palloc(a, sizeof(*conf->response_code_strings)
+ * RESPONSE_CODES);
+ memcpy(conf->response_code_strings, base->response_code_strings,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES);
+ }
+
+ conf->d = new->d;
+ conf->d_is_fnmatch = new->d_is_fnmatch;
+ conf->d_components = new->d_components;
+ conf->r = new->r;
+
+ if (new->opts & OPT_UNSET) {
+ /* there was no explicit setting of new->opts, so we merge
+ * preserve the invariant (opts_add & opts_remove) == 0
+ */
+ conf->opts_add = (conf->opts_add & ~new->opts_remove) | new->opts_add;
+ conf->opts_remove = (conf->opts_remove & ~new->opts_add)
+ | new->opts_remove;
+ conf->opts = (conf->opts & ~conf->opts_remove) | conf->opts_add;
+ if ((base->opts & OPT_INCNOEXEC) && (new->opts & OPT_INCLUDES)) {
+ conf->opts = (conf->opts & ~OPT_INCNOEXEC) | OPT_INCLUDES;
+ }
+ }
+ else {
+ /* otherwise we just copy, because an explicit opts setting
+ * overrides all earlier +/- modifiers
+ */
+ conf->opts = new->opts;
+ conf->opts_add = new->opts_add;
+ conf->opts_remove = new->opts_remove;
+ }
+
+ if (!(new->override & OR_UNSET)) {
+ conf->override = new->override;
+ }
+ if (new->ap_default_type) {
+ conf->ap_default_type = new->ap_default_type;
+ }
+
+ if (new->ap_auth_type) {
+ conf->ap_auth_type = new->ap_auth_type;
+ }
+ if (new->ap_auth_name) {
+ conf->ap_auth_name = new->ap_auth_name;
+ }
+ if (new->ap_requires) {
+ conf->ap_requires = new->ap_requires;
+ }
+
+ if (new->response_code_strings) {
+ if (conf->response_code_strings == NULL) {
+ conf->response_code_strings = apr_palloc(a,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES);
+ memcpy(conf->response_code_strings, new->response_code_strings,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES);
+ }
+ else {
+ for (i = 0; i < RESPONSE_CODES; ++i) {
+ if (new->response_code_strings[i] != NULL) {
+ conf->response_code_strings[i]
+ = new->response_code_strings[i];
+ }
+ }
+ }
+ }
+ if (new->hostname_lookups != HOSTNAME_LOOKUP_UNSET) {
+ conf->hostname_lookups = new->hostname_lookups;
+ }
+ if ((new->do_rfc1413 & 2) == 0) {
+ conf->do_rfc1413 = new->do_rfc1413;
+ }
+ if ((new->content_md5 & 2) == 0) {
+ conf->content_md5 = new->content_md5;
+ }
+ if (new->use_canonical_name != USE_CANONICAL_NAME_UNSET) {
+ conf->use_canonical_name = new->use_canonical_name;
+ }
+
+#ifdef RLIMIT_CPU
+ if (new->limit_cpu) {
+ conf->limit_cpu = new->limit_cpu;
+ }
+#endif
+#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
+ if (new->limit_mem) {
+ conf->limit_mem = new->limit_mem;
+ }
+#endif
+#ifdef RLIMIT_NPROC
+ if (new->limit_nproc) {
+ conf->limit_nproc = new->limit_nproc;
+ }
+#endif
+
+ if (new->limit_req_body) {
+ conf->limit_req_body = new->limit_req_body;
+ }
+
+ if (new->limit_xml_body != AP_LIMIT_UNSET)
+ conf->limit_xml_body = new->limit_xml_body;
+ else
+ conf->limit_xml_body = base->limit_xml_body;
+
+ conf->sec = apr_array_append(a, base->sec, new->sec);
+
+ if (new->satisfy != SATISFY_NOSPEC) {
+ conf->satisfy = new->satisfy;
+ }
+
+#ifdef WIN32
+ if (new->script_interpreter_source != INTERPRETER_SOURCE_UNSET) {
+ conf->script_interpreter_source = new->script_interpreter_source;
+ }
+#endif
+
+ if (new->server_signature != srv_sig_unset) {
+ conf->server_signature = new->server_signature;
+ }
+
+ if (new->add_default_charset != ADD_DEFAULT_CHARSET_UNSET) {
+ conf->add_default_charset = new->add_default_charset;
+ if (new->add_default_charset_name) {
+ conf->add_default_charset_name = new->add_default_charset_name;
+ }
+ }
+ conf->output_filters = apr_array_append(a, base->output_filters,
+ new->output_filters);
+ conf->input_filters = apr_array_append(a, base->input_filters,
+ new->input_filters);
+
+ return (void*)conf;
+}
+
+static void *create_core_server_config(apr_pool_t *a, server_rec *s)
+{
+ core_server_config *conf;
+ int is_virtual = s->is_virtual;
+
+ conf = (core_server_config *)apr_pcalloc(a, sizeof(core_server_config));
+#ifdef GPROF
+ conf->gprof_dir = NULL;
+#endif
+ conf->access_name = is_virtual ? NULL : DEFAULT_ACCESS_FNAME;
+ conf->ap_document_root = is_virtual ? NULL : DOCUMENT_LOCATION;
+ conf->sec = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
+ conf->sec_url = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
+
+ return (void *)conf;
+}
+
+static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
+{
+ core_server_config *base = (core_server_config *)basev;
+ core_server_config *virt = (core_server_config *)virtv;
+ core_server_config *conf;
+
+ conf = (core_server_config *)apr_pcalloc(p, sizeof(core_server_config));
+ *conf = *virt;
+ if (!conf->access_name) {
+ conf->access_name = base->access_name;
+ }
+ if (!conf->ap_document_root) {
+ conf->ap_document_root = base->ap_document_root;
+ }
+ conf->sec = apr_array_append(p, base->sec, virt->sec);
+ conf->sec_url = apr_array_append(p, base->sec_url, virt->sec_url);
+
+ return conf;
+}
+
+/* Add per-directory configuration entry (for <directory> section);
+ * these are part of the core server config.
+ */
+
+AP_CORE_DECLARE(void) ap_add_per_dir_conf(server_rec *s, void *dir_config)
+{
+ core_server_config *sconf = ap_get_module_config(s->module_config,
+ &core_module);
+ void **new_space = (void **)apr_array_push(sconf->sec);
+
+ *new_space = dir_config;
+}
+
+AP_CORE_DECLARE(void) ap_add_per_url_conf(server_rec *s, void *url_config)
+{
+ core_server_config *sconf = ap_get_module_config(s->module_config,
+ &core_module);
+ void **new_space = (void **)apr_array_push(sconf->sec_url);
+
+ *new_space = url_config;
+}
+
+AP_CORE_DECLARE(void) ap_add_file_conf(core_dir_config *conf, void *url_config)
+{
+ void **new_space = (void **)apr_array_push(conf->sec);
+
+ *new_space = url_config;
+}
+
+/* core_reorder_directories reorders the directory sections such that the
+ * 1-component sections come first, then the 2-component, and so on, finally
+ * followed by the "special" sections. A section is "special" if it's a regex,
+ * or if it doesn't start with / -- consider proxy: matching. All movements
+ * are in-order to preserve the ordering of the sections from the config files.
+ * See directory_walk().
+ */
+
+#if defined(HAVE_DRIVE_LETTERS)
+#define IS_SPECIAL(entry_core) \
+ ((entry_core)->r != NULL \
+ || ((entry_core)->d[0] != '/' && (entry_core)->d[1] != ':'))
+#elif defined(NETWARE)
+/* XXX: Fairly certain this is correct... '/' must prefix the path
+ * or else in the case xyz:/ or abc/xyz:/, '/' must follow the ':'.
+ * If there is no leading '/' or embedded ':/', then we are special.
+ */
+#define IS_SPECIAL(entry_core) \
+ ((entry_core)->r != NULL \
+ || ((entry_core)->d[0] != '/' \
+ && strchr((entry_core)->d, ':') \
+ && *(strchr((entry_core)->d, ':') + 1) != '/'))
+#else
+#define IS_SPECIAL(entry_core) \
+ ((entry_core)->r != NULL || (entry_core)->d[0] != '/')
+#endif
+
+/* We need to do a stable sort, qsort isn't stable. So to make it stable
+ * we'll be maintaining the original index into the list, and using it
+ * as the minor key during sorting. The major key is the number of
+ * components (where a "special" section has infinite components).
+ */
+struct reorder_sort_rec {
+ ap_conf_vector_t *elt;
+ int orig_index;
+};
+
+static int reorder_sorter(const void *va, const void *vb)
+{
+ const struct reorder_sort_rec *a = va;
+ const struct reorder_sort_rec *b = vb;
+ core_dir_config *core_a;
+ core_dir_config *core_b;
+
+ core_a = ap_get_module_config(a->elt, &core_module);
+ core_b = ap_get_module_config(b->elt, &core_module);
+ if (IS_SPECIAL(core_a)) {
+ if (!IS_SPECIAL(core_b)) {
+ return 1;
+ }
+ }
+ else if (IS_SPECIAL(core_b)) {
+ return -1;
+ }
+ else {
+ /* we know they're both not special */
+ if (core_a->d_components < core_b->d_components) {
+ return -1;
+ }
+ else if (core_a->d_components > core_b->d_components) {
+ return 1;
+ }
+ }
+ /* Either they're both special, or they're both not special and have the
+ * same number of components. In any event, we now have to compare
+ * the minor key. */
+ return a->orig_index - b->orig_index;
+}
+
+void ap_core_reorder_directories(apr_pool_t *p, server_rec *s)
+{
+ core_server_config *sconf;
+ apr_array_header_t *sec;
+ struct reorder_sort_rec *sortbin;
+ int nelts;
+ ap_conf_vector_t **elts;
+ int i;
+ apr_pool_t *tmp;
+
+ sconf = ap_get_module_config(s->module_config, &core_module);
+ sec = sconf->sec;
+ nelts = sec->nelts;
+ elts = (ap_conf_vector_t **)sec->elts;
+
+ /* we have to allocate tmp space to do a stable sort */
+ apr_pool_create(&tmp, p);
+ sortbin = apr_palloc(tmp, sec->nelts * sizeof(*sortbin));
+ for (i = 0; i < nelts; ++i) {
+ sortbin[i].orig_index = i;
+ sortbin[i].elt = elts[i];
+ }
+
+ qsort(sortbin, nelts, sizeof(*sortbin), reorder_sorter);
+
+ /* and now copy back to the original array */
+ for (i = 0; i < nelts; ++i) {
+ elts[i] = sortbin[i].elt;
+ }
+
+ apr_pool_destroy(tmp);
+}
+
+/*****************************************************************
+ *
+ * There are some elements of the core config structures in which
+ * other modules have a legitimate interest (this is ugly, but necessary
+ * to preserve NCSA back-compatibility). So, we have a bunch of accessors
+ * here...
+ */
+
+AP_DECLARE(int) ap_allow_options(request_rec *r)
+{
+ core_dir_config *conf =
+ (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+
+ return conf->opts;
+}
+
+AP_DECLARE(int) ap_allow_overrides(request_rec *r)
+{
+ core_dir_config *conf;
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ return conf->override;
+}
+
+AP_DECLARE(const char *) ap_auth_type(request_rec *r)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+ return conf->ap_auth_type;
+}
+
+AP_DECLARE(const char *) ap_auth_name(request_rec *r)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+ return conf->ap_auth_name;
+}
+
+AP_DECLARE(const char *) ap_default_type(request_rec *r)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+ return conf->ap_default_type
+ ? conf->ap_default_type
+ : DEFAULT_CONTENT_TYPE;
+}
+
+AP_DECLARE(const char *) ap_document_root(request_rec *r) /* Don't use this! */
+{
+ core_server_config *conf;
+
+ conf = (core_server_config *)ap_get_module_config(r->server->module_config,
+ &core_module);
+ return conf->ap_document_root;
+}
+
+AP_DECLARE(const apr_array_header_t *) ap_requires(request_rec *r)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+ return conf->ap_requires;
+}
+
+AP_DECLARE(int) ap_satisfies(request_rec *r)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ return conf->satisfy;
+}
+
+/* Should probably just get rid of this... the only code that cares is
+ * part of the core anyway (and in fact, it isn't publicised to other
+ * modules).
+ */
+
+char *ap_response_code_string(request_rec *r, int error_index)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ if (conf->response_code_strings == NULL) {
+ return NULL;
+ }
+ return conf->response_code_strings[error_index];
+}
+
+
+/* Code from Harald Hanche-Olsen <hanche@imf.unit.no> */
+static APR_INLINE void do_double_reverse (conn_rec *conn)
+{
+ apr_sockaddr_t *sa;
+ apr_status_t rv;
+
+ if (conn->double_reverse) {
+ /* already done */
+ return;
+ }
+ if (conn->remote_host == NULL || conn->remote_host[0] == '\0') {
+ /* single reverse failed, so don't bother */
+ conn->double_reverse = -1;
+ return;
+ }
+ rv = apr_sockaddr_info_get(&sa, conn->remote_host, APR_UNSPEC, 0, 0, conn->pool);
+ if (rv == APR_SUCCESS) {
+ while (sa) {
+ if (sa->ipaddr_len == conn->remote_addr->ipaddr_len &&
+ !memcmp(sa->ipaddr_ptr, conn->remote_addr->ipaddr_ptr,
+ sa->ipaddr_len)) {
+ conn->double_reverse = 1;
+ return;
+ }
+ sa = sa->next;
+ }
+ }
+ conn->double_reverse = -1;
+}
+
+AP_DECLARE(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config,
+ int type)
+{
+ int hostname_lookups;
+
+ /* If we haven't checked the host name, and we want to */
+ if (dir_config) {
+ hostname_lookups =
+ ((core_dir_config *)ap_get_module_config(dir_config, &core_module))
+ ->hostname_lookups;
+ if (hostname_lookups == HOSTNAME_LOOKUP_UNSET) {
+ hostname_lookups = HOSTNAME_LOOKUP_OFF;
+ }
+ }
+ else {
+ /* the default */
+ hostname_lookups = HOSTNAME_LOOKUP_OFF;
+ }
+
+ if (type != REMOTE_NOLOOKUP
+ && conn->remote_host == NULL
+ && (type == REMOTE_DOUBLE_REV
+ || hostname_lookups != HOSTNAME_LOOKUP_OFF)) {
+ apr_sockaddr_t *remote_addr;
+
+ apr_socket_addr_get(&remote_addr, APR_REMOTE, conn->client_socket);
+ if (apr_getnameinfo(&conn->remote_host, remote_addr, 0) == APR_SUCCESS) {
+ ap_str_tolower(conn->remote_host);
+
+ if (hostname_lookups == HOSTNAME_LOOKUP_DOUBLE) {
+ do_double_reverse(conn);
+ if (conn->double_reverse != 1) {
+ conn->remote_host = NULL;
+ }
+ }
+ }
+ /* if failed, set it to the NULL string to indicate error */
+ if (conn->remote_host == NULL) {
+ conn->remote_host = "";
+ }
+ }
+ if (type == REMOTE_DOUBLE_REV) {
+ do_double_reverse(conn);
+ if (conn->double_reverse == -1) {
+ return NULL;
+ }
+ }
+
+/*
+ * Return the desired information; either the remote DNS name, if found,
+ * or either NULL (if the hostname was requested) or the IP address
+ * (if any identifier was requested).
+ */
+ if (conn->remote_host != NULL && conn->remote_host[0] != '\0') {
+ return conn->remote_host;
+ }
+ else {
+ if (type == REMOTE_HOST || type == REMOTE_DOUBLE_REV) {
+ return NULL;
+ }
+ else {
+ return conn->remote_ip;
+ }
+ }
+}
+
+AP_DECLARE(const char *) ap_get_remote_logname(request_rec *r)
+{
+ core_dir_config *dir_conf;
+
+ if (r->connection->remote_logname != NULL) {
+ return r->connection->remote_logname;
+ }
+
+/* If we haven't checked the identity, and we want to */
+ dir_conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ if (dir_conf->do_rfc1413 & 1) {
+ return ap_rfc1413(r->connection, r->server);
+ }
+ else {
+ return NULL;
+ }
+}
+
+/* There are two options regarding what the "name" of a server is. The
+ * "canonical" name as defined by ServerName and Port, or the "client's
+ * name" as supplied by a possible Host: header or full URI. We never
+ * trust the port passed in the client's headers, we always use the
+ * port of the actual socket.
+ *
+ * The DNS option to UseCanonicalName causes this routine to do a
+ * reverse lookup on the local IP address of the connection and use
+ * that for the ServerName. This makes its value more reliable while
+ * at the same time allowing Demon's magic virtual hosting to work.
+ * The assumption is that DNS lookups are sufficiently quick...
+ * -- fanf 1998-10-03
+ */
+AP_DECLARE(const char *) ap_get_server_name(request_rec *r)
+{
+ conn_rec *conn = r->connection;
+ core_dir_config *d;
+
+ d = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ if (d->use_canonical_name == USE_CANONICAL_NAME_OFF) {
+ return r->hostname ? r->hostname : r->server->server_hostname;
+ }
+ if (d->use_canonical_name == USE_CANONICAL_NAME_DNS) {
+ if (conn->local_host == NULL) {
+ apr_sockaddr_t *local_addr;
+
+ apr_socket_addr_get(&local_addr, APR_LOCAL, conn->client_socket);
+ if (apr_getnameinfo(&conn->local_host, local_addr, 0) != APR_SUCCESS)
+ conn->local_host = apr_pstrdup(conn->pool, r->server->server_hostname);
+ else {
+ ap_str_tolower(conn->local_host);
+ }
+ }
+ return conn->local_host;
+ }
+ /* default */
+ return r->server->server_hostname;
+}
+
+AP_DECLARE(apr_port_t) ap_get_server_port(const request_rec *r)
+{
+ apr_port_t port;
+ core_dir_config *d =
+ (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+
+ port = r->server->port ? r->server->port : ap_default_port(r);
+
+ if (d->use_canonical_name == USE_CANONICAL_NAME_OFF
+ || d->use_canonical_name == USE_CANONICAL_NAME_DNS) {
+ if (r->hostname) {
+ apr_sockaddr_t *localsa;
+
+ apr_socket_addr_get(&localsa, APR_LOCAL, r->connection->client_socket);
+ apr_sockaddr_port_get(&port, localsa);
+ }
+ }
+ /* default */
+ return port;
+}
+
+AP_DECLARE(char *) ap_construct_url(apr_pool_t *p, const char *uri,
+ request_rec *r)
+{
+ unsigned port = ap_get_server_port(r);
+ const char *host = ap_get_server_name(r);
+
+ if (ap_is_default_port(port, r)) {
+ return apr_pstrcat(p, ap_http_method(r), "://", host, uri, NULL);
+ }
+ return apr_psprintf(p, "%s://%s:%u%s", ap_http_method(r), host, port, uri);
+}
+
+AP_DECLARE(unsigned long) ap_get_limit_req_body(const request_rec *r)
+{
+ core_dir_config *d =
+ (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+
+ return d->limit_req_body;
+}
+
+#ifdef WIN32
+static apr_status_t get_win32_registry_default_value(apr_pool_t *p, HKEY hkey,
+ char* relativepath,
+ char **value)
+{
+ HKEY hkeyOpen;
+ DWORD type;
+ DWORD size = 0;
+ DWORD result = RegOpenKeyEx(hkey, relativepath, 0,
+ KEY_QUERY_VALUE, &hkeyOpen);
+
+ if (result != ERROR_SUCCESS)
+ return APR_FROM_OS_ERROR(result);
+
+ /* Read to NULL buffer to determine value size */
+ result = RegQueryValueEx(hkeyOpen, "", 0, &type, NULL, &size);
+
+ if (result == ERROR_SUCCESS) {
+ if ((size < 2) || (type != REG_SZ && type != REG_EXPAND_SZ)) {
+ result = ERROR_INVALID_PARAMETER;
+ }
+ else {
+ *value = apr_palloc(p, size);
+ /* Read value based on size query above */
+ result = RegQueryValueEx(hkeyOpen, "", 0, &type, *value, &size);
+ }
+ }
+
+ /* TODO: This might look fine, but we need to provide some warning
+ * somewhere that some environment variables may -not- be translated,
+ * seeing as we may have chopped the environment table down somewhat.
+ */
+ if ((result == ERROR_SUCCESS) && (type == REG_EXPAND_SZ))
+ {
+ char *tmp = *value;
+ size = ExpandEnvironmentStrings(tmp, *value, 0);
+ if (size) {
+ *value = apr_palloc(p, size);
+ size = ExpandEnvironmentStrings(tmp, *value, size);
+ }
+ }
+
+ RegCloseKey(hkeyOpen);
+ return APR_FROM_OS_ERROR(result);
+}
+
+static char* get_interpreter_from_win32_registry(apr_pool_t *p, const char* ext,
+ char** arguments, int strict)
+{
+ char execcgi_path[] = "SHELL\\EXECCGI\\COMMAND";
+ char execopen_path[] = "SHELL\\OPEN\\COMMAND";
+ char typeName[MAX_PATH];
+ int cmdOfName = FALSE;
+ HKEY hkeyName;
+ HKEY hkeyType;
+ DWORD type;
+ int size;
+ int result;
+ char *buffer;
+ char *s;
+
+ if (!ext)
+ return NULL;
+ /*
+ * Future optimization:
+ * When the registry is successfully searched, store the strings for
+ * interpreter and arguments in an ext hash to speed up subsequent look-ups
+ */
+
+ /* Open the key associated with the script filetype extension */
+ result = RegOpenKeyEx(HKEY_CLASSES_ROOT, ext, 0, KEY_QUERY_VALUE,
+ &hkeyType);
+
+ if (result != ERROR_SUCCESS)
+ return NULL;
+
+ /* Retrieve the name of the script filetype extension */
+ size = sizeof(typeName);
+ result = RegQueryValueEx(hkeyType, "", NULL, &type, typeName, &size);
+
+ if (result == ERROR_SUCCESS && type == REG_SZ && typeName[0]) {
+ /* Open the key associated with the script filetype extension */
+ result = RegOpenKeyEx(HKEY_CLASSES_ROOT, typeName, 0,
+ KEY_QUERY_VALUE, &hkeyName);
+
+ if (result == ERROR_SUCCESS)
+ cmdOfName = TRUE;
+ }
+
+ /* Open the key for the script command path by:
+ *
+ * 1) the 'named' filetype key for ExecCGI/Command
+ * 2) the extension's type key for ExecCGI/Command
+ *
+ * and if the strict arg is false, then continue trying:
+ *
+ * 3) the 'named' filetype key for Open/Command
+ * 4) the extension's type key for Open/Command
+ */
+
+ if (cmdOfName) {
+ result = get_win32_registry_default_value(p, hkeyName,
+ execcgi_path, &buffer);
+ }
+
+ if (!cmdOfName || (result != ERROR_SUCCESS)) {
+ result = get_win32_registry_default_value(p, hkeyType,
+ execcgi_path, &buffer);
+ }
+
+ if (!strict && cmdOfName && (result != ERROR_SUCCESS)) {
+ result = get_win32_registry_default_value(p, hkeyName,
+ execopen_path, &buffer);
+ }
+
+ if (!strict && (result != ERROR_SUCCESS)) {
+ result = get_win32_registry_default_value(p, hkeyType,
+ execopen_path, &buffer);
+ }
+
+ if (cmdOfName)
+ RegCloseKey(hkeyName);
+
+ RegCloseKey(hkeyType);
+
+ if (result != ERROR_SUCCESS)
+ return NULL;
+
+ /*
+ * The canonical way shell command entries are entered in the Win32
+ * registry is as follows:
+ * shell [options] "%1" [args]
+ * where
+ * shell - full path name to interpreter or shell to run.
+ * E.g., c:\usr\local\ntreskit\perl\bin\perl.exe
+ * options - optional switches
+ * E.g., \C
+ * "%1" - Place holder for file to run the shell against.
+ * Typically quoted.
+ * options - additional arguments
+ * E.g., /silent
+ *
+ * If we find a %1 or a quoted %1, lop off the remainder to arguments.
+ */
+ if (buffer && *buffer) {
+ if ((s = strstr(buffer, "\"%1")))
+ {
+ *s = '\0';
+ *arguments = s + 4;
+ }
+ else if ((s = strstr(buffer, "%1")))
+ {
+ *s = '\0';
+ *arguments = buffer + 2;
+ }
+ else
+ *arguments = strchr(buffer, '\0');
+ while (**arguments && isspace(**arguments))
+ ++*arguments;
+ }
+
+ return buffer;
+}
+
+AP_DECLARE (file_type_e) ap_get_win32_interpreter(const request_rec *r,
+ char** interpreter,
+ char** arguments)
+{
+ HANDLE hFile;
+ DWORD nBytesRead;
+ BOOLEAN bResult;
+ char buffer[1024];
+ core_dir_config *d;
+ int i;
+ file_type_e fileType = eFileTypeUNKNOWN;
+ char *ext = NULL;
+ char *exename = NULL;
+
+ d = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ /* Find the file extension */
+ exename = strrchr(r->filename, '/');
+ if (!exename) {
+ exename = strrchr(r->filename, '\\');
+ }
+ if (!exename) {
+ exename = r->filename;
+ }
+ else {
+ exename++;
+ }
+ ext = strrchr(exename, '.');
+
+ if (ext && (!strcasecmp(ext,".bat") || !strcasecmp(ext,".cmd")))
+ {
+ char *comspec = getenv("COMSPEC");
+ if (comspec) {
+ *interpreter = apr_pstrcat(r->pool, "\"", comspec, "\" /c ", NULL);
+ return eFileTypeSCRIPT;
+ }
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r->server,
+ "Failed to start a '%s' file as a script." APR_EOL_STR
+ "\tCOMSPEC variable is missing from the environment.", ext);
+ return eFileTypeUNKNOWN;
+ }
+
+ /* If the file has an extension and it is not .com and not .exe and
+ * we've been instructed to search the registry, then do it!
+ */
+ if (ext && strcasecmp(ext,".exe") && strcasecmp(ext,".com") &&
+ (d->script_interpreter_source == INTERPRETER_SOURCE_REGISTRY ||
+ d->script_interpreter_source == INTERPRETER_SOURCE_REGISTRY_STRICT)) {
+ /* Check the registry */
+ int strict = (d->script_interpreter_source
+ == INTERPRETER_SOURCE_REGISTRY_STRICT);
+ *interpreter = get_interpreter_from_win32_registry(r->pool, ext,
+ arguments, strict);
+ if (*interpreter)
+ return eFileTypeSCRIPT;
+ else if (d->script_interpreter_source == INTERPRETER_SOURCE_REGISTRY_STRICT) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r->server,
+ "ScriptInterpreterSource config directive set to \"registry-strict\"." APR_EOL_STR
+ "\tInterpreter not found for files of type '%s'.", ext);
+ return eFileTypeUNKNOWN;
+ }
+ else
+ {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r->server,
+ "ScriptInterpreterSource config directive set to \"registry\"." APR_EOL_STR
+ "\tInterpreter not found for files of type '%s', "
+ "trying \"script\" method...", ext);
+ }
+ }
+
+ /* Need to peek into the file figure out what it really is... */
+ /* This is wrong for Unicode FS ... should move to APR */
+ hFile = CreateFile(r->filename, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ return eFileTypeUNKNOWN;
+ }
+ bResult = ReadFile(hFile, (void*) &buffer, sizeof(buffer) - 1,
+ &nBytesRead, NULL);
+ if (!bResult || (nBytesRead == 0)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, GetLastError(), r,
+ "ReadFile(%s) failed", r->filename);
+ CloseHandle(hFile);
+ return eFileTypeUNKNOWN;
+ }
+ CloseHandle(hFile);
+ buffer[nBytesRead] = '\0';
+
+ /* Script or executable, that is the question... */
+ if ((buffer[0] == '#') && (buffer[1] == '!')) {
+ /* Assuming file is a script since it starts with a shebang */
+ fileType = eFileTypeSCRIPT;
+ for (i = 2; i < sizeof(buffer); i++) {
+ if ((buffer[i] == '\r')
+ || (buffer[i] == '\n')) {
+ break;
+ }
+ }
+ buffer[i] = '\0';
+ for (i = 2; buffer[i] == ' ' ; ++i)
+ ;
+ *interpreter = apr_pstrdup(r->pool, buffer + i );
+ }
+ else {
+ /* Not a script, is it an executable? */
+ IMAGE_DOS_HEADER *hdr = (IMAGE_DOS_HEADER*)buffer;
+ if ((nBytesRead >= sizeof(IMAGE_DOS_HEADER)) && (hdr->e_magic == IMAGE_DOS_SIGNATURE)) {
+ if (hdr->e_lfarlc < 0x40)
+ fileType = eFileTypeEXE16;
+ else
+ fileType = eFileTypeEXE32;
+ }
+ else
+ fileType = eFileTypeUNKNOWN;
+ }
+
+ return fileType;
+}
+#endif
+
+/*****************************************************************
+ *
+ * Commands... this module handles almost all of the NCSA httpd.conf
+ * commands, but most of the old srm.conf is in the the modules.
+ */
+
+
+/* returns a parent if it matches the given directive */
+static const ap_directive_t * find_parent(const ap_directive_t *dirp,
+ const char *what)
+{
+ while (dirp->parent != NULL) {
+ dirp = dirp->parent;
+ /* ### it would be nice to have atom-ized directives */
+ if (strcasecmp(dirp->directive, what) == 0)
+ return dirp;
+ }
+ return NULL;
+}
+
+AP_DECLARE(const char *) ap_check_cmd_context(cmd_parms *cmd,
+ unsigned forbidden)
+{
+ const char *gt = (cmd->cmd->name[0] == '<'
+ && cmd->cmd->name[strlen(cmd->cmd->name)-1] != '>')
+ ? ">" : "";
+ const ap_directive_t *found;
+
+ if ((forbidden & NOT_IN_VIRTUALHOST) && cmd->server->is_virtual) {
+ return apr_pstrcat(cmd->pool, cmd->cmd->name, gt,
+ " cannot occur within <VirtualHost> section", NULL);
+ }
+
+ if ((forbidden & NOT_IN_LIMIT) && cmd->limited != -1) {
+ return apr_pstrcat(cmd->pool, cmd->cmd->name, gt,
+ " cannot occur within <Limit> section", NULL);
+ }
+
+ if ((forbidden & NOT_IN_DIR_LOC_FILE) == NOT_IN_DIR_LOC_FILE
+ && cmd->path != NULL) {
+ return apr_pstrcat(cmd->pool, cmd->cmd->name, gt,
+ " cannot occur within <Directory/Location/Files> "
+ "section", NULL);
+ }
+
+ if (((forbidden & NOT_IN_DIRECTORY)
+ && ((found = find_parent(cmd->directive, "<Directory"))
+ || (found = find_parent(cmd->directive, "<DirectoryMatch"))))
+ || ((forbidden & NOT_IN_LOCATION)
+ && ((found = find_parent(cmd->directive, "<Location"))
+ || (found = find_parent(cmd->directive, "<LocationMatch"))))
+ || ((forbidden & NOT_IN_FILES)
+ && ((found = find_parent(cmd->directive, "<Files"))
+ || (found = find_parent(cmd->directive, "<FilesMatch"))))) {
+ return apr_pstrcat(cmd->pool, cmd->cmd->name, gt,
+ " cannot occur within ", found->directive,
+ "> section", NULL);
+ }
+
+ return NULL;
+}
+
+static const char *set_access_name(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ void *sconf = cmd->server->module_config;
+ core_server_config *conf = ap_get_module_config(sconf, &core_module);
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ conf->access_name = apr_pstrdup(cmd->pool, arg);
+ return NULL;
+}
+
+#ifdef GPROF
+static const char *set_gprof_dir(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ void *sconf = cmd->server->module_config;
+ core_server_config *conf = ap_get_module_config(sconf, &core_module);
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ conf->gprof_dir = apr_pstrdup(cmd->pool, arg);
+ return NULL;
+}
+#endif /*GPROF*/
+
+static const char *set_add_default_charset(cmd_parms *cmd,
+ void *d_, const char *arg)
+{
+ core_dir_config *d=d_;
+
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+ if (!strcasecmp(arg, "Off")) {
+ d->add_default_charset = ADD_DEFAULT_CHARSET_OFF;
+ }
+ else if (!strcasecmp(arg, "On")) {
+ d->add_default_charset = ADD_DEFAULT_CHARSET_ON;
+ d->add_default_charset_name = DEFAULT_ADD_DEFAULT_CHARSET_NAME;
+ }
+ else {
+ d->add_default_charset = ADD_DEFAULT_CHARSET_ON;
+ d->add_default_charset_name = arg;
+ }
+ return NULL;
+}
+
+static const char *set_document_root(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ void *sconf = cmd->server->module_config;
+ core_server_config *conf = ap_get_module_config(sconf, &core_module);
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ arg = ap_os_canonical_filename(cmd->pool, arg);
+ if (/* TODO: ap_configtestonly && ap_docrootcheck && */ !ap_is_directory(cmd->pool, arg)) {
+ if (cmd->server->is_virtual) {
+ ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, cmd->pool,
+ "Warning: DocumentRoot [%s] does not exist",
+ arg);
+ }
+ else {
+ return "DocumentRoot must be a directory";
+ }
+ }
+
+ conf->ap_document_root = arg;
+ return NULL;
+}
+
+AP_DECLARE(void) ap_custom_response(request_rec *r, int status, char *string)
+{
+ core_dir_config *conf =
+ ap_get_module_config(r->per_dir_config, &core_module);
+ int idx;
+
+ if(conf->response_code_strings == NULL) {
+ conf->response_code_strings =
+ apr_pcalloc(r->pool,
+ sizeof(*conf->response_code_strings) *
+ RESPONSE_CODES);
+ }
+
+ idx = ap_index_of_response(status);
+
+ conf->response_code_strings[idx] =
+ ((ap_is_url(string) || (*string == '/')) && (*string != '"')) ?
+ apr_pstrdup(r->pool, string) : apr_pstrcat(r->pool, "\"", string, NULL);
+}
+
+static const char *set_error_document(cmd_parms *cmd, void *conf_,
+ const char *errno_str, const char *msg)
+{
+ core_dir_config *conf=conf_;
+ int error_number, index_number, idx500;
+ enum { MSG, LOCAL_PATH, REMOTE_PATH } what = MSG;
+
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ /* 1st parameter should be a 3 digit number, which we recognize;
+ * convert it into an array index
+ */
+ error_number = atoi(errno_str);
+ idx500 = ap_index_of_response(HTTP_INTERNAL_SERVER_ERROR);
+
+ if (error_number == HTTP_INTERNAL_SERVER_ERROR) {
+ index_number = idx500;
+ }
+ else if ((index_number = ap_index_of_response(error_number)) == idx500) {
+ return apr_pstrcat(cmd->pool, "Unsupported HTTP response code ",
+ errno_str, NULL);
+ }
+
+ /* Heuristic to determine second argument. */
+ if (ap_strchr_c(msg,' '))
+ what = MSG;
+ else if (msg[0] == '/')
+ what = LOCAL_PATH;
+ else if (ap_is_url(msg))
+ what = REMOTE_PATH;
+ else
+ what = MSG;
+
+ /* The entry should be ignored if it is a full URL for a 401 error */
+
+ if (error_number == 401 && what == REMOTE_PATH) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, cmd->server,
+ "cannot use a full URL in a 401 ErrorDocument "
+ "directive --- ignoring!");
+ }
+ else { /* Store it... */
+ if (conf->response_code_strings == NULL) {
+ conf->response_code_strings =
+ apr_pcalloc(cmd->pool,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES);
+ }
+ /* hack. Prefix a " if it is a msg; as that is what
+ * http_protocol.c relies on to distinguish between
+ * a msg and a (local) path.
+ */
+ conf->response_code_strings[index_number] = (what == MSG) ?
+ apr_pstrcat(cmd->pool, "\"",msg,NULL) :
+ apr_pstrdup(cmd->pool, msg);
+ }
+
+ return NULL;
+}
+
+static const char *set_override(cmd_parms *cmd, void *d_, const char *l)
+{
+ core_dir_config *d=d_;
+ char *w;
+
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ d->override = OR_NONE;
+ while (l[0]) {
+ w = ap_getword_conf(cmd->pool, &l);
+ if (!strcasecmp(w, "Limit")) {
+ d->override |= OR_LIMIT;
+ }
+ else if (!strcasecmp(w, "Options")) {
+ d->override |= OR_OPTIONS;
+ }
+ else if (!strcasecmp(w, "FileInfo")) {
+ d->override |= OR_FILEINFO;
+ }
+ else if (!strcasecmp(w, "AuthConfig")) {
+ d->override |= OR_AUTHCFG;
+ }
+ else if (!strcasecmp(w, "Indexes")) {
+ d->override |= OR_INDEXES;
+ }
+ else if (!strcasecmp(w, "None")) {
+ d->override = OR_NONE;
+ }
+ else if (!strcasecmp(w, "All")) {
+ d->override = OR_ALL;
+ }
+ else {
+ return apr_pstrcat(cmd->pool, "Illegal override option ", w, NULL);
+ }
+ d->override &= ~OR_UNSET;
+ }
+
+ return NULL;
+}
+
+static const char *set_options(cmd_parms *cmd, void *d_, const char *l)
+{
+ core_dir_config *d=d_;
+ allow_options_t opt;
+ int first = 1;
+ char action;
+
+ while (l[0]) {
+ char *w = ap_getword_conf(cmd->pool, &l);
+ action = '\0';
+
+ if (*w == '+' || *w == '-') {
+ action = *(w++);
+ }
+ else if (first) {
+ d->opts = OPT_NONE;
+ first = 0;
+ }
+
+ if (!strcasecmp(w, "Indexes")) {
+ opt = OPT_INDEXES;
+ }
+ else if (!strcasecmp(w, "Includes")) {
+ opt = OPT_INCLUDES;
+ }
+ else if (!strcasecmp(w, "IncludesNOEXEC")) {
+ opt = (OPT_INCLUDES | OPT_INCNOEXEC);
+ }
+ else if (!strcasecmp(w, "FollowSymLinks")) {
+ opt = OPT_SYM_LINKS;
+ }
+ else if (!strcasecmp(w, "SymLinksIfOwnerMatch")) {
+ opt = OPT_SYM_OWNER;
+ }
+ else if (!strcasecmp(w, "execCGI")) {
+ opt = OPT_EXECCGI;
+ }
+ else if (!strcasecmp(w, "MultiViews")) {
+ opt = OPT_MULTI;
+ }
+ else if (!strcasecmp(w, "RunScripts")) { /* AI backcompat. Yuck */
+ opt = OPT_MULTI|OPT_EXECCGI;
+ }
+ else if (!strcasecmp(w, "None")) {
+ opt = OPT_NONE;
+ }
+ else if (!strcasecmp(w, "All")) {
+ opt = OPT_ALL;
+ }
+ else {
+ return apr_pstrcat(cmd->pool, "Illegal option ", w, NULL);
+ }
+
+ /* we ensure the invariant (d->opts_add & d->opts_remove) == 0 */
+ if (action == '-') {
+ d->opts_remove |= opt;
+ d->opts_add &= ~opt;
+ d->opts &= ~opt;
+ }
+ else if (action == '+') {
+ d->opts_add |= opt;
+ d->opts_remove &= ~opt;
+ d->opts |= opt;
+ }
+ else {
+ d->opts |= opt;
+ }
+ }
+
+ return NULL;
+}
+
+static const char *satisfy(cmd_parms *cmd, void *c_, const char *arg)
+{
+ core_dir_config *c=c_;
+
+ if (!strcasecmp(arg, "all")) {
+ c->satisfy = SATISFY_ALL;
+ }
+ else if (!strcasecmp(arg, "any")) {
+ c->satisfy = SATISFY_ANY;
+ }
+ else {
+ return "Satisfy either 'any' or 'all'.";
+ }
+ return NULL;
+}
+
+static const char *require(cmd_parms *cmd, void *c_, const char *arg)
+{
+ require_line *r;
+ core_dir_config *c=c_;
+
+ if (!c->ap_requires) {
+ c->ap_requires = apr_array_make(cmd->pool, 2, sizeof(require_line));
+ }
+ r = (require_line *)apr_array_push(c->ap_requires);
+ r->requirement = apr_pstrdup(cmd->pool, arg);
+ r->method_mask = cmd->limited;
+ return NULL;
+}
+
+AP_CORE_DECLARE_NONSTD(const char *) ap_limit_section(cmd_parms *cmd, void *dummy,
+ const char *arg) {
+ const char *limited_methods = ap_getword(cmd->pool, &arg, '>');
+ void *tog = cmd->cmd->cmd_data;
+ int limited = 0;
+ const char *errmsg;
+
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ while (limited_methods[0]) {
+ char *method = ap_getword_conf(cmd->pool, &limited_methods);
+ int methnum = ap_method_number_of(method);
+
+ if (methnum == M_TRACE && !tog) {
+ return "TRACE cannot be controlled by <Limit>";
+ }
+ else if (methnum == M_INVALID) {
+ char **xmethod;
+ register int i, j, k;
+
+ /*
+ * Deal with <Limit> by adding the method to the list.
+ */
+ if (!tog) {
+ if (cmd->limited_xmethods == NULL) {
+ cmd->limited_xmethods = apr_array_make(cmd->pool, 2,
+ sizeof(char *));
+ }
+ xmethod = (char **) apr_array_push(cmd->limited_xmethods);
+ *xmethod = apr_pstrdup(cmd->pool, method);
+ }
+ /*
+ * <LimitExcept>, so remove any/all occurrences of the method
+ * in the extension array.
+ */
+ else if ((cmd->limited_xmethods != NULL)
+ && (cmd->limited_xmethods->nelts != 0)) {
+ xmethod = (char **) cmd->limited_xmethods->elts;
+ for (i = 0; i < cmd->limited_xmethods->nelts; ) {
+ if (strcmp(xmethod[i], method) == 0) {
+ for (j = i, k = i + 1;
+ k < cmd->limited_xmethods->nelts;
+ ++j, ++k) {
+ xmethod[j] = xmethod[k];
+ }
+ cmd->limited_xmethods->nelts--;
+ }
+ }
+ }
+ }
+ limited |= (1 << methnum);
+ }
+
+ /* Killing two features with one function,
+ * if (tog == NULL) <Limit>, else <LimitExcept>
+ */
+ cmd->limited = tog ? ~limited : limited;
+
+ errmsg = ap_walk_config(cmd->directive->first_child, cmd, cmd->context);
+
+ cmd->limited = -1;
+
+ return errmsg;
+}
+
+/* We use this in <DirectoryMatch> and <FilesMatch>, to ensure that
+ * people don't get bitten by wrong-cased regex matches
+ */
+
+#ifdef WIN32
+#define USE_ICASE REG_ICASE
+#else
+#define USE_ICASE 0
+#endif
+
+/*
+ * Report a missing-'>' syntax error.
+ */
+static char *unclosed_directive(cmd_parms *cmd)
+{
+ return apr_pstrcat(cmd->pool, cmd->cmd->name,
+ "> directive missing closing '>'", NULL);
+}
+
+static const char *dirsection(cmd_parms *cmd, void *mconfig, const char *arg)
+{
+ const char *errmsg;
+ const char *endp = ap_strrchr_c(arg, '>');
+ int old_overrides = cmd->override;
+ char *old_path = cmd->path;
+ core_dir_config *conf;
+ ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool);
+ regex_t *r = NULL;
+ const command_rec *thiscmd = cmd->cmd;
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ arg=apr_pstrndup(cmd->pool, arg, endp-arg);
+
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ cmd->override = OR_ALL|ACCESS_CONF;
+
+ if (thiscmd->cmd_data) { /* <DirectoryMatch> */
+ r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
+ }
+ else if (!strcmp(cmd->path, "~")) {
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
+ }
+#if defined(HAVE_DRIVE_LETTERS) || defined(NETWARE)
+ else if (strcmp(cmd->path, "/") == 0) {
+ /* Treat 'default' path / as an inalienable root */
+ cmd->path = apr_pstrdup(cmd->pool, cmd->path);
+ }
+#endif
+#if defined(HAVE_UNC_PATHS)
+ else if (strcmp(cmd->path, "//") == 0) {
+ /* Treat UNC path // as an inalienable root */
+ cmd->path = apr_pstrdup(cmd->pool, cmd->path);
+ }
+#endif
+ else {
+ /* Ensure that the pathname is canonical */
+ cmd->path = ap_os_canonical_filename(cmd->pool, cmd->path);
+ }
+
+ /* initialize our config and fetch it */
+ conf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path,
+ &core_module, cmd->pool);
+
+ errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_dir_conf);
+ if (errmsg != NULL)
+ return errmsg;
+
+ conf->r = r;
+
+ ap_add_per_dir_conf(cmd->server, new_dir_conf);
+
+ if (*arg != '\0') {
+ return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
+ "> arguments not (yet) supported.", NULL);
+ }
+
+ cmd->path = old_path;
+ cmd->override = old_overrides;
+
+ return NULL;
+}
+
+static const char *urlsection(cmd_parms *cmd, void *mconfig, const char *arg)
+{
+ const char *errmsg;
+ const char *endp = ap_strrchr_c(arg, '>');
+ int old_overrides = cmd->override;
+ char *old_path = cmd->path;
+ core_dir_config *conf;
+ regex_t *r = NULL;
+ const command_rec *thiscmd = cmd->cmd;
+ ap_conf_vector_t *new_url_conf = ap_create_per_dir_config(cmd->pool);
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ arg=apr_pstrndup(cmd->pool, arg, endp-arg);
+
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ cmd->override = OR_ALL|ACCESS_CONF;
+
+ if (thiscmd->cmd_data) { /* <LocationMatch> */
+ r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+ }
+ else if (!strcmp(cmd->path, "~")) {
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+ }
+
+ /* initialize our config and fetch it */
+ conf = ap_set_config_vectors(cmd->server, new_url_conf, cmd->path,
+ &core_module, cmd->pool);
+
+ errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_url_conf);
+ if (errmsg != NULL)
+ return errmsg;
+
+ conf->d = apr_pstrdup(cmd->pool, cmd->path); /* No mangling, please */
+ conf->d_is_fnmatch = apr_is_fnmatch(conf->d) != 0;
+ conf->r = r;
+
+ ap_add_per_url_conf(cmd->server, new_url_conf);
+
+ if (*arg != '\0') {
+ return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
+ "> arguments not (yet) supported.", NULL);
+ }
+
+ cmd->path = old_path;
+ cmd->override = old_overrides;
+
+ return NULL;
+}
+
+static const char *filesection(cmd_parms *cmd, void *mconfig, const char *arg)
+{
+ const char *errmsg;
+ const char *endp = ap_strrchr_c(arg, '>');
+ int old_overrides = cmd->override;
+ char *old_path = cmd->path;
+ core_dir_config *conf;
+ regex_t *r = NULL;
+ const command_rec *thiscmd = cmd->cmd;
+ core_dir_config *c=mconfig;
+ ap_conf_vector_t *new_file_conf = ap_create_per_dir_config(cmd->pool);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_LOCATION);
+
+ if (err != NULL) {
+ return err;
+ }
+
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ arg=apr_pstrndup(cmd->pool, arg, endp-arg);
+
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ /* Only if not an .htaccess file */
+ if (!old_path) {
+ cmd->override = OR_ALL|ACCESS_CONF;
+ }
+
+ if (thiscmd->cmd_data) { /* <FilesMatch> */
+ r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
+ }
+ else if (!strcmp(cmd->path, "~")) {
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
+ }
+ else {
+ /* Ensure that the pathname is canonical */
+ cmd->path = ap_os_canonical_filename(cmd->pool, cmd->path);
+ }
+
+ /* initialize our config and fetch it */
+ conf = ap_set_config_vectors(cmd->server, new_file_conf, cmd->path,
+ &core_module, cmd->pool);
+
+ errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_file_conf);
+ if (errmsg != NULL)
+ return errmsg;
+
+ conf->d = cmd->path;
+ conf->d_is_fnmatch = apr_is_fnmatch(conf->d) != 0;
+ conf->r = r;
+
+ ap_add_file_conf(c, new_file_conf);
+
+ if (*arg != '\0') {
+ return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
+ "> arguments not (yet) supported.", NULL);
+ }
+
+ cmd->path = old_path;
+ cmd->override = old_overrides;
+
+ return NULL;
+}
+
+static const char *start_ifmod(cmd_parms *cmd, void *mconfig, const char *arg)
+{
+ const char *endp = ap_strrchr_c(arg, '>');
+ int not = (arg[0] == '!');
+ module *found;
+
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ arg=apr_pstrndup(cmd->pool, arg, endp-arg);
+
+ if (not) {
+ arg++;
+ }
+
+ found = ap_find_linked_module(arg);
+
+ if ((!not && found) || (not && !found)) {
+ ap_directive_t *parent = NULL;
+ ap_directive_t *current = NULL;
+ const char *retval;
+
+ retval = ap_build_cont_config(cmd->pool, cmd->temp_pool, cmd,
+ &current, &parent, "<IfModule");
+ *(ap_directive_t **)mconfig = current;
+ return retval;
+ }
+ else {
+ *(ap_directive_t **)mconfig = NULL;
+ return ap_soak_end_container(cmd, "<IfModule");
+ }
+}
+
+AP_DECLARE(int) ap_exists_config_define(const char *name)
+{
+ char **defines;
+ int i;
+
+ defines = (char **)ap_server_config_defines->elts;
+ for (i = 0; i < ap_server_config_defines->nelts; i++) {
+ if (strcmp(defines[i], name) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static const char *start_ifdefine(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *endp;
+ int defined;
+ int not = 0;
+
+ endp = ap_strrchr_c(arg, '>');
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ arg=apr_pstrndup(cmd->pool, arg, endp-arg);
+
+ if (arg[0] == '!') {
+ not = 1;
+ arg++;
+ }
+
+ defined = ap_exists_config_define(arg);
+ if ((!not && defined) || (not && !defined)) {
+ ap_directive_t *parent = NULL;
+ ap_directive_t *current = NULL;
+ const char *retval;
+
+ retval = ap_build_cont_config(cmd->pool, cmd->temp_pool, cmd,
+ &current, &parent, "<IfDefine");
+ *(ap_directive_t **)dummy = current;
+ return retval;
+ }
+ else {
+ *(ap_directive_t **)dummy = NULL;
+ return ap_soak_end_container(cmd, "<IfDefine");
+ }
+}
+
+/* httpd.conf commands... beginning with the <VirtualHost> business */
+
+static const char *virtualhost_section(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ server_rec *main_server = cmd->server, *s;
+ const char *errmsg;
+ const char *endp = ap_strrchr_c(arg, '>');
+ apr_pool_t *p = cmd->pool;
+
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ arg=apr_pstrndup(cmd->pool, arg, endp-arg);
+
+ /* FIXME: There's another feature waiting to happen here -- since you
+ can now put multiple addresses/names on a single <VirtualHost>
+ you might want to use it to group common definitions and then
+ define other "subhosts" with their individual differences. But
+ personally I'd rather just do it with a macro preprocessor. -djg */
+ if (main_server->is_virtual) {
+ return "<VirtualHost> doesn't nest!";
+ }
+
+ errmsg = ap_init_virtual_host(p, arg, main_server, &s);
+ if (errmsg) {
+ return errmsg;
+ }
+
+ s->next = main_server->next;
+ main_server->next = s;
+
+ s->defn_name = cmd->directive->filename;
+ s->defn_line_number = cmd->directive->line_num;
+
+ cmd->server = s;
+
+ errmsg = ap_walk_config(cmd->directive->first_child, cmd,
+ s->lookup_defaults);
+
+ cmd->server = main_server;
+
+ return errmsg;
+}
+
+static const char *set_server_alias(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ if (!cmd->server->names) {
+ return "ServerAlias only used in <VirtualHost>";
+ }
+ while (*arg) {
+ char **item, *name = ap_getword_conf(cmd->pool, &arg);
+ if (ap_is_matchexp(name)) {
+ item = (char **)apr_array_push(cmd->server->wild_names);
+ }
+ else {
+ item = (char **)apr_array_push(cmd->server->names);
+ }
+ *item = name;
+ }
+ return NULL;
+}
+
+static const char *add_filter(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ core_dir_config *conf = dummy;
+ char **newfilter;
+
+ newfilter = (char **)apr_array_push(conf->output_filters);
+ *newfilter = apr_pstrdup(cmd->pool, arg);
+ return NULL;
+}
+
+static const char *add_input_filter(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ core_dir_config *conf = dummy;
+ char **newfilter;
+
+ newfilter = (char **)apr_array_push(conf->input_filters);
+ *newfilter = apr_pstrdup(cmd->pool, arg);
+ return NULL;
+}
+
+static const char *set_server_string_slot(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ /* This one's pretty generic... */
+
+ int offset = (int)(long)cmd->info;
+ char *struct_ptr = (char *)cmd->server;
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ *(const char **)(struct_ptr + offset) = arg;
+ return NULL;
+}
+
+static const char *server_port(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ int port;
+
+ if (err != NULL) {
+ return err;
+ }
+ port = atoi(arg);
+ if (port <= 0 || port >= 65536) { /* 65536 == 1<<16 */
+ return apr_pstrcat(cmd->temp_pool, "The port number \"", arg,
+ "\" is outside the appropriate range "
+ "(i.e., 1..65535).", NULL);
+ }
+ cmd->server->port = port;
+ return NULL;
+}
+
+static const char *set_signature_flag(cmd_parms *cmd, void *d_,
+ const char *arg)
+{
+ core_dir_config *d=d_;
+
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (strcasecmp(arg, "On") == 0) {
+ d->server_signature = srv_sig_on;
+ }
+ else if (strcasecmp(arg, "Off") == 0) {
+ d->server_signature = srv_sig_off;
+ }
+ else if (strcasecmp(arg, "EMail") == 0) {
+ d->server_signature = srv_sig_withmail;
+ }
+ else {
+ return "ServerSignature: use one of: off | on | email";
+ }
+ return NULL;
+}
+
+static const char *set_server_root(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+ if (err != NULL) {
+ return err;
+ }
+
+ arg = ap_os_canonical_filename(cmd->pool, arg);
+
+ if (!ap_is_directory(cmd->pool, arg)) {
+ return "ServerRoot must be a valid directory";
+ }
+ ap_server_root = arg;
+ return NULL;
+}
+
+static const char *set_timeout(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ cmd->server->timeout = atoi(arg);
+ return NULL;
+}
+
+static const char *set_idcheck(cmd_parms *cmd, void *d_, int arg)
+{
+ core_dir_config *d=d_;
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ d->do_rfc1413 = arg != 0;
+ return NULL;
+}
+
+static const char *set_hostname_lookups(cmd_parms *cmd, void *d_,
+ const char *arg)
+{
+ core_dir_config *d=d_;
+
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (!strcasecmp(arg, "on")) {
+ d->hostname_lookups = HOSTNAME_LOOKUP_ON;
+ }
+ else if (!strcasecmp(arg, "off")) {
+ d->hostname_lookups = HOSTNAME_LOOKUP_OFF;
+ }
+ else if (!strcasecmp(arg, "double")) {
+ d->hostname_lookups = HOSTNAME_LOOKUP_DOUBLE;
+ }
+ else {
+ return "parameter must be 'on', 'off', or 'double'";
+ }
+ return NULL;
+}
+
+static const char *set_serverpath(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ cmd->server->path = arg;
+ cmd->server->pathlen = strlen(arg);
+ return NULL;
+}
+
+static const char *set_content_md5(cmd_parms *cmd, void *d_, int arg)
+{
+ core_dir_config *d=d_;
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ d->content_md5 = arg != 0;
+ return NULL;
+}
+
+static const char *set_use_canonical_name(cmd_parms *cmd, void *d_,
+ const char *arg)
+{
+ core_dir_config *d=d_;
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (strcasecmp(arg, "on") == 0) {
+ d->use_canonical_name = USE_CANONICAL_NAME_ON;
+ }
+ else if (strcasecmp(arg, "off") == 0) {
+ d->use_canonical_name = USE_CANONICAL_NAME_OFF;
+ }
+ else if (strcasecmp(arg, "dns") == 0) {
+ d->use_canonical_name = USE_CANONICAL_NAME_DNS;
+ }
+ else {
+ return "parameter must be 'on', 'off', or 'dns'";
+ }
+ return NULL;
+}
+
+
+static const char *include_config (cmd_parms *cmd, void *dummy,
+ const char *name)
+{
+ ap_directive_t *conftree = NULL;
+
+ ap_process_resource_config(cmd->server,
+ ap_server_root_relative(cmd->pool, name),
+ &conftree, cmd->pool, cmd->temp_pool);
+ *(ap_directive_t **)dummy = conftree;
+ return NULL;
+}
+
+static const char *set_loglevel(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ char *str;
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if ((str = ap_getword_conf(cmd->pool, &arg))) {
+ if (!strcasecmp(str, "emerg")) {
+ cmd->server->loglevel = APLOG_EMERG;
+ }
+ else if (!strcasecmp(str, "alert")) {
+ cmd->server->loglevel = APLOG_ALERT;
+ }
+ else if (!strcasecmp(str, "crit")) {
+ cmd->server->loglevel = APLOG_CRIT;
+ }
+ else if (!strcasecmp(str, "error")) {
+ cmd->server->loglevel = APLOG_ERR;
+ }
+ else if (!strcasecmp(str, "warn")) {
+ cmd->server->loglevel = APLOG_WARNING;
+ }
+ else if (!strcasecmp(str, "notice")) {
+ cmd->server->loglevel = APLOG_NOTICE;
+ }
+ else if (!strcasecmp(str, "info")) {
+ cmd->server->loglevel = APLOG_INFO;
+ }
+ else if (!strcasecmp(str, "debug")) {
+ cmd->server->loglevel = APLOG_DEBUG;
+ }
+ else {
+ return "LogLevel requires level keyword: one of "
+ "emerg/alert/crit/error/warn/notice/info/debug";
+ }
+ }
+ else {
+ return "LogLevel requires level keyword";
+ }
+
+ return NULL;
+}
+
+AP_DECLARE(const char *) ap_psignature(const char *prefix, request_rec *r)
+{
+ char sport[20];
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+ if ((conf->server_signature == srv_sig_off)
+ || (conf->server_signature == srv_sig_unset)) {
+ return "";
+ }
+
+ apr_snprintf(sport, sizeof sport, "%u", (unsigned) ap_get_server_port(r));
+
+ if (conf->server_signature == srv_sig_withmail) {
+ return apr_pstrcat(r->pool, prefix, "<ADDRESS>" AP_SERVER_BASEVERSION
+ " Server at <A HREF=\"mailto:",
+ r->server->server_admin, "\">",
+ ap_get_server_name(r), "</A> Port ", sport,
+ "</ADDRESS>\n", NULL);
+ }
+ return apr_pstrcat(r->pool, prefix, "<ADDRESS>" AP_SERVER_BASEVERSION
+ " Server at ", ap_get_server_name(r), " Port ", sport,
+ "</ADDRESS>\n", NULL);
+}
+
+/*
+ * Load an authorisation realm into our location configuration, applying the
+ * usual rules that apply to realms.
+ */
+static const char *set_authname(cmd_parms *cmd, void *mconfig,
+ const char *word1)
+{
+ core_dir_config *aconfig = (core_dir_config *)mconfig;
+
+ aconfig->ap_auth_name = ap_escape_quotes(cmd->pool, word1);
+ return NULL;
+}
+
+#ifdef _OSD_POSIX /* BS2000 Logon Passwd file */
+static const char *set_bs2000_account(cmd_parms *cmd, void *dummy, char *name)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ return os_set_account(cmd->pool, name);
+}
+#endif /*_OSD_POSIX*/
+
+/*
+ * Handle a request to include the server's OS platform in the Server
+ * response header field (the ServerTokens directive). Unfortunately
+ * this requires a new global in order to communicate the setting back to
+ * http_main so it can insert the information in the right place in the
+ * string.
+ */
+
+static char *server_version = NULL;
+static int version_locked = 0;
+
+enum server_token_type {
+ SrvTk_MIN, /* eg: Apache/1.3.0 */
+ SrvTk_OS, /* eg: Apache/1.3.0 (UNIX) */
+ SrvTk_FULL, /* eg: Apache/1.3.0 (UNIX) PHP/3.0 FooBar/1.2b */
+ SrvTk_PRODUCT_ONLY /* eg: Apache */
+};
+static enum server_token_type ap_server_tokens = SrvTk_FULL;
+
+static apr_status_t reset_version(void *dummy)
+{
+ version_locked = 0;
+ ap_server_tokens = SrvTk_FULL;
+ server_version = NULL;
+ return APR_SUCCESS;
+}
+
+AP_DECLARE(const char *) ap_get_server_version(void)
+{
+ return (server_version ? server_version : AP_SERVER_BASEVERSION);
+}
+
+AP_DECLARE(void) ap_add_version_component(apr_pool_t *pconf, const char *component)
+{
+ if (! version_locked) {
+ /*
+ * If the version string is null, register our cleanup to reset the
+ * pointer on pool destruction. We also know that, if NULL,
+ * we are adding the original SERVER_BASEVERSION string.
+ */
+ if (server_version == NULL) {
+ apr_pool_cleanup_register(pconf, NULL, reset_version,
+ apr_pool_cleanup_null);
+ server_version = apr_pstrdup(pconf, component);
+ }
+ else {
+ /*
+ * Tack the given component identifier to the end of
+ * the existing string.
+ */
+ server_version = apr_pstrcat(pconf, server_version, " ",
+ component, NULL);
+ }
+ }
+}
+
+/*
+ * This routine adds the real server base identity to the version string,
+ * and then locks out changes until the next reconfig.
+ */
+static void ap_set_version(apr_pool_t *pconf)
+{
+ if (ap_server_tokens == SrvTk_PRODUCT_ONLY) {
+ ap_add_version_component(pconf, AP_SERVER_BASEPRODUCT);
+ }
+ else if (ap_server_tokens == SrvTk_MIN) {
+ ap_add_version_component(pconf, AP_SERVER_BASEVERSION);
+ }
+ else {
+ ap_add_version_component(pconf, AP_SERVER_BASEVERSION " (" PLATFORM ")");
+ }
+ /*
+ * Lock the server_version string if we're not displaying
+ * the full set of tokens
+ */
+ if (ap_server_tokens != SrvTk_FULL) {
+ version_locked++;
+ }
+}
+
+static const char *set_serv_tokens(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (!strcasecmp(arg, "OS")) {
+ ap_server_tokens = SrvTk_OS;
+ }
+ else if (!strcasecmp(arg, "Min") || !strcasecmp(arg, "Minimal")) {
+ ap_server_tokens = SrvTk_MIN;
+ }
+ else if (!strcasecmp(arg, "Prod") || !strcasecmp(arg, "ProductOnly")) {
+ ap_server_tokens = SrvTk_PRODUCT_ONLY;
+ }
+ else {
+ ap_server_tokens = SrvTk_FULL;
+ }
+ return NULL;
+}
+
+static const char *set_limit_req_line(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ int lim;
+
+ if (err != NULL) {
+ return err;
+ }
+ lim = atoi(arg);
+ if (lim < 0) {
+ return apr_pstrcat(cmd->temp_pool, "LimitRequestLine \"", arg,
+ "\" must be a non-negative integer", NULL);
+ }
+ if (lim > DEFAULT_LIMIT_REQUEST_LINE) {
+ return apr_psprintf(cmd->temp_pool, "LimitRequestLine \"%s\" "
+ "must not exceed the precompiled maximum of %d",
+ arg, DEFAULT_LIMIT_REQUEST_LINE);
+ }
+ cmd->server->limit_req_line = lim;
+ return NULL;
+}
+
+static const char *set_limit_req_fieldsize(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ int lim;
+
+ if (err != NULL) {
+ return err;
+ }
+ lim = atoi(arg);
+ if (lim < 0) {
+ return apr_pstrcat(cmd->temp_pool, "LimitRequestFieldsize \"", arg,
+ "\" must be a non-negative integer (0 = no limit)",
+ NULL);
+ }
+ if (lim > DEFAULT_LIMIT_REQUEST_FIELDSIZE) {
+ return apr_psprintf(cmd->temp_pool, "LimitRequestFieldsize \"%s\" "
+ "must not exceed the precompiled maximum of %d",
+ arg, DEFAULT_LIMIT_REQUEST_FIELDSIZE);
+ }
+ cmd->server->limit_req_fieldsize = lim;
+ return NULL;
+}
+
+static const char *set_limit_req_fields(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ int lim;
+
+ if (err != NULL) {
+ return err;
+ }
+ lim = atoi(arg);
+ if (lim < 0) {
+ return apr_pstrcat(cmd->temp_pool, "LimitRequestFields \"", arg,
+ "\" must be a non-negative integer (0 = no limit)",
+ NULL);
+ }
+ cmd->server->limit_req_fields = lim;
+ return NULL;
+}
+
+static const char *set_limit_req_body(cmd_parms *cmd, void *conf_,
+ const char *arg)
+{
+ core_dir_config *conf=conf_;
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ /* WTF: If strtoul is not portable, then write a replacement.
+ * Instead we have an idiotic define in httpd.h that prevents
+ * it from being used even when it is available. Sheesh.
+ */
+ conf->limit_req_body = (unsigned long)strtol(arg, (char **)NULL, 10);
+ return NULL;
+}
+
+static const char *set_limit_xml_req_body(cmd_parms *cmd, void *conf_,
+ const char *arg)
+{
+ core_dir_config *conf = conf_;
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ conf->limit_xml_body = atol(arg);
+ if (conf->limit_xml_body < 0)
+ return "LimitXMLRequestBody requires a non-negative integer.";
+
+ return NULL;
+}
+
+AP_DECLARE(size_t) ap_get_limit_xml_body(const request_rec *r)
+{
+ core_dir_config *conf;
+
+ conf = ap_get_module_config(r->per_dir_config, &core_module);
+ if (conf->limit_xml_body == AP_LIMIT_UNSET)
+ return AP_DEFAULT_LIMIT_XML_BODY;
+ return (size_t)conf->limit_xml_body;
+}
+
+#ifdef WIN32
+static const char *set_interpreter_source(cmd_parms *cmd, core_dir_config *d,
+ char *arg)
+{
+ if (!strcasecmp(arg, "registry")) {
+ d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY;
+ } else if (!strcasecmp(arg, "registry-strict")) {
+ d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY_STRICT;
+ } else if (!strcasecmp(arg, "script")) {
+ d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG;
+ } else {
+ return apr_pstrcat(cmd->temp_pool, "ScriptInterpreterSource \"", arg,
+ "\" must be \"registry\", \"registry-strict\" or "
+ "\"script\"", NULL);
+ }
+ return NULL;
+}
+#endif
+
+#if !defined (RLIMIT_CPU) || !(defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)) || !defined (RLIMIT_NPROC)
+static const char *no_set_limit(cmd_parms *cmd, void *conf_,
+ const char *arg, const char *arg2)
+{
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, cmd->server,
+ "%s not supported on this platform", cmd->cmd->name);
+ return NULL;
+}
+#endif
+
+#ifdef RLIMIT_CPU
+static const char *set_limit_cpu(cmd_parms *cmd, void *conf_,
+ const char *arg, const char *arg2)
+{
+ core_dir_config *conf=conf_;
+
+ unixd_set_rlimit(cmd, &conf->limit_cpu, arg, arg2, RLIMIT_CPU);
+ return NULL;
+}
+#endif
+
+#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)
+static const char *set_limit_mem(cmd_parms *cmd, void *conf_,
+ const char *arg, const char * arg2)
+{
+ core_dir_config *conf=conf_;
+
+#if defined(RLIMIT_AS)
+ unixd_set_rlimit(cmd, &conf->limit_mem, arg, arg2 ,RLIMIT_AS);
+#elif defined(RLIMIT_DATA)
+ unixd_set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_DATA);
+#elif defined(RLIMIT_VMEM)
+ unixd_set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_VMEM);
+#endif
+ return NULL;
+}
+#endif
+
+#ifdef RLIMIT_NPROC
+static const char *set_limit_nproc(cmd_parms *cmd, void *conf_,
+ const char *arg, const char * arg2)
+{
+ core_dir_config *conf=conf_;
+
+ unixd_set_rlimit(cmd, &conf->limit_nproc, arg, arg2, RLIMIT_NPROC);
+ return NULL;
+}
+#endif
+
+static apr_status_t writev_it_all(apr_socket_t *s,
+ struct iovec *vec, int nvec,
+ apr_size_t len, apr_size_t *nbytes)
+{
+ apr_size_t bytes_written = 0;
+ apr_status_t rv;
+ apr_size_t n = len;
+ apr_size_t i = 0;
+
+ *nbytes = 0;
+
+ /* XXX handle checking for non-blocking socket */
+ while (bytes_written != len) {
+ rv = apr_sendv(s, vec + i, nvec - i, &n);
+ bytes_written += n;
+ if (rv != APR_SUCCESS)
+ return rv;
+ *nbytes += n;
+
+ /* If the write did not complete, adjust the iovecs and issue
+ * apr_sendv again
+ */
+ if (bytes_written < len) {
+ /* Skip over the vectors that have already been written */
+ apr_size_t cnt = vec[i].iov_len;
+ while (n >= cnt && i + 1 < nvec) {
+ i++;
+ cnt += vec[i].iov_len;
+ }
+ if (n < cnt) {
+ /* Handle partial write of vec i */
+ vec[i].iov_base = (char *) vec[i].iov_base +
+ (vec[i].iov_len - (cnt - n));
+ vec[i].iov_len = cnt -n;
+ }
+ }
+ n = len - bytes_written;
+ }
+
+ return APR_SUCCESS;
+}
+
+/* sendfile_it_all()
+ * send the entire file using sendfile()
+ * handle partial writes
+ * return only when all bytes have been sent or an error is encountered.
+ */
+
+#if APR_HAS_SENDFILE
+static apr_status_t sendfile_it_all(conn_rec *c,
+ apr_file_t *fd,
+ apr_hdtr_t *hdtr,
+ apr_off_t file_offset,
+ apr_size_t file_bytes_left,
+ apr_size_t total_bytes_left,
+ apr_int32_t flags)
+{
+ apr_status_t rv;
+#ifdef AP_DEBUG
+ apr_int32_t timeout = 0;
+#endif
+
+ AP_DEBUG_ASSERT((apr_getsocketopt(c->client_socket, APR_SO_TIMEOUT,
+ &timeout) == APR_SUCCESS) &&
+ timeout > 0); /* socket must be in timeout mode */
+ do {
+ apr_size_t tmplen = file_bytes_left;
+
+ rv = apr_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen,
+ flags);
+ total_bytes_left -= tmplen;
+ if (!total_bytes_left || rv != APR_SUCCESS) {
+ return rv; /* normal case & error exit */
+ }
+
+ AP_DEBUG_ASSERT(total_bytes_left > 0 && tmplen > 0);
+
+ /* partial write, oooh noooo...
+ * Skip over any header data which was written
+ */
+ while (tmplen && hdtr->numheaders) {
+ if (tmplen >= hdtr->headers[0].iov_len) {
+ tmplen -= hdtr->headers[0].iov_len;
+ --hdtr->numheaders;
+ ++hdtr->headers;
+ }
+ else {
+ char *iov_base = (char *)hdtr->headers[0].iov_base;
+
+ hdtr->headers[0].iov_len -= tmplen;
+ iov_base += tmplen;
+ hdtr->headers[0].iov_base = iov_base;
+ tmplen = 0;
+ }
+ }
+
+ /* Skip over any file data which was written */
+
+ if (tmplen <= file_bytes_left) {
+ file_offset += tmplen;
+ file_bytes_left -= tmplen;
+ continue;
+ }
+ tmplen -= file_bytes_left;
+ file_bytes_left = 0;
+ file_offset = 0;
+
+ /* Skip over any trailer data which was written */
+
+ while (tmplen && hdtr->numtrailers) {
+ if (tmplen >= hdtr->trailers[0].iov_len) {
+ tmplen -= hdtr->trailers[0].iov_len;
+ --hdtr->numtrailers;
+ ++hdtr->trailers;
+ }
+ else {
+ char *iov_base = (char *)hdtr->trailers[0].iov_base;
+
+ hdtr->trailers[0].iov_len -= tmplen;
+ iov_base += tmplen;
+ hdtr->trailers[0].iov_base = iov_base;
+ tmplen = 0;
+ }
+ }
+ } while (1);
+}
+#endif
+
+/*
+ * send_the_file()
+ * Sends the contents of file fd along with header/trailer bytes, if any,
+ * to the network. send_the_file will return only when all the bytes have been
+ * sent (i.e., it handles partial writes) or on a network error condition.
+ */
+static apr_status_t send_the_file(conn_rec *c, apr_file_t *fd,
+ apr_hdtr_t *hdtr, apr_off_t offset,
+ apr_size_t length, apr_size_t *nbytes)
+{
+ apr_status_t rv = APR_SUCCESS;
+ apr_int32_t togo; /* Remaining number of bytes in the file to send */
+ apr_size_t sendlen = 0;
+ apr_size_t bytes_sent;
+ apr_int32_t i;
+ apr_off_t o; /* Track the file offset for partial writes */
+ char buffer[8192];
+
+ *nbytes = 0;
+
+ /* Send the headers
+ * writev_it_all handles partial writes.
+ * XXX: optimization... if headers are less than MIN_WRITE_SIZE, copy
+ * them into buffer
+ */
+ if ( hdtr && hdtr->numheaders > 0 ) {
+ for (i = 0; i < hdtr->numheaders; i++) {
+ sendlen += hdtr->headers[i].iov_len;
+ }
+ rv = writev_it_all(c->client_socket, hdtr->headers, hdtr->numheaders,
+ sendlen, &bytes_sent);
+ if (rv == APR_SUCCESS)
+ *nbytes += bytes_sent; /* track total bytes sent */
+ }
+
+ /* Seek the file to 'offset' */
+ if (offset != 0 && rv == APR_SUCCESS) {
+ rv = apr_file_seek(fd, APR_SET, &offset);
+ }
+
+ /* Send the file, making sure to handle partial writes */
+ togo = length;
+ while (rv == APR_SUCCESS && togo) {
+ sendlen = togo > sizeof(buffer) ? sizeof(buffer) : togo;
+ o = 0;
+ rv = apr_file_read(fd, buffer, &sendlen);
+ while (rv == APR_SUCCESS && sendlen) {
+ bytes_sent = sendlen;
+ rv = apr_send(c->client_socket, &buffer[o], &bytes_sent);
+ if (rv == APR_SUCCESS) {
+ sendlen -= bytes_sent; /* sendlen != bytes_sent ==> partial write */
+ o += bytes_sent; /* o is where we are in the buffer */
+ *nbytes += bytes_sent;
+ togo -= bytes_sent; /* track how much of the file we've sent */
+ }
+ }
+ }
+
+ /* Send the trailers
+ * XXX: optimization... if it will fit, send this on the last send in the
+ * loop above
+ */
+ sendlen = 0;
+ if ( rv == APR_SUCCESS && hdtr && hdtr->numtrailers > 0 ) {
+ for (i = 0; i < hdtr->numtrailers; i++) {
+ sendlen += hdtr->trailers[i].iov_len;
+ }
+ rv = writev_it_all(c->client_socket, hdtr->trailers, hdtr->numtrailers,
+ sendlen, &bytes_sent);
+ if (rv == APR_SUCCESS)
+ *nbytes += bytes_sent;
+ }
+
+ return rv;
+}
+
+/* Note --- ErrorDocument will now work from .htaccess files.
+ * The AllowOverride of Fileinfo allows webmasters to turn it off
+ */
+
+static const command_rec core_cmds[] = {
+
+/* Old access config file commands */
+
+AP_INIT_RAW_ARGS("<Directory", dirsection, NULL, RSRC_CONF,
+ "Container for directives affecting resources located in the specified "
+ "directories"),
+AP_INIT_RAW_ARGS("<Location", urlsection, NULL, RSRC_CONF,
+ "Container for directives affecting resources accessed through the "
+ "specified URL paths"),
+AP_INIT_RAW_ARGS("<VirtualHost", virtualhost_section, NULL, RSRC_CONF,
+ "Container to map directives to a particular virtual host, takes one or "
+ "more host addresses"),
+AP_INIT_RAW_ARGS("<Files", filesection, NULL, OR_ALL,
+ "Container for directives affecting files matching specified patterns"),
+AP_INIT_RAW_ARGS("<Limit", ap_limit_section, NULL, OR_ALL,
+ "Container for authentication directives when accessed using specified HTTP "
+ "methods"),
+AP_INIT_RAW_ARGS("<LimitExcept", ap_limit_section, (void*)1, OR_ALL,
+ "Container for authentication directives to be applied when any HTTP "
+ "method other than those specified is used to access the resource"),
+AP_INIT_TAKE1("<IfModule", start_ifmod, NULL, EXEC_ON_READ | OR_ALL,
+ "Container for directives based on existance of specified modules"),
+AP_INIT_TAKE1("<IfDefine", start_ifdefine, NULL, EXEC_ON_READ | OR_ALL,
+ "Container for directives based on existance of command line defines"),
+AP_INIT_RAW_ARGS("<DirectoryMatch", dirsection, (void*)1, RSRC_CONF,
+ "Container for directives affecting resources located in the "
+ "specified directories"),
+AP_INIT_RAW_ARGS("<LocationMatch", urlsection, (void*)1, RSRC_CONF,
+ "Container for directives affecting resources accessed through the "
+ "specified URL paths"),
+AP_INIT_RAW_ARGS("<FilesMatch", filesection, (void*)1, OR_ALL,
+ "Container for directives affecting files matching specified patterns"),
+AP_INIT_TAKE1("AuthType", ap_set_string_slot,
+ (void*)XtOffsetOf(core_dir_config, ap_auth_type), OR_AUTHCFG,
+ "An HTTP authorization type (e.g., \"Basic\")"),
+AP_INIT_TAKE1("AuthName", set_authname, NULL, OR_AUTHCFG,
+ "The authentication realm (e.g. \"Members Only\")"),
+AP_INIT_RAW_ARGS("Require", require, NULL, OR_AUTHCFG,
+ "Selects which authenticated users or groups may access a protected space"),
+AP_INIT_TAKE1("Satisfy", satisfy, NULL, OR_AUTHCFG,
+ "access policy if both allow and require used ('all' or 'any')"),
+#ifdef GPROF
+AP_INIT_TAKE1("GprofDir", set_gprof_dir, NULL, RSRC_CONF,
+ "Directory to plop gmon.out files"),
+#endif
+AP_INIT_TAKE1("AddDefaultCharset", set_add_default_charset, NULL, OR_FILEINFO,
+ "The name of the default charset to add to any Content-Type without one or 'Off' to disable"),
+
+/* Old resource config file commands */
+
+AP_INIT_RAW_ARGS("AccessFileName", set_access_name, NULL, RSRC_CONF,
+ "Name(s) of per-directory config files (default: .htaccess)"),
+AP_INIT_TAKE1("DocumentRoot", set_document_root, NULL, RSRC_CONF,
+ "Root directory of the document tree"),
+AP_INIT_TAKE2("ErrorDocument", set_error_document, NULL, OR_FILEINFO,
+ "Change responses for HTTP errors"),
+AP_INIT_RAW_ARGS("AllowOverride", set_override, NULL, ACCESS_CONF,
+ "Controls what groups of directives can be configured by per-directory "
+ "config files"),
+AP_INIT_RAW_ARGS("Options", set_options, NULL, OR_OPTIONS,
+ "Set a number of attributes for a given directory"),
+AP_INIT_TAKE1("DefaultType", ap_set_string_slot,
+ (void*)XtOffsetOf (core_dir_config, ap_default_type),
+ OR_FILEINFO, "the default MIME type for untypable files"),
+
+/* Old server config file commands */
+
+AP_INIT_TAKE1("Port", server_port, NULL, RSRC_CONF, "A TCP port number"),
+AP_INIT_TAKE1("HostnameLookups", set_hostname_lookups, NULL,
+ ACCESS_CONF|RSRC_CONF,
+ "\"on\" to enable, \"off\" to disable reverse DNS lookups, or \"double\" to "
+ "enable double-reverse DNS lookups"),
+AP_INIT_TAKE1("ServerAdmin", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, server_admin), RSRC_CONF,
+ "The email address of the server administrator"),
+AP_INIT_TAKE1("ServerName", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, server_hostname), RSRC_CONF,
+ "The hostname of the server"),
+AP_INIT_TAKE1("ServerSignature", set_signature_flag, NULL, OR_ALL,
+ "En-/disable server signature (on|off|email)"),
+AP_INIT_TAKE1("ServerRoot", set_server_root, NULL, RSRC_CONF,
+ "Common directory of server-related files (logs, confs, etc.)"),
+AP_INIT_TAKE1("ErrorLog", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, error_fname), RSRC_CONF,
+ "The filename of the error log"),
+AP_INIT_RAW_ARGS("ServerAlias", set_server_alias, NULL, RSRC_CONF,
+ "A name or names alternately used to access the server"),
+AP_INIT_TAKE1("ServerPath", set_serverpath, NULL, RSRC_CONF,
+ "The pathname the server can be reached at"),
+AP_INIT_TAKE1("Timeout", set_timeout, NULL, RSRC_CONF,
+ "Timeout duration (sec)"),
+AP_INIT_FLAG("IdentityCheck", set_idcheck, NULL, RSRC_CONF|ACCESS_CONF,
+ "Enable identd (RFC 1413) user lookups - SLOW"),
+AP_INIT_FLAG("ContentDigest", set_content_md5, NULL, OR_OPTIONS,
+ "whether or not to send a Content-MD5 header with each request"),
+AP_INIT_TAKE1("UseCanonicalName", set_use_canonical_name, NULL,
+ RSRC_CONF|ACCESS_CONF,
+ "How to work out the ServerName : Port when constructing URLs"),
+/* TODO: RlimitFoo should all be part of mod_cgi, not in the core */
+/* TODO: ListenBacklog in MPM */
+AP_INIT_TAKE1("Include", include_config, NULL,
+ (RSRC_CONF | ACCESS_CONF | EXEC_ON_READ),
+ "Name of the config file to be included"),
+AP_INIT_TAKE1("LogLevel", set_loglevel, NULL, RSRC_CONF,
+ "Level of verbosity in error logging"),
+AP_INIT_TAKE1("NameVirtualHost", ap_set_name_virtual_host, NULL, RSRC_CONF,
+ "A numeric IP address:port, or the name of a host"),
+#ifdef _OSD_POSIX
+AP_INIT_TAKE1("BS2000Account", set_bs2000_account, NULL, RSRC_CONF,
+ "Name of server User's bs2000 logon account name"),
+#endif
+#ifdef WIN32
+AP_INIT_TAKE1("ScriptInterpreterSource", set_interpreter_source, NULL,
+ OR_FILEINFO,
+ "Where to find interpreter to run Win32 scripts (Registry or script shebang line)"),
+#endif
+AP_INIT_TAKE1("ServerTokens", set_serv_tokens, NULL, RSRC_CONF,
+ "Determine tokens displayed in the Server: header - Min(imal), OS or Full"),
+AP_INIT_TAKE1("LimitRequestLine", set_limit_req_line, NULL, RSRC_CONF,
+ "Limit on maximum size of an HTTP request line"),
+AP_INIT_TAKE1("LimitRequestFieldsize", set_limit_req_fieldsize, NULL,
+ RSRC_CONF,
+ "Limit on maximum size of an HTTP request header field"),
+AP_INIT_TAKE1("LimitRequestFields", set_limit_req_fields, NULL, RSRC_CONF,
+ "Limit (0 = unlimited) on max number of header fields in a request message"),
+AP_INIT_TAKE1("LimitRequestBody", set_limit_req_body,
+ (void*)XtOffsetOf(core_dir_config, limit_req_body), OR_ALL,
+ "Limit (in bytes) on maximum size of request message body"),
+AP_INIT_TAKE1("LimitXMLRequestBody", set_limit_xml_req_body, NULL, OR_ALL,
+ "Limit (in bytes) on maximum size of an XML-based request "
+ "body"),
+
+/* System Resource Controls */
+#ifdef RLIMIT_CPU
+AP_INIT_TAKE12("RLimitCPU", set_limit_cpu,
+ (void*)XtOffsetOf(core_dir_config, limit_cpu),
+ OR_ALL, "Soft/hard limits for max CPU usage in seconds"),
+#else
+AP_INIT_TAKE12("RLimitCPU", no_set_limit, NULL,
+ OR_ALL, "Soft/hard limits for max CPU usage in seconds"),
+#endif
+#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined (RLIMIT_AS)
+AP_INIT_TAKE12("RLimitMEM", set_limit_mem,
+ (void*)XtOffsetOf(core_dir_config, limit_mem),
+ OR_ALL, "Soft/hard limits for max memory usage per process"),
+#else
+AP_INIT_TAKE12("RLimitMEM", no_set_limit, NULL,
+ OR_ALL, "Soft/hard limits for max memory usage per process"),
+#endif
+#ifdef RLIMIT_NPROC
+AP_INIT_TAKE12("RLimitNPROC", set_limit_nproc,
+ (void*)XtOffsetOf(core_dir_config, limit_nproc),
+ OR_ALL, "soft/hard limits for max number of processes per uid"),
+#else
+AP_INIT_TAKE12("RLimitNPROC", no_set_limit, NULL,
+ OR_ALL, "soft/hard limits for max number of processes per uid"),
+#endif
+/* XXX These should be allowable in .htaccess files, but currently it won't
+ * play well with the Options stuff. Until that is fixed, I would prefer
+ * to leave it just in the conf file. Other should feel free to disagree
+ * with me. Rbb.
+ */
+AP_INIT_ITERATE("SetOutputFilter", add_filter, NULL, ACCESS_CONF,
+ "filters to be run"),
+AP_INIT_ITERATE("SetInputFilter", add_input_filter, NULL, ACCESS_CONF,
+ "filters to be run on the request body"),
+{ NULL }
+};
+
+/*****************************************************************
+ *
+ * Core handlers for various phases of server operation...
+ */
+
+AP_DECLARE_NONSTD(int) ap_core_translate(request_rec *r)
+{
+ void *sconf = r->server->module_config;
+ core_server_config *conf = ap_get_module_config(sconf, &core_module);
+
+ if (r->proxyreq) {
+ return HTTP_FORBIDDEN;
+ }
+ if ((r->uri[0] != '/') && strcmp(r->uri, "*")) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+ "Invalid URI in request %s", r->the_request);
+ return HTTP_BAD_REQUEST;
+ }
+
+ if (r->server->path
+ && !strncmp(r->uri, r->server->path, r->server->pathlen)
+ && (r->server->path[r->server->pathlen - 1] == '/'
+ || r->uri[r->server->pathlen] == '/'
+ || r->uri[r->server->pathlen] == '\0')) {
+ r->filename = apr_pstrcat(r->pool, conf->ap_document_root,
+ (r->uri + r->server->pathlen), NULL);
+ }
+ else {
+ /*
+ * Make sure that we do not mess up the translation by adding two
+ * /'s in a row. This happens under windows when the document
+ * root ends with a /
+ */
+ if ((conf->ap_document_root[strlen(conf->ap_document_root)-1] == '/')
+ && (*(r->uri) == '/')) {
+ r->filename = apr_pstrcat(r->pool, conf->ap_document_root, r->uri+1,
+ NULL);
+ }
+ else {
+ r->filename = apr_pstrcat(r->pool, conf->ap_document_root, r->uri,
+ NULL);
+ }
+ }
+
+ return OK;
+}
+
+static int do_nothing(request_rec *r) { return OK; }
+
+static int default_handler(request_rec *r)
+{
+ apr_bucket_brigade *bb;
+ apr_bucket *e;
+ core_dir_config *d;
+ int errstatus;
+ apr_file_t *fd = NULL;
+ apr_status_t status;
+ /* XXX if/when somebody writes a content-md5 filter we either need to
+ * remove this support or coordinate when to use the filter vs.
+ * when to use this code
+ * The current choice of when to compute the md5 here matches the 1.3
+ * support fairly closely (unlike 1.3, we don't handle computing md5
+ * when the charset is translated).
+ */
+ int bld_content_md5;
+
+ /*
+ * The old way of doing handlers meant that this handler would
+ * match literally anything - this way will require handler to
+ * have a / in the middle, which probably captures the original
+ * intent, but may cause problems at first - Ben 7th Jan 01
+ */
+ if(strcmp(r->handler,"default-handler")
+ && ap_strcmp_match(r->handler,"*/*"))
+ return DECLINED;
+
+ d = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+ bld_content_md5 = (d->content_md5 & 1)
+ && r->output_filters->frec->ftype != AP_FTYPE_CONTENT;
+
+ ap_allow_methods(r, MERGE_ALLOW, "GET", "OPTIONS", "POST", NULL);
+
+ if ((errstatus = ap_discard_request_body(r)) != OK) {
+ return errstatus;
+ }
+
+ if (r->method_number == M_INVALID) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+ "Invalid method in request %s", r->the_request);
+ return HTTP_NOT_IMPLEMENTED;
+ }
+ if (r->method_number == M_OPTIONS) {
+ return ap_send_http_options(r);
+ }
+ if (r->method_number == M_PUT) {
+ return HTTP_METHOD_NOT_ALLOWED;
+ }
+ if (r->finfo.filetype == 0 || (r->path_info && *r->path_info)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
+ "File does not exist: %s",r->path_info ?
+ apr_pstrcat(r->pool, r->filename, r->path_info, NULL)
+ : r->filename);
+ return HTTP_NOT_FOUND;
+ }
+
+ if (r->method_number != M_GET && r->method_number != M_POST) {
+ return HTTP_METHOD_NOT_ALLOWED;
+ }
+
+ if ((status = apr_file_open(&fd, r->filename, APR_READ | APR_BINARY, 0, r->connection->pool)) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
+ "file permissions deny server access: %s", r->filename);
+ return HTTP_FORBIDDEN;
+ }
+ ap_update_mtime(r, r->finfo.mtime);
+ ap_set_last_modified(r);
+ ap_set_etag(r);
+ apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
+ ap_set_content_length(r, r->finfo.size);
+ if ((errstatus = ap_meets_conditions(r)) != OK) {
+ apr_file_close(fd);
+ return errstatus;
+ }
+
+ if (bld_content_md5) {
+ apr_table_setn(r->headers_out, "Content-MD5",
+ ap_md5digest(r->pool, fd));
+ }
+
+ bb = apr_brigade_create(r->pool);
+ e = apr_bucket_file_create(fd, 0, r->finfo.size);
+
+ APR_BRIGADE_INSERT_HEAD(bb, e);
+ e = apr_bucket_eos_create();
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+
+ return ap_pass_brigade(r->output_filters, bb);
+}
+
+static int core_input_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode)
+{
+ apr_bucket *e;
+
+ if (!f->ctx) { /* If we haven't passed up the socket yet... */
+ f->ctx = (void *)1;
+ e = apr_bucket_socket_create(f->c->client_socket);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ return APR_SUCCESS;
+ }
+ else {
+ /* Either some code lost track of the socket
+ * bucket or we already found out that the
+ * client closed.
+ */
+ return APR_EOF;
+ }
+}
+
+/* Default filter. This filter should almost always be used. Its only job
+ * is to send the headers if they haven't already been sent, and then send
+ * the actual data.
+ */
+typedef struct CORE_OUTPUT_FILTER_CTX {
+ apr_bucket_brigade *b;
+} core_output_filter_ctx_t;
+
+#define MAX_IOVEC_TO_WRITE 16
+
+static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
+{
+ apr_status_t rv;
+ conn_rec *c = f->c;
+ core_output_filter_ctx_t *ctx = f->ctx;
+
+ if (ctx == NULL) {
+ f->ctx = ctx = apr_pcalloc(c->pool, sizeof(core_output_filter_ctx_t));
+ }
+
+ /* If we have a saved brigade, concatenate the new brigade to it */
+ if (ctx->b) {
+ APR_BRIGADE_CONCAT(ctx->b, b);
+ b = ctx->b;
+ ctx->b = NULL;
+ }
+
+ /* Perform multiple passes over the brigade, sending batches of output
+ to the connection. */
+ while (b) {
+ apr_size_t nbytes = 0;
+ apr_bucket *e;
+
+ /* tail of brigade if we need another pass */
+ apr_bucket_brigade *more = NULL;
+
+ /* one group of iovecs per pass over the brigade */
+ apr_size_t nvec = 0;
+ apr_size_t nvec_trailers = 0;
+ struct iovec vec[MAX_IOVEC_TO_WRITE];
+ struct iovec vec_trailers[MAX_IOVEC_TO_WRITE];
+
+ /* one file per pass over the brigade */
+ apr_file_t *fd = NULL;
+ apr_size_t flen = 0;
+ apr_off_t foffset = 0;
+
+ /* Iterate over the brigade: collect iovecs and/or a file */
+ APR_BRIGADE_FOREACH(e, b) {
+ if (APR_BUCKET_IS_EOS(e) || APR_BUCKET_IS_FLUSH(e)) {
+ break;
+ }
+ /* It doesn't make any sense to use sendfile for a file bucket
+ * that represents 10 bytes.
+ */
+ else if (APR_BUCKET_IS_FILE(e)
+ && (e->length >= AP_MIN_SENDFILE_BYTES)) {
+ apr_bucket_file *a = e->data;
+
+ /* We can't handle more than one file bucket at a time
+ * so we split here and send the file we have already
+ * found.
+ */
+ if (fd) {
+ more = apr_brigade_split(b, e);
+ break;
+ }
+
+ fd = a->fd;
+ flen = e->length;
+ foffset = e->start;
+ }
+ else {
+ const char *str;
+ apr_size_t n;
+
+ rv = apr_bucket_read(e, &str, &n, APR_BLOCK_READ);
+ if (n) {
+ nbytes += n;
+ if (!fd) {
+ vec[nvec].iov_base = (char*) str;
+ vec[nvec].iov_len = n;
+ nvec++;
+ }
+ else {
+ /* The bucket is a trailer to a file bucket */
+ vec_trailers[nvec_trailers].iov_base = (char*) str;
+ vec_trailers[nvec_trailers].iov_len = n;
+ nvec_trailers++;
+ }
+ }
+ }
+
+ if ((nvec == MAX_IOVEC_TO_WRITE) ||
+ (nvec_trailers == MAX_IOVEC_TO_WRITE)) {
+ /* Split the brigade and break */
+ if (APR_BUCKET_NEXT(e) != APR_BRIGADE_SENTINEL(b)) {
+ more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
+ }
+ break;
+ }
+ }
+
+ /* Completed iterating over the brigades, now determine if we want
+ * to buffer the brigade or send the brigade out on the network.
+ *
+ * Save if:
+ *
+ * 1) we didn't see a file, we don't have more passes over the
+ * brigade to perform, we haven't accumulated enough bytes to
+ * send, AND we didn't stop at a FLUSH bucket.
+ * (IOW, we will save away plain old bytes)
+ * or
+ * 2) we hit the EOS and have a keep-alive connection
+ * (IOW, this response is a bit more complex, but we save it
+ * with the hope of concatenating with another response)
+ */
+ if ((!fd && !more &&
+ (nbytes < AP_MIN_BYTES_TO_WRITE) && !APR_BUCKET_IS_FLUSH(e))
+ || (APR_BUCKET_IS_EOS(e) && c->keepalive)) {
+
+ /* NEVER save an EOS in here. If we are saving a brigade with
+ * an EOS bucket, then we are doing keepalive connections, and
+ * we want to process to second request fully.
+ */
+ if (APR_BUCKET_IS_EOS(e)) {
+ apr_bucket_delete(e);
+ }
+ ap_save_brigade(f, &ctx->b, &b);
+ return APR_SUCCESS;
+ }
+
+ if (fd) {
+ apr_hdtr_t hdtr;
+#if APR_HAS_SENDFILE
+ apr_int32_t flags = 0;
+#endif
+
+ memset(&hdtr, '\0', sizeof(hdtr));
+ if (nvec) {
+ hdtr.numheaders = nvec;
+ hdtr.headers = vec;
+ }
+ if (nvec_trailers) {
+ hdtr.numtrailers = nvec_trailers;
+ hdtr.trailers = vec_trailers;
+ }
+#if APR_HAS_SENDFILE
+ if (!c->keepalive) {
+ /* Prepare the socket to be reused */
+ flags |= APR_SENDFILE_DISCONNECT_SOCKET;
+ }
+ rv = sendfile_it_all(c, /* the connection */
+ fd, /* the file to send */
+ &hdtr, /* header and trailer iovecs */
+ foffset, /* offset in the file to begin
+ sending from */
+ flen, /* length of file */
+ nbytes + flen, /* total length including
+ headers */
+ flags); /* apr_sendfile flags */
+
+ /* If apr_sendfile() returns APR_ENOTIMPL, call send_the_file()
+ * to loop on apr_file_read/apr_send to send the file. Our Windows
+ * binary distributions (which work on Windows 9x/NT) are
+ * compiled on Windows NT. TransmitFile is not available on
+ * Windows 95/98 and we discover this at runtime when
+ * apr_sendfile() returns APR_ENOTIMPL. Having apr_sendfile()
+ * return APR_ENOTIMPL seems the cleanest way to handle this
+ * case.
+ */
+ if (rv == APR_ENOTIMPL)
+#endif
+ {
+ apr_size_t unused_bytes_sent;
+
+ rv = send_the_file(c, fd, &hdtr, foffset, flen,
+ &unused_bytes_sent);
+ }
+ fd = NULL;
+ }
+ else {
+ apr_size_t unused_bytes_sent;
+
+ rv = writev_it_all(c->client_socket,
+ vec, nvec,
+ nbytes, &unused_bytes_sent);
+ }
+
+ apr_brigade_destroy(b);
+ if (rv != APR_SUCCESS) {
+ /* XXX: log the error */
+ if (more)
+ apr_brigade_destroy(more);
+ return rv;
+ }
+
+ b = more;
+ more = NULL;
+ } /* end while () */
+
+ return APR_SUCCESS;
+}
+
+static void core_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
+{
+ ap_set_version(pconf);
+}
+
+static void core_open_logs(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
+{
+ ap_open_logs(s, pconf);
+}
+
+static void core_insert_filter(request_rec *r)
+{
+ int i;
+ core_dir_config *conf = (core_dir_config *)
+ ap_get_module_config(r->per_dir_config,
+ &core_module);
+ char **items = (char **)conf->output_filters->elts;
+
+ for (i = 0; i < conf->output_filters->nelts; i++) {
+ char *foobar = items[i];
+ ap_add_output_filter(foobar, NULL, r, r->connection);
+ }
+
+ items = (char **)conf->input_filters->elts;
+ for (i = 0; i < conf->input_filters->nelts; i++) {
+ char *foobar = items[i];
+ ap_add_input_filter(foobar, NULL, r, r->connection);
+ }
+}
+
+static void register_hooks(apr_pool_t *p)
+{
+ ap_hook_post_config(core_post_config,NULL,NULL,APR_HOOK_REALLY_FIRST);
+ ap_hook_translate_name(ap_core_translate,NULL,NULL,APR_HOOK_REALLY_LAST);
+ ap_hook_open_logs(core_open_logs,NULL,NULL,APR_HOOK_MIDDLE);
+ ap_hook_handler(default_handler,NULL,NULL,APR_HOOK_REALLY_LAST);
+ /* FIXME: I suspect we can eliminate the need for these - Ben */
+ ap_hook_type_checker(do_nothing,NULL,NULL,APR_HOOK_REALLY_LAST);
+ ap_hook_access_checker(do_nothing,NULL,NULL,APR_HOOK_REALLY_LAST);
+
+ /* register the core's insert_filter hook and register core-provided
+ * filters
+ */
+ ap_hook_insert_filter(core_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
+
+ ap_register_input_filter("CORE_IN", core_input_filter, AP_FTYPE_NETWORK);
+ ap_register_output_filter("CONTENT_LENGTH", ap_content_length_filter,
+ AP_FTYPE_HTTP_HEADER);
+ ap_register_output_filter("BYTERANGE", ap_byterange_filter,
+ AP_FTYPE_HTTP_HEADER);
+ ap_register_output_filter("CORE", core_output_filter, AP_FTYPE_NETWORK);
+ ap_register_output_filter("SUBREQ_CORE", ap_sub_req_output_filter,
+ AP_FTYPE_CONTENT);
+ ap_register_output_filter("OLD_WRITE", ap_old_write_filter,
+ AP_FTYPE_CONTENT - 1);
+}
+
+AP_DECLARE_DATA module core_module = {
+ STANDARD20_MODULE_STUFF,
+ create_core_dir_config, /* create per-directory config structure */
+ merge_core_dir_configs, /* merge per-directory config structures */
+ create_core_server_config, /* create per-server config structure */
+ merge_core_server_configs, /* merge per-server config structures */
+ core_cmds, /* command apr_table_t */
+ register_hooks /* register hooks */
+};
diff --git a/server/error_bucket.c b/server/error_bucket.c
new file mode 100644
index 0000000000..071119882b
--- /dev/null
+++ b/server/error_bucket.c
@@ -0,0 +1,104 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+#include "http_protocol.h"
+#include "apr_buckets.h"
+#include "apr_strings.h"
+#include <stdlib.h>
+#if APR_HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+static apr_status_t error_read(apr_bucket *b, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ *str = NULL;
+ *len = 0;
+ return APR_SUCCESS;
+}
+
+AP_DECLARE(apr_bucket *) ap_bucket_error_make(apr_bucket *b, int error,
+ const char *buf, apr_pool_t *p)
+{
+ ap_bucket_error *h;
+
+ h = malloc(sizeof(*h));
+ if (h == NULL) {
+ return NULL;
+ }
+ h->status = error;
+ if (buf) {
+ h->start = apr_pstrdup(p, buf);
+ }
+
+ b->length = 0;
+ b->type = &ap_bucket_type_error;
+ b->data = h;
+ return b;
+}
+
+AP_DECLARE(apr_bucket *) ap_bucket_error_create(int error,
+ const char *buf, apr_pool_t *p)
+{
+ apr_bucket_do_create(ap_bucket_error_make(b, error, buf, p));
+}
+
+AP_DECLARE_DATA const apr_bucket_type_t ap_bucket_type_error = {
+ "ERROR", 5,
+ free,
+ error_read,
+ apr_bucket_setaside_notimpl,
+ apr_bucket_split_notimpl,
+ apr_bucket_copy_notimpl
+};
diff --git a/server/mpm/beos/.cvsignore b/server/mpm/beos/.cvsignore
new file mode 100644
index 0000000000..84df257214
--- /dev/null
+++ b/server/mpm/beos/.cvsignore
@@ -0,0 +1,5 @@
+.deps
+.libs
+*.lo
+*.la
+Makefile
diff --git a/server/mpm/beos/Makefile.in b/server/mpm/beos/Makefile.in
new file mode 100644
index 0000000000..ba75075533
--- /dev/null
+++ b/server/mpm/beos/Makefile.in
@@ -0,0 +1,5 @@
+
+LTLIBRARY_NAME = libbeos.la
+LTLIBRARY_SOURCES = beos.c scoreboard.c
+
+include $(top_srcdir)/build/ltlib.mk
diff --git a/server/mpm/beos/beos.c b/server/mpm/beos/beos.c
new file mode 100644
index 0000000000..b70576bb16
--- /dev/null
+++ b/server/mpm/beos/beos.c
@@ -0,0 +1,1134 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/* The new BeOS MPM!
+ *
+ * This one basically is a single process multi threaded model, but
+ * I couldn't be bothered adding the spmt_ to the front of the name!
+ * Anyway, this is still under development so it isn't yet the default
+ * choice.
+ */
+
+#define CORE_PRIVATE
+
+#include "apr_portable.h"
+#include "httpd.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_config.h" /* for read_config */
+#include "http_core.h" /* for get_remote_host */
+#include "http_connection.h"
+#include "ap_mpm.h"
+#include "beosd.h"
+#include "iol_socket.h"
+#include "ap_listen.h"
+#include "scoreboard.h"
+//#include "poll.h"
+#include "mpm_common.h"
+#include "mpm.h"
+#include <unistd.h>
+#include <sys/socket.h>
+
+/*
+ * Actual definitions of config globals
+ */
+
+int ap_threads_per_child=0; /* Worker threads per child */
+int ap_max_requests_per_child=0;
+static char *ap_pid_fname=NULL;
+static char *ap_scoreboard_fname=NULL;
+static int ap_threads_to_start=0;
+static int min_spare_threads=0;
+static int max_spare_threads=0;
+static int ap_thread_limit=0;
+static time_t ap_restart_time=0;
+API_VAR_EXPORT int ap_extended_status = 0;
+static int num_listening_sockets = 0; /* set by open_listeners in ap_mpm_run */
+static ap_socket_t ** listening_sockets;
+ap_lock_t *accept_mutex = NULL;
+
+static ap_pool_t *pconf; /* Pool for config stuff */
+static ap_pool_t *pchild; /* Pool for httpd child stuff */
+
+static int server_pid;
+
+/* Keep track of the number of worker threads currently active */
+static int worker_thread_count;
+ap_lock_t *worker_thread_count_mutex;
+
+/* The structure used to pass unique initialization info to each thread */
+typedef struct {
+ int slot;
+ ap_pool_t *tpool;
+} proc_info;
+
+struct ap_ctable ap_child_table[HARD_SERVER_LIMIT];
+
+/*
+ * The max child slot ever assigned, preserved across restarts. Necessary
+ * to deal with MaxClients changes across SIGWINCH restarts. We use this
+ * value to optimize routines that have to scan the entire scoreboard.
+ */
+int ap_max_child_assigned = -1;
+int ap_max_threads_limit = -1;
+static char ap_coredump_dir[MAX_STRING_LEN];
+
+/* shared http_main globals... */
+
+server_rec *ap_server_conf;
+
+/* one_process */
+/* TODO - get this working again... */
+static int one_process = 0;
+
+#ifdef DEBUG_SIGSTOP
+int raise_sigstop_flags;
+#endif
+
+API_EXPORT(const server_rec *) ap_get_server_conf(void)
+{
+ return (ap_server_conf);
+}
+
+API_EXPORT(int) ap_get_max_daemons(void)
+{
+ return ap_max_child_assigned;
+}
+
+/* a clean exit from a child with proper cleanup
+ static void clean_child_exit(int code) __attribute__ ((noreturn)); */
+void clean_child_exit(int code)
+{
+ if (pchild)
+ ap_destroy_pool(pchild);
+ exit(code);
+}
+
+/* handle all varieties of core dumping signals */
+static void sig_coredump(int sig)
+{
+ chdir(ap_coredump_dir);
+ signal(sig, SIG_DFL);
+ kill(server_pid, sig);
+ /* At this point we've got sig blocked, because we're still inside
+ * the signal handler. When we leave the signal handler it will
+ * be unblocked, and we'll take the signal... and coredump or whatever
+ * is appropriate for this particular Unix. In addition the parent
+ * will see the real signal we received -- whereas if we called
+ * abort() here, the parent would only see SIGABRT.
+ */
+}
+
+/*****************************************************************
+ * Connection structures and accounting...
+ */
+
+/* volatile just in case */
+static int volatile shutdown_pending;
+static int volatile restart_pending;
+static int volatile is_graceful;
+
+/*
+ * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
+ * functions to initiate shutdown or restart without relying on signals.
+ * Previously this was initiated in sig_term() and restart() signal handlers,
+ * but we want to be able to start a shutdown/restart from other sources --
+ * e.g. on Win32, from the service manager. Now the service manager can
+ * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that
+ * these functions can also be called by the child processes, since global
+ * variables are no longer used to pass on the required action to the parent.
+ *
+ * These should only be called from the parent process itself, since the
+ * parent process will use the shutdown_pending and restart_pending variables
+ * to determine whether to shutdown or restart. The child process should
+ * call signal_parent() directly to tell the parent to die -- this will
+ * cause neither of those variable to be set, which the parent will
+ * assume means something serious is wrong (which it will be, for the
+ * child to force an exit) and so do an exit anyway.
+ */
+
+void ap_start_shutdown(void)
+{
+ if (shutdown_pending == 1) {
+ /* Um, is this _probably_ not an error, if the user has
+ * tried to do a shutdown twice quickly, so we won't
+ * worry about reporting it.
+ */
+ return;
+ }
+ shutdown_pending = 1;
+}
+
+/* do a graceful restart if graceful == 1 */
+void ap_start_restart(int graceful)
+{
+
+ if (restart_pending == 1) {
+ /* Probably not an error - don't bother reporting it */
+ return;
+ }
+ restart_pending = 1;
+ is_graceful = graceful;
+}
+
+static void sig_term(int sig)
+{
+ ap_start_shutdown();
+}
+
+static void restart(int sig)
+{
+ ap_start_restart(sig == SIGWINCH);
+}
+
+static void tell_workers_to_exit()
+{
+ int i, code = 99;
+
+ for (i=0;i<ap_max_child_assigned;i++) {
+ if (ap_child_table[i].status != SERVER_DEAD)
+ send_data(ap_child_table[i].pid, code, NULL, 0);
+
+ }
+}
+
+static void push_workers_off_cliff(int sig)
+{
+ int i;
+
+ for (i=0;i<ap_max_child_assigned;i++)
+ kill(ap_child_table[i].pid, sig);
+}
+
+static void set_signals(void)
+{
+ struct sigaction sa;
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ if (!one_process) {
+ sa.sa_handler = sig_coredump;
+
+ if (sigaction(SIGSEGV, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGSEGV)");
+ if (sigaction(SIGBUS, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGBUS)");
+ if (sigaction(SIGABRT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABRT)");
+ if (sigaction(SIGILL, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGILL)");
+ sa.sa_flags = 0;
+ }
+ sa.sa_handler = sig_term;
+ if (sigaction(SIGTERM, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
+
+ sa.sa_handler = SIG_IGN;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGPIPE)");
+
+ /* we want to ignore HUPs and WINCH while we're busy processing one */
+ sigaddset(&sa.sa_mask, SIGHUP);
+ sigaddset(&sa.sa_mask, SIGWINCH);
+ sa.sa_handler = restart;
+ if (sigaction(SIGHUP, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
+ if (sigaction(SIGWINCH, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGWINCH)");
+}
+
+static void process_child_status(int pid, ap_wait_t status)
+{
+ /* Child died... if it died due to a fatal error,
+ * we should simply bail out.
+ */
+ if ((WIFEXITED(status)) &&
+ WEXITSTATUS(status) == APEXIT_CHILDFATAL) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, errno, ap_server_conf,
+ "Child %d returned a Fatal error... \n"
+ "Apache is exiting!",
+ pid);
+ exit(APEXIT_CHILDFATAL);
+ }
+ if (WIFSIGNALED(status)) {
+ switch (WTERMSIG(status)) {
+ case SIGTERM:
+ case SIGHUP:
+ case SIGUSR1:
+ case SIGKILL:
+ break;
+ default:
+#ifdef SYS_SIGLIST
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status)) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
+ errno, ap_server_conf,
+ "child pid %d exit signal %s (%d), "
+ "possible coredump in %s",
+ pid, (WTERMSIG(status) >= NumSIG) ? "" :
+ SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status),
+ ap_coredump_dir);
+ }
+ else {
+#endif
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
+ errno, ap_server_conf,
+ "child pid %d exit signal %s (%d)", pid,
+ SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status));
+#ifdef WCOREDUMP
+ }
+#endif
+#else
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
+ errno, ap_server_conf,
+ "child pid %d exit signal %d",
+ pid, WTERMSIG(status));
+#endif
+ }
+ }
+}
+
+static int setup_listeners(server_rec *s)
+{
+ ap_listen_rec *lr;
+ int num_listeners = 0;
+ if (ap_listen_open(s->process, s->port)) {
+ return 0;
+ }
+ for (lr = ap_listeners; lr; lr = lr->next) {
+ num_listeners++;
+ }
+ return num_listeners;
+}
+
+/*****************************************************************
+ * Here follows a long bunch of generic server bookkeeping stuff...
+ */
+
+//#define sock_disable_nagle(s) /* NOOP */
+
+int ap_graceful_stop_signalled(void)
+{
+ /* XXX - Does this really work? - Manoj */
+ return is_graceful;
+}
+
+/*****************************************************************
+ * Child process main loop.
+ */
+
+static void process_socket(ap_pool_t *p, ap_socket_t *sock, int my_child_num)
+{
+ BUFF *conn_io;
+ conn_rec *current_conn;
+ ap_iol *iol;
+ long conn_id = my_child_num;
+ int csd;
+
+ iol = beos_attach_socket(sock);
+ if (iol == NULL) {
+ if (errno == EBADF) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, errno, NULL,
+ "filedescriptor (%u) larger than FD_SETSIZE (%u) "
+ "found, you probably need to rebuild Apache with a "
+ "larger FD_SETSIZE", csd, FD_SETSIZE);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, NULL,
+ "error attaching to socket");
+ }
+ ap_close_socket(sock);
+ return;
+ }
+
+ conn_io = ap_bcreate(p, B_RDWR);
+ ap_bpush_iol(conn_io, iol);
+
+ current_conn = ap_new_apr_connection(p, ap_server_conf, conn_io, sock,
+ conn_id);
+
+ ap_process_connection(current_conn);
+ ap_lingering_close(current_conn);
+}
+
+static int32 worker_thread(void * dummy)
+{
+ proc_info * ti = dummy;
+ int child_slot = ti->slot;
+ ap_pool_t *tpool = ti->tpool;
+ ap_socket_t *csd = NULL;
+ ap_pool_t *ptrans; /* Pool for per-transaction stuff */
+ ap_socket_t *sd = NULL;
+ int srv , n;
+ int curr_pollfd, last_pollfd = 0;
+ sigset_t sig_mask;
+ int requests_this_child = ap_max_requests_per_child;
+ ap_pollfd_t *pollset;
+ /* each worker thread is in control of it's own destiny...*/
+ int this_worker_should_exit = 0;
+ thread_id me = find_thread(NULL);
+
+ free(ti);
+
+ /* block the signals for this thread */
+ sigfillset(&sig_mask);
+ sigprocmask(SIG_BLOCK, &sig_mask, NULL);
+
+ ap_create_pool(&ptrans, tpool);
+
+ ap_lock(worker_thread_count_mutex);
+ worker_thread_count++;
+ ap_unlock(worker_thread_count_mutex);
+
+ /* now setup our own pollset...this will use APR woohoo! */
+ ap_setup_poll(&pollset, num_listening_sockets, tpool);
+ for(n=0 ; n < num_listening_sockets ; ++n)
+ ap_add_poll_socket(pollset, listening_sockets[n], APR_POLLIN);
+
+ while (!this_worker_should_exit) {
+ this_worker_should_exit |= (ap_max_requests_per_child != 0) && (requests_this_child <= 0);
+
+ if (this_worker_should_exit) break;
+
+ ap_lock(accept_mutex);
+ while (!this_worker_should_exit) {
+ ap_int16_t event;
+ ap_status_t ret = ap_poll(pollset, &srv, -1);
+
+ if (has_data(me)) {
+ thread_id sender;
+ receive_data(&sender, NULL, 0);
+ this_worker_should_exit = 1;
+ }
+ if (ret != APR_SUCCESS) {
+ if (errno == EINTR) {
+ continue;
+ }
+
+ /* poll() will only return errors in catastrophic
+ * circumstances. Let's try exiting gracefully, for now. */
+ ap_log_error(APLOG_MARK, APLOG_ERR,errno, (const server_rec *)
+ ap_get_server_conf(), "poll: (listen)");
+ this_worker_should_exit = 1;
+ }
+
+ if (this_worker_should_exit) break;
+
+ if (num_listening_sockets == 1) {
+ sd = ap_listeners->sd;
+ goto got_fd;
+ }
+ else {
+ /* find a listener */
+ curr_pollfd = last_pollfd;
+ do {
+ curr_pollfd++;
+ if (curr_pollfd > num_listening_sockets) {
+ curr_pollfd = 1;
+ }
+ /* Get the revent... */
+ ap_get_revents(&event, listening_sockets[curr_pollfd], pollset);
+
+ /* XXX: Should we check for POLLERR? */
+ if (event & APR_POLLIN) {
+ last_pollfd = curr_pollfd;
+ sd = listening_sockets[curr_pollfd];
+ goto got_fd;
+ }
+ } while (curr_pollfd != last_pollfd);
+ }
+ }
+ got_fd:
+ if (!this_worker_should_exit) {
+ ap_accept(&csd, sd, ptrans);
+ ap_unlock(accept_mutex);
+ process_socket(ptrans, csd, child_slot);
+ requests_this_child--;
+ }
+ else {
+ ap_unlock(accept_mutex);
+ break;
+ }
+ ap_clear_pool(ptrans);
+ }
+
+ ap_destroy_pool(tpool);
+ ap_lock(worker_thread_count_mutex);
+ worker_thread_count--;
+ if (worker_thread_count == 0) {
+ /* All the threads have exited, now finish the shutdown process
+ * by signalling the sigwait thread */
+ kill(server_pid, SIGTERM);
+ }
+ ap_unlock(worker_thread_count_mutex);
+
+ return (0);
+}
+
+static int make_worker(server_rec *s, int slot, time_t now)
+{
+ thread_id tid;
+ proc_info *my_info = (proc_info *)malloc(sizeof(proc_info));
+
+ if (my_info == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
+ "malloc: out of memory");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ my_info->slot = slot;
+ ap_create_pool(&my_info->tpool, pchild);
+
+ if (slot + 1 > ap_max_child_assigned)
+ ap_max_child_assigned = slot + 1;
+
+ /* TODO: figure out the one_process stuff... */
+/*
+ if (one_process) {
+ set_signals();
+ ap_child_table[slot].pid = getpid();
+ ap_child_table[slot].status = SERVER_ALIVE;
+ }
+This is deliberate to remind me to do something about it!
+*/
+
+ tid = spawn_thread(worker_thread, "apache_worker", B_NORMAL_PRIORITY,
+ my_info);
+ if (tid < B_NO_ERROR) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, s,
+ "spawn_thread: Unable to start a new thread");
+ /* In case system resources are maxxed out, we don't want
+ * Apache running away with the CPU trying to fork over and
+ * over and over again. */
+ sleep(10);
+
+ return -1;
+ }
+ resume_thread(tid);
+
+ ap_child_table[slot].pid = getpid();
+ ap_child_table[slot].status = SERVER_ALIVE;
+ return 0;
+}
+
+/* start up a bunch of children */
+static void startup_threads(int number_to_start)
+{
+ int i;
+
+ for (i = 0; number_to_start && i < ap_thread_limit; ++i) {
+ if (ap_child_table[i].status != SERVER_DEAD) {
+ continue;
+ }
+ if (make_worker(ap_server_conf, i, 0) < 0) {
+ break;
+ }
+ --number_to_start;
+ }
+}
+
+
+/*
+ * spawn_rate is the number of children that will be spawned on the
+ * next maintenance cycle if there aren't enough idle servers. It is
+ * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
+ * without the need to spawn.
+ */
+static int spawn_rate = 1;
+#ifndef MAX_SPAWN_RATE
+#define MAX_SPAWN_RATE (32)
+#endif
+static int hold_off_on_exponential_spawning;
+
+static void perform_idle_server_maintenance(void)
+{
+ int i;
+ time_t now = 0;
+ int free_length;
+ int free_slots[MAX_SPAWN_RATE];
+ int last_non_dead;
+
+ /* initialize the free_list */
+ free_length = 0;
+
+ for (i = 0; i < ap_thread_limit; ++i) {
+ if (ap_child_table[i].status == SERVER_DEAD) {
+ if (free_length < spawn_rate) {
+ free_slots[free_length] = i;
+ ++free_length;
+ }
+ }
+ else {
+ last_non_dead = i;
+ }
+
+ if (i >= ap_max_child_assigned && free_length >= spawn_rate) {
+ break;
+ }
+ }
+ ap_max_child_assigned = last_non_dead + 1;
+
+ if (free_length > 0) {
+ for (i = 0; i < free_length; ++i) {
+ make_worker(ap_server_conf, free_slots[i], now);
+ }
+ /* the next time around we want to spawn twice as many if this
+ * wasn't good enough, but not if we've just done a graceful
+ */
+ if (hold_off_on_exponential_spawning) {
+ --hold_off_on_exponential_spawning;
+ } else if (spawn_rate < MAX_SPAWN_RATE) {
+ spawn_rate *= 2;
+ }
+ } else {
+ spawn_rate = 1;
+ }
+}
+
+static void server_main_loop(int remaining_threads_to_start)
+{
+ int child_slot;
+ ap_wait_t status;
+ ap_proc_t pid;
+ int i;
+
+ while (!restart_pending && !shutdown_pending) {
+ ap_wait_or_timeout(&status, &pid, pconf);
+
+ if (pid.pid >= 0) {
+ process_child_status(pid.pid, status);
+ /* non-fatal death... note that it's gone in the scoreboard. */
+ child_slot = -1;
+ for (i = 0; i < ap_max_child_assigned; ++i) {
+ if (ap_child_table[i].pid == pid.pid) {
+ int j;
+
+ child_slot = i;
+ for (j = 0; j < HARD_THREAD_LIMIT; j++) {
+ ap_beos_force_reset_connection_status(i * HARD_THREAD_LIMIT + j);
+ }
+ break;
+ }
+ }
+ if (child_slot >= 0) {
+ ap_child_table[child_slot].status = SERVER_DEAD;
+
+ if (remaining_threads_to_start
+ && child_slot < ap_thread_limit) {
+ /* we're still doing a 1-for-1 replacement of dead
+ * children with new children
+ */
+ make_worker(ap_server_conf, child_slot, time(NULL));
+ --remaining_threads_to_start;
+ }
+#ifdef APR_HAS_OTHER_CHILD
+ }
+ else if (ap_reap_other_child(&pid, status) == 0) {
+ /* handled */
+#endif
+ }
+ else if (is_graceful) {
+ /* Great, we've probably just lost a slot in the
+ * scoreboard. Somehow we don't know about this
+ * child.
+ */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, errno, ap_server_conf,
+ "long lost child came home! (pid %ld)", pid.pid);
+ }
+
+ /* Don't perform idle maintenance when a child dies,
+ * only do it when there's a timeout. Remember only a
+ * finite number of children can die, and it's pretty
+ * pathological for a lot to die suddenly.
+ */
+ continue;
+ }
+ else if (remaining_threads_to_start) {
+ /* we hit a 1 second timeout in which none of the previous
+ * generation of children needed to be reaped... so assume
+ * they're all done, and pick up the slack if any is left.
+ */
+ startup_threads(remaining_threads_to_start);
+ remaining_threads_to_start = 0;
+ /* In any event we really shouldn't do the code below because
+ * few of the servers we just started are in the IDLE state
+ * yet, so we'd mistakenly create an extra server.
+ */
+ continue;
+ }
+
+ perform_idle_server_maintenance();
+ }
+}
+
+int ap_mpm_run(ap_pool_t *_pconf, ap_pool_t *plog, server_rec *s)
+{
+ int remaining_threads_to_start, i;
+ ap_status_t rv;
+ ap_listen_rec *lr;
+ pconf = _pconf;
+ ap_server_conf = s;
+
+ if ((num_listening_sockets = setup_listeners(ap_server_conf)) < 1) {
+ /* XXX: hey, what's the right way for the mpm to indicate a fatal error? */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, errno, s,
+ "no listening sockets available, shutting down");
+ return 1;
+ }
+
+ ap_log_pid(pconf, ap_pid_fname);
+
+ /*
+ * Create our locks...
+ */
+
+ /* accept_mutex
+ * used to lock around select so we only have one thread
+ * in select at a time
+ */
+ if ((rv = ap_create_lock(&accept_mutex, APR_MUTEX, APR_CROSS_PROCESS,
+ NULL, pconf)) != APR_SUCCESS) {
+ /* tsch tsch, can't have more than one thread in the accept loop
+ at a time so we need to fall on our sword... */
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
+ "Couldn't create accept lock");
+ return 1;
+ }
+ /* worker_thread_count_mutex
+ * locks the worker_thread_count so we have ana ccurate count...
+ */
+ if ((rv = ap_create_lock(&worker_thread_count_mutex, APR_MUTEX, APR_CROSS_PROCESS,
+ NULL, pconf)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
+ "Couldn't create worker thread count lock");
+ return 1;
+ }
+
+ /*
+ * Startup/shutdown...
+ */
+
+ if (!is_graceful)
+ reinit_scoreboard(pconf);
+
+ set_signals();
+ /* Sanity cehcks to avoid thrashing... */
+ if (max_spare_threads < min_spare_threads )
+ max_spare_threads = min_spare_threads;
+
+ /* If we're doing a graceful_restart then we're going to see a lot
+ * of threads exiting immediately when we get into the main loop
+ * below (because we just sent them SIGWINCH). This happens pretty
+ * rapidly... and for each one that exits we'll start a new one until
+ * we reach at least threads_min_free. But we may be permitted to
+ * start more than that, so we'll just keep track of how many we're
+ * supposed to start up without the 1 second penalty between each fork.
+ */
+ remaining_threads_to_start = ap_threads_to_start;
+ /* sanity check on the number to start... */
+ if (remaining_threads_to_start > ap_thread_limit) {
+ remaining_threads_to_start = ap_thread_limit;
+ }
+
+ /* setup the child pool to use for the workers. Each worker creates
+ * a seperate pool of it's own to use.
+ */
+ ap_create_pool(&pchild, pconf);
+ ap_child_init_hook(pchild, ap_server_conf);
+
+ /* Now that we have the child pool (pchild) we can allocate
+ * the listenfds and creat the pollset...
+ */
+ listening_sockets = ap_palloc(pchild,
+ sizeof(*listening_sockets) * (num_listening_sockets));
+ for (lr = ap_listeners, i = 0; i < num_listening_sockets; lr = lr->next, ++i)
+ listening_sockets[i]=lr->sd;
+
+ /* we assume all goes OK...hmm might want to check that! */
+ if (!is_graceful) {
+ startup_threads(remaining_threads_to_start);
+ remaining_threads_to_start = 0;
+ }
+ else {
+ /* give the system some time to recover before kicking into
+ * exponential mode */
+ hold_off_on_exponential_spawning = 10;
+ }
+
+ /*
+ * record that we've entered the world !
+ */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, errno, ap_server_conf,
+ "%s configured -- resuming normal operations",
+ ap_get_server_version());
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, errno, ap_server_conf,
+ "Server built: %s", ap_get_server_built());
+ restart_pending = shutdown_pending = 0;
+
+ /*
+ * main_loop until it's all over
+ */
+ server_main_loop(remaining_threads_to_start);
+
+ /*
+ * If we get here we're shutting down...
+ */
+ if (shutdown_pending) {
+ /* Time to gracefully shut down:
+ * Kill child processes, tell them to call child_exit, etc...
+ */
+ if (beosd_killpg(getpgrp(), SIGTERM) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ "killpg SIGTERM");
+
+ /* cleanup pid file on normal shutdown */
+ {
+ const char *pidfile = NULL;
+ pidfile = ap_server_root_relative (pconf, ap_pid_fname);
+ if ( pidfile != NULL && unlink(pidfile) == 0)
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO,
+ errno, ap_server_conf,
+ "removed PID file %s (pid=%ld)",
+ pidfile, (long)getpid());
+ }
+
+ /* use ap_reclaim_child_processes starting with SIGTERM */
+ ap_reclaim_child_processes(1);
+
+ /* record the shutdown in the log */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, errno, ap_server_conf,
+ "caught SIGTERM, shutting down");
+
+ return 1;
+ }
+
+ /* we've been told to restart */
+ signal(SIGHUP, SIG_IGN);
+
+ if (one_process) {
+ return 1;
+ }
+
+ if (is_graceful) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, errno, ap_server_conf,
+ "SIGWINCH received. Doing graceful restart");
+ tell_workers_to_exit();
+ /* TODO - need to test some ideas here... */
+ }
+ else {
+ /* Kill 'em all. Since the child acts the same on the parents SIGTERM
+ * and a SIGHUP, we may as well use the same signal, because some user
+ * pthreads are stealing signals from us left and right.
+ */
+ push_workers_off_cliff(SIGTERM);
+
+ ap_reclaim_child_processes(1); /* Start with SIGTERM */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, errno, ap_server_conf,
+ "SIGHUP received. Attempting to restart");
+ }
+
+ if (!is_graceful) {
+ ap_restart_time = time(NULL);
+ }
+
+ return 0;
+}
+
+static void beos_pre_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptemp)
+{
+ static int restart_num = 0;
+
+ one_process = !!getenv("ONE_PROCESS");
+
+ /* sigh, want this only the second time around */
+ if (restart_num++ == 1) {
+ is_graceful = 0;
+ if (!one_process)
+ beosd_detach();
+ server_pid = getpid();
+ }
+
+ beosd_pre_config();
+ ap_listen_pre_config();
+ ap_threads_to_start = DEFAULT_START_THREADS;
+ min_spare_threads = DEFAULT_MIN_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
+ max_spare_threads = DEFAULT_MAX_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
+ ap_thread_limit = HARD_SERVER_LIMIT;
+ ap_threads_per_child = DEFAULT_THREADS_PER_CHILD;
+ ap_pid_fname = DEFAULT_PIDLOG;
+ ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
+ ap_beos_set_maintain_connection_status(1);
+
+ ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
+}
+
+static void beos_hooks(void)
+{
+ INIT_SIGLIST()
+ one_process = 0;
+
+ ap_hook_pre_config(beos_pre_config, NULL, NULL, AP_HOOK_MIDDLE);
+}
+
+
+static const char *set_pidfile(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (cmd->server->is_virtual) {
+ return "PidFile directive not allowed in <VirtualHost>";
+ }
+ ap_pid_fname = arg;
+ return NULL;
+}
+
+static const char *set_scoreboard(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_scoreboard_fname = arg;
+ return NULL;
+}
+
+static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_threads_to_start = atoi(arg);
+ return NULL;
+}
+
+static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ min_spare_threads = atoi(arg);
+ if (min_spare_threads <= 0) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "WARNING: detected MinSpareThreads set to non-positive.");
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "Resetting to 1 to avoid almost certain Apache failure.");
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "Please read the documentation.");
+ min_spare_threads = 1;
+ }
+
+ return NULL;
+}
+
+static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ max_spare_threads = atoi(arg);
+ return NULL;
+}
+
+static const char *set_server_limit (cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_thread_limit = atoi(arg);
+ if (ap_thread_limit > HARD_SERVER_LIMIT) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "WARNING: MaxClients of %d exceeds compile time limit "
+ "of %d servers,", ap_thread_limit, HARD_SERVER_LIMIT);
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ " lowering MaxClients to %d. To increase, please "
+ "see the", HARD_SERVER_LIMIT);
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ " HARD_SERVER_LIMIT define in src/include/httpd.h.");
+ ap_thread_limit = HARD_SERVER_LIMIT;
+ }
+ else if (ap_thread_limit < 1) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "WARNING: Require MaxClients > 0, setting to 1");
+ ap_thread_limit = 1;
+ }
+ return NULL;
+}
+
+static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_threads_per_child = atoi(arg);
+ if (ap_threads_per_child > HARD_THREAD_LIMIT) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "WARNING: ThreadsPerChild of %d exceeds compile time"
+ "limit of %d threads,\n", ap_threads_per_child,
+ HARD_THREAD_LIMIT);
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ " lowering ThreadsPerChild to %d. To increase, please"
+ "see the", HARD_THREAD_LIMIT);
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ " HARD_THREAD_LIMIT define in src/include/httpd.h.");
+ }
+ else if (ap_threads_per_child < 1) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "WARNING: Require ThreadsPerChild > 0, setting to 1");
+ ap_threads_per_child = 1;
+ }
+ return NULL;
+}
+
+static const char *set_max_requests(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_max_requests_per_child = atoi(arg);
+
+ return NULL;
+}
+
+static const char *set_maintain_connection_status(cmd_parms *cmd,
+ core_dir_config *d, int arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_beos_set_maintain_connection_status(arg != 0);
+ return NULL;
+}
+
+static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, char *arg)
+{
+ ap_finfo_t finfo;
+ const char *fname;
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ fname = ap_server_root_relative(cmd->pool, arg);
+ if ((ap_stat(&finfo, fname, cmd->pool) != APR_SUCCESS) ||
+ (finfo.filetype != APR_DIR)) {
+ return ap_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
+ " does not exist or is not a directory", NULL);
+ }
+ ap_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir));
+ return NULL;
+}
+
+static const command_rec beos_cmds[] = {
+UNIX_DAEMON_COMMANDS
+LISTEN_COMMANDS
+{ "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1,
+ "A file for logging the server process ID"},
+{ "ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, TAKE1,
+ "A file for Apache to maintain runtime process management information"},
+{ "StartServers", set_daemons_to_start, NULL, RSRC_CONF, TAKE1,
+ "Number of child processes launched at server startup" },
+{ "MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF, TAKE1,
+ "Minimum number of idle children, to handle request spikes" },
+{ "MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF, TAKE1,
+ "Maximum number of idle children" },
+{ "MaxClients", set_server_limit, NULL, RSRC_CONF, TAKE1,
+ "Maximum number of children alive at the same time" },
+{ "ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF, TAKE1,
+ "Number of threads each child creates" },
+{ "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1,
+ "Maximum number of requests a particular child serves before dying." },
+{ "ConnectionStatus", set_maintain_connection_status, NULL, RSRC_CONF, FLAG,
+ "Whether or not to maintain status information on current connections"},
+{ "CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, TAKE1,
+ "The location of the directory Apache changes to before dumping core" },
+{ NULL }
+};
+
+module MODULE_VAR_EXPORT mpm_beos_module = {
+ MPM20_MODULE_STUFF,
+ NULL, /* hook to run before apache parses args */
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ beos_cmds, /* command ap_table_t */
+ NULL, /* handlers */
+ beos_hooks /* register_hooks */
+};
+
diff --git a/server/mpm/beos/beos.h b/server/mpm/beos/beos.h
new file mode 100644
index 0000000000..c7cffdef24
--- /dev/null
+++ b/server/mpm/beos/beos.h
@@ -0,0 +1,69 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef APACHE_MPM_BEOS_H
+#define APACHE_MPM_BEOS_H
+
+extern int ap_threads_per_child;
+extern int ap_max_requests_per_child;
+extern int ap_pipe_of_death[2];
+extern int ap_extended_status;
+extern void clean_child_exit(int);
+extern int max_daemons_limit;
+
+#endif /* APACHE_MPM_BEOS_H */
diff --git a/server/mpm/beos/config5.m4 b/server/mpm/beos/config5.m4
new file mode 100644
index 0000000000..4f201408d6
--- /dev/null
+++ b/server/mpm/beos/config5.m4
@@ -0,0 +1,7 @@
+dnl ## XXX - Need a more thorough check of the proper flags to use
+
+if test "$MPM_NAME" = "beos" ; then
+ apache_apr_flags="--enable-threads"
+
+ APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile)
+fi
diff --git a/server/mpm/beos/mpm.h b/server/mpm/beos/mpm.h
new file mode 100644
index 0000000000..af4e237777
--- /dev/null
+++ b/server/mpm/beos/mpm.h
@@ -0,0 +1,79 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef APACHE_MPM_BEOS_H
+#define APACHE_MPM_BEOS_H
+
+#define BEOS_MPM
+extern int ap_max_child_assigned;
+#include "scoreboard.h"
+
+#define SERVER_DEAD 0
+#define SERVER_DYING 1
+#define SERVER_ALIVE 2
+
+typedef struct ap_ctable{
+ pid_t pid;
+ unsigned char status;
+} ap_ctable;
+
+extern ap_ctable ap_child_table[HARD_SERVER_LIMIT];
+extern server_rec *ap_server_conf;
+
+
+#endif /* APACHE_MPM_BEOS_H */
diff --git a/server/mpm/beos/mpm_default.h b/server/mpm/beos/mpm_default.h
new file mode 100644
index 0000000000..ffe055d40d
--- /dev/null
+++ b/server/mpm/beos/mpm_default.h
@@ -0,0 +1,143 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef APACHE_MPM_DEFAULT_H
+#define APACHE_MPM_DEFAULT_H
+
+/* Number of servers to spawn off by default --- also, if fewer than
+ * this free when the caretaker checks, it will spawn more.
+ */
+#ifndef DEFAULT_START_DAEMON
+#define DEFAULT_START_DAEMON 5
+#endif
+#define DEFAULT_START_THREADS 20
+
+/* Maximum number of *free* server processes --- more than this, and
+ * they will die off.
+ */
+
+#ifndef DEFAULT_MAX_FREE_DAEMON
+#define DEFAULT_MAX_FREE_DAEMON 10
+#endif
+
+/* Minimum --- fewer than this, and more will be created */
+
+#ifndef DEFAULT_MIN_FREE_DAEMON
+#define DEFAULT_MIN_FREE_DAEMON 5
+#endif
+
+/* Limit on the total --- clients will be locked out if more servers than
+ * this are needed. It is intended solely to keep the server from crashing
+ * when things get out of hand.
+ *
+ * We keep a hard maximum number of servers, for two reasons --- first off,
+ * in case something goes seriously wrong, we want to stop the fork bomb
+ * short of actually crashing the machine we're running on by filling some
+ * kernel table. Secondly, it keeps the size of the scoreboard file small
+ * enough that we can read the whole thing without worrying too much about
+ * the overhead.
+ */
+
+#ifdef NO_THREADS
+#define HARD_SERVER_LIMIT 256
+#endif
+#ifndef HARD_SERVER_LIMIT
+#define HARD_SERVER_LIMIT 64
+#endif
+
+/* Limit on the threads per process. Clients will be locked out if more than
+ * this * HARD_SERVER_LIMIT are needed.
+ *
+ * We keep this for one reason it keeps the size of the scoreboard file small
+ * enough that we can read the whole thing without worrying too much about
+ * the overhead.
+ */
+#ifdef NO_THREADS
+#define HARD_THREAD_LIMIT 1
+#endif
+#ifndef HARD_THREAD_LIMIT
+#define HARD_THREAD_LIMIT 64
+#endif
+
+#ifdef NO_THREADS
+#define DEFAULT_THREADS_PER_CHILD 1
+#endif
+#ifndef DEFAULT_THREADS_PER_CHILD
+#define DEFAULT_THREADS_PER_CHILD 10
+#endif
+
+/* Where the main/parent process's pid is logged */
+#ifndef DEFAULT_PIDLOG
+#define DEFAULT_PIDLOG "logs/httpd.pid"
+#endif
+
+/*
+ * Interval, in microseconds, between scoreboard maintenance.
+ */
+#ifndef SCOREBOARD_MAINTENANCE_INTERVAL
+#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000
+#endif
+
+/* Number of requests to try to handle in a single process. If <= 0,
+ * the children don't die off.
+ */
+#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
+#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
+#endif
+
+#endif /* AP_MPM_DEFAULT_H */
diff --git a/server/mpm/experimental/perchild/.cvsignore b/server/mpm/experimental/perchild/.cvsignore
new file mode 100644
index 0000000000..84df257214
--- /dev/null
+++ b/server/mpm/experimental/perchild/.cvsignore
@@ -0,0 +1,5 @@
+.deps
+.libs
+*.lo
+*.la
+Makefile
diff --git a/server/mpm/experimental/perchild/Makefile.in b/server/mpm/experimental/perchild/Makefile.in
new file mode 100644
index 0000000000..4d83d72d2d
--- /dev/null
+++ b/server/mpm/experimental/perchild/Makefile.in
@@ -0,0 +1,5 @@
+
+LTLIBRARY_NAME = libperchild.la
+LTLIBRARY_SOURCES = perchild.c scoreboard.c
+
+include $(top_srcdir)/build/ltlib.mk
diff --git a/server/mpm/experimental/perchild/config5.m4 b/server/mpm/experimental/perchild/config5.m4
new file mode 100644
index 0000000000..bd179baed9
--- /dev/null
+++ b/server/mpm/experimental/perchild/config5.m4
@@ -0,0 +1,6 @@
+dnl ## XXX - Need a more thorough check of the proper flags to use
+
+if test "$MPM_NAME" = "perchild" ; then
+
+ APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile)
+fi
diff --git a/server/mpm/experimental/perchild/mpm.h b/server/mpm/experimental/perchild/mpm.h
new file mode 100644
index 0000000000..eef0032dd3
--- /dev/null
+++ b/server/mpm/experimental/perchild/mpm.h
@@ -0,0 +1,83 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "httpd.h"
+#include "mpm_default.h"
+#include "unixd.h"
+
+#ifndef APACHE_MPM_PERCHILD_H
+#define APACHE_MPM_PERCHILD_H
+
+#define PERCHILD_MPM
+
+/* Table of child status */
+#define SERVER_DEAD 0
+#define SERVER_DYING 1
+#define SERVER_ALIVE 2
+
+typedef struct ap_ctable{
+ pid_t pid;
+ unsigned char status;
+} ap_ctable;
+
+extern int ap_max_daemons_limit;
+extern ap_ctable ap_child_table[HARD_SERVER_LIMIT];
+extern server_rec *ap_server_conf;
+extern char ap_coredump_dir[MAX_STRING_LEN];
+
+#endif /* APACHE_MPM_PERCHILD_H */
diff --git a/server/mpm/experimental/perchild/mpm_default.h b/server/mpm/experimental/perchild/mpm_default.h
new file mode 100644
index 0000000000..5bd323b09b
--- /dev/null
+++ b/server/mpm/experimental/perchild/mpm_default.h
@@ -0,0 +1,144 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef APACHE_MPM_DEFAULT_H
+#define APACHE_MPM_DEFAULT_H
+
+/* Number of threads to spawn off by default --- also, if fewer than
+ * this free when the caretaker checks, it will spawn more.
+ */
+#ifndef DEFAULT_START_THREAD
+#define DEFAULT_START_THREAD 5
+#endif
+
+/* Maximum number of *free* server threads --- more than this, and
+ * they will die off.
+ */
+
+#ifndef DEFAULT_MAX_SPARE_THREAD
+#define DEFAULT_MAX_SPARE_THREAD 10
+#endif
+
+/* Minimum --- fewer than this, and more will be created */
+
+#ifndef DEFAULT_MIN_SPARE_THREAD
+#define DEFAULT_MIN_SPARE_THREAD 5
+#endif
+
+/* Limit on the threads per process. Clients will be locked out if more than
+ * this * HARD_SERVER_LIMIT are needed.
+ *
+ * We keep this for one reason it keeps the size of the scoreboard file small
+ * enough that we can read the whole thing without worrying too much about
+ * the overhead.
+ */
+#ifndef HARD_THREAD_LIMIT
+#define HARD_THREAD_LIMIT 64
+#endif
+
+/* Number of servers to spawn off by default
+ */
+#ifndef DEFAULT_NUM_DAEMON
+#define DEFAULT_NUM_DAEMON 2
+#endif
+
+/* Limit on the total --- clients will be locked out if more servers than
+ * this are needed. It is intended solely to keep the server from crashing
+ * when things get out of hand.
+ *
+ * We keep a hard maximum number of servers, for two reasons --- first off,
+ * in case something goes seriously wrong, we want to stop the fork bomb
+ * short of actually crashing the machine we're running on by filling some
+ * kernel table. Secondly, it keeps the size of the scoreboard file small
+ * enough that we can read the whole thing without worrying too much about
+ * the overhead.
+ */
+#ifndef HARD_SERVER_LIMIT
+#define HARD_SERVER_LIMIT 8
+#endif
+
+/* File used for accept locking, when we use a file */
+#ifndef DEFAULT_LOCKFILE
+#define DEFAULT_LOCKFILE "logs/accept.lock"
+#endif
+
+/* Scoreboard file, if there is one */
+#ifndef DEFAULT_SCOREBOARD
+#define DEFAULT_SCOREBOARD "logs/apache_runtime_status"
+#endif
+
+/* Where the main/parent process's pid is logged */
+#ifndef DEFAULT_PIDLOG
+#define DEFAULT_PIDLOG "logs/httpd.pid"
+#endif
+
+/*
+ * Interval, in microseconds, between scoreboard maintenance.
+ */
+#ifndef SCOREBOARD_MAINTENANCE_INTERVAL
+#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000
+#endif
+
+/* Number of requests to try to handle in a single process. If <= 0,
+ * the children don't die off.
+ */
+#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
+#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
+#endif
+
+#endif /* AP_MPM_DEFAULT_H */
diff --git a/server/mpm/experimental/perchild/perchild.c b/server/mpm/experimental/perchild/perchild.c
new file mode 100644
index 0000000000..3a9748755f
--- /dev/null
+++ b/server/mpm/experimental/perchild/perchild.c
@@ -0,0 +1,1430 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#define CORE_PRIVATE
+
+#include "ap_config.h"
+#include "apr_strings.h"
+#include "apr_portable.h"
+#include "apr_file_io.h"
+#include "httpd.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_config.h" /* for read_config */
+#include "http_core.h" /* for get_remote_host */
+#include "http_connection.h"
+#include "ap_mpm.h"
+#include "unixd.h"
+#include "mpm_common.h"
+#include "iol_socket.h"
+#include "ap_listen.h"
+#include "mpm_default.h"
+#include "mpm.h"
+#include "scoreboard.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <poll.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#include <pthread.h>
+#include <signal.h>
+
+/*
+ * Actual definitions of config globals
+ */
+
+static int threads_to_start = 0; /* Worker threads per child */
+static int min_spare_threads = 0;
+static int max_spare_threads = 0;
+static int max_threads = 0;
+static int max_requests_per_child = 0;
+static const char *ap_pid_fname=NULL;
+API_VAR_EXPORT const char *ap_scoreboard_fname=NULL;
+static int num_daemons=0;
+static int workers_may_exit = 0;
+static int requests_this_child;
+static int num_listenfds = 0;
+static ap_socket_t **listenfds;
+
+struct ap_ctable ap_child_table[HARD_SERVER_LIMIT];
+
+/*
+ * The max child slot ever assigned, preserved across restarts. Necessary
+ * to deal with NumServers changes across SIGWINCH restarts. We use this
+ * value to optimize routines that have to scan the entire child table.
+ *
+ * XXX - It might not be worth keeping this code in. There aren't very
+ * many child processes in this MPM.
+ */
+int ap_max_daemons_limit = -1;
+
+char ap_coredump_dir[MAX_STRING_LEN];
+
+static ap_file_t *pipe_of_death_in = NULL;
+static ap_file_t *pipe_of_death_out = NULL;
+static pthread_mutex_t pipe_of_death_mutex;
+
+/* *Non*-shared http_main globals... */
+
+server_rec *ap_server_conf;
+
+/* one_process --- debugging mode variable; can be set from the command line
+ * with the -X flag. If set, this gets you the child_main loop running
+ * in the process which originally started up (no detach, no make_child),
+ * which is a pretty nice debugging environment. (You'll get a SIGHUP
+ * early in standalone_main; just continue through. This is the server
+ * trying to kill off any child processes which it might have lying
+ * around --- Apache doesn't keep track of their pids, it just sends
+ * SIGHUP to the process group, ignoring it in the root process.
+ * Continue through and you'll be fine.).
+ */
+
+static int one_process = 0;
+
+#ifdef DEBUG_SIGSTOP
+int raise_sigstop_flags;
+#endif
+
+static ap_pool_t *pconf; /* Pool for config stuff */
+static ap_pool_t *pchild; /* Pool for httpd child stuff */
+static ap_pool_t *thread_pool_parent; /* Parent of per-thread pools */
+static pthread_mutex_t thread_pool_parent_mutex;
+
+static int child_num;
+static unsigned int my_pid; /* Linux getpid() doesn't work except in
+ main thread. Use this instead */
+/* Keep track of the number of worker threads currently active */
+static int worker_thread_count;
+static pthread_mutex_t worker_thread_count_mutex;
+static int worker_thread_free_ids[HARD_THREAD_LIMIT];
+static pthread_attr_t worker_thread_attr;
+
+/* Keep track of the number of idle worker threads */
+static int idle_thread_count;
+static pthread_mutex_t idle_thread_count_mutex;
+
+/* Locks for accept serialization */
+#ifdef NO_SERIALIZED_ACCEPT
+#define SAFE_ACCEPT(stmt) APR_SUCCESS
+#else
+#define SAFE_ACCEPT(stmt) (stmt)
+static ap_lock_t *process_accept_mutex;
+#endif /* NO_SERIALIZED_ACCEPT */
+static const char *lock_fname;
+static pthread_mutex_t thread_accept_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+API_EXPORT(int) ap_get_max_daemons(void)
+{
+ return ap_max_daemons_limit;
+}
+
+/* a clean exit from a child with proper cleanup */
+static void clean_child_exit(int code)
+{
+ if (pchild) {
+ ap_destroy_pool(pchild);
+ }
+ exit(code);
+}
+
+/* handle all varieties of core dumping signals */
+static void sig_coredump(int sig)
+{
+ chdir(ap_coredump_dir);
+ ap_signal(sig, SIG_DFL);
+ kill(getpid(), sig);
+ /* At this point we've got sig blocked, because we're still inside
+ * the signal handler. When we leave the signal handler it will
+ * be unblocked, and we'll take the signal... and coredump or whatever
+ * is appropriate for this particular Unix. In addition the parent
+ * will see the real signal we received -- whereas if we called
+ * abort() here, the parent would only see SIGABRT.
+ */
+}
+
+static void just_die(int sig)
+{
+ clean_child_exit(0);
+}
+
+/*****************************************************************
+ * Connection structures and accounting...
+ */
+
+/* volatile just in case */
+static int volatile shutdown_pending;
+static int volatile restart_pending;
+static int volatile is_graceful;
+
+/*
+ * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
+ * functions to initiate shutdown or restart without relying on signals.
+ * Previously this was initiated in sig_term() and restart() signal handlers,
+ * but we want to be able to start a shutdown/restart from other sources --
+ * e.g. on Win32, from the service manager. Now the service manager can
+ * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that
+ * these functions can also be called by the child processes, since global
+ * variables are no longer used to pass on the required action to the parent.
+ *
+ * These should only be called from the parent process itself, since the
+ * parent process will use the shutdown_pending and restart_pending variables
+ * to determine whether to shutdown or restart. The child process should
+ * call signal_parent() directly to tell the parent to die -- this will
+ * cause neither of those variable to be set, which the parent will
+ * assume means something serious is wrong (which it will be, for the
+ * child to force an exit) and so do an exit anyway.
+ */
+
+void ap_start_shutdown(void)
+{
+ if (shutdown_pending == 1) {
+ /* Um, is this _probably_ not an error, if the user has
+ * tried to do a shutdown twice quickly, so we won't
+ * worry about reporting it.
+ */
+ return;
+ }
+ shutdown_pending = 1;
+}
+
+/* do a graceful restart if graceful == 1 */
+void ap_start_restart(int graceful)
+{
+
+ if (restart_pending == 1) {
+ /* Probably not an error - don't bother reporting it */
+ return;
+ }
+ restart_pending = 1;
+ is_graceful = graceful;
+}
+
+static void sig_term(int sig)
+{
+ ap_start_shutdown();
+}
+
+static void restart(int sig)
+{
+#ifndef WIN32
+ ap_start_restart(sig == SIGWINCH);
+#else
+ ap_start_restart(1);
+#endif
+}
+
+static void set_signals(void)
+{
+#ifndef NO_USE_SIGACTION
+ struct sigaction sa;
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ if (!one_process) {
+ sa.sa_handler = sig_coredump;
+#if defined(SA_ONESHOT)
+ sa.sa_flags = SA_ONESHOT;
+#elif defined(SA_RESETHAND)
+ sa.sa_flags = SA_RESETHAND;
+#endif
+ if (sigaction(SIGSEGV, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGSEGV)");
+#ifdef SIGBUS
+ if (sigaction(SIGBUS, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGBUS)");
+#endif
+#ifdef SIGABORT
+ if (sigaction(SIGABORT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABORT)");
+#endif
+#ifdef SIGABRT
+ if (sigaction(SIGABRT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABRT)");
+#endif
+#ifdef SIGILL
+ if (sigaction(SIGILL, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGILL)");
+#endif
+ sa.sa_flags = 0;
+ }
+ sa.sa_handler = sig_term;
+ if (sigaction(SIGTERM, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
+#ifdef SIGINT
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
+#endif
+#ifdef SIGXCPU
+ sa.sa_handler = SIG_DFL;
+ if (sigaction(SIGXCPU, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGXCPU)");
+#endif
+#ifdef SIGXFSZ
+ sa.sa_handler = SIG_DFL;
+ if (sigaction(SIGXFSZ, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGXFSZ)");
+#endif
+#ifdef SIGPIPE
+ sa.sa_handler = SIG_IGN;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGPIPE)");
+#endif
+
+ /* we want to ignore HUPs and WINCH while we're busy processing one */
+ sigaddset(&sa.sa_mask, SIGHUP);
+ sigaddset(&sa.sa_mask, SIGWINCH);
+ sa.sa_handler = restart;
+ if (sigaction(SIGHUP, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
+ if (sigaction(SIGWINCH, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGWINCH)");
+#else
+ if (!one_process) {
+ ap_signal(SIGSEGV, sig_coredump);
+#ifdef SIGBUS
+ ap_signal(SIGBUS, sig_coredump);
+#endif /* SIGBUS */
+#ifdef SIGABORT
+ ap_signal(SIGABORT, sig_coredump);
+#endif /* SIGABORT */
+#ifdef SIGABRT
+ ap_signal(SIGABRT, sig_coredump);
+#endif /* SIGABRT */
+#ifdef SIGILL
+ ap_signal(SIGILL, sig_coredump);
+#endif /* SIGILL */
+#ifdef SIGXCPU
+ ap_signal(SIGXCPU, SIG_DFL);
+#endif /* SIGXCPU */
+#ifdef SIGXFSZ
+ ap_signal(SIGXFSZ, SIG_DFL);
+#endif /* SIGXFSZ */
+ }
+
+ ap_signal(SIGTERM, sig_term);
+#ifdef SIGHUP
+ ap_signal(SIGHUP, restart);
+#endif /* SIGHUP */
+#ifdef SIGWINCH
+ ap_signal(SIGWINCH, restart);
+#endif /* SIGWINCH */
+#ifdef SIGPIPE
+ ap_signal(SIGPIPE, SIG_IGN);
+#endif /* SIGPIPE */
+
+#endif
+}
+
+/*****************************************************************
+ * Here follows a long bunch of generic server bookkeeping stuff...
+ */
+
+int ap_graceful_stop_signalled(void)
+{
+ /* XXX - Does this really work? - Manoj */
+ return is_graceful;
+}
+
+/*****************************************************************
+ * Child process main loop.
+ */
+
+static void process_socket(ap_pool_t *p, ap_socket_t *sock, long conn_id)
+{
+ BUFF *conn_io;
+ conn_rec *current_conn;
+ ap_iol *iol;
+ int csd;
+ ap_status_t rv;
+
+ if ((rv = ap_get_os_sock(&csd, sock)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, "ap_get_os_sock");
+ }
+
+ if (csd >= FD_SETSIZE) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, NULL,
+ "new file descriptor %d is too large; you probably need "
+ "to rebuild Apache with a larger FD_SETSIZE "
+ "(currently %d)",
+ csd, FD_SETSIZE);
+ ap_close_socket(sock);
+ return;
+ }
+
+ ap_sock_disable_nagle(csd);
+ iol = ap_iol_attach_socket(p, sock);
+ conn_io = ap_bcreate(p, B_RDWR);
+ ap_bpush_iol(conn_io, iol);
+
+ current_conn = ap_new_apr_connection(p, ap_server_conf, conn_io, sock,
+ conn_id);
+
+ ap_process_connection(current_conn);
+ ap_lingering_close(current_conn);
+}
+
+static void *worker_thread(void *);
+
+/* Starts a thread as long as we're below max_threads */
+static int start_thread(void)
+{
+ pthread_t thread;
+
+ pthread_mutex_lock(&worker_thread_count_mutex);
+ if (worker_thread_count < max_threads) {
+ if (pthread_create(&thread, &worker_thread_attr, worker_thread,
+ &worker_thread_free_ids[worker_thread_count])) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
+ "pthread_create: unable to create worker thread");
+ /* In case system resources are maxxed out, we don't want
+ Apache running away with the CPU trying to fork over and
+ over and over again if we exit. */
+ sleep(10);
+ workers_may_exit = 1;
+ pthread_mutex_unlock(&worker_thread_count_mutex);
+ return 0;
+ }
+ else {
+ worker_thread_count++;
+ }
+ }
+ else {
+ static int reported = 0;
+
+ if (!reported) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, ap_server_conf,
+ "server reached MaxThreadsPerChild setting, consider raising the"
+ " MaxThreadsPerChild or NumServers settings");
+ reported = 1;
+ }
+ pthread_mutex_unlock(&worker_thread_count_mutex);
+ return 0;
+ }
+ pthread_mutex_unlock(&worker_thread_count_mutex);
+ return 1;
+
+}
+/* Sets workers_may_exit if we received a character on the pipe_of_death */
+static void check_pipe_of_death(void)
+{
+ pthread_mutex_lock(&pipe_of_death_mutex);
+ if (!workers_may_exit) {
+ int ret;
+ char pipe_read_char;
+ ap_ssize_t n = 1;
+
+ ret = ap_recv(listenfds[0], &pipe_read_char, &n);
+ if (ap_canonical_error(ret) == APR_EAGAIN) {
+ /* It lost the lottery. It must continue to suffer
+ * through a life of servitude. */
+ }
+ else {
+ /* It won the lottery (or something else is very
+ * wrong). Embrace death with open arms. */
+ workers_may_exit = 1;
+ }
+ }
+ pthread_mutex_unlock(&pipe_of_death_mutex);
+}
+
+/* idle_thread_count should be incremented before starting a worker_thread */
+
+static void *worker_thread(void *arg)
+{
+ ap_socket_t *csd = NULL;
+ ap_pool_t *tpool; /* Pool for this thread */
+ ap_pool_t *ptrans; /* Pool for per-transaction stuff */
+ ap_socket_t *sd = NULL;
+ int srv;
+ int curr_pollfd, last_pollfd = 0;
+ int thread_just_started = 1;
+ int thread_num = *((int *) arg);
+ long conn_id = child_num * HARD_THREAD_LIMIT + thread_num;
+ ap_pollfd_t *pollset;
+ int n;
+ ap_status_t rv;
+
+ pthread_mutex_lock(&thread_pool_parent_mutex);
+ ap_create_pool(&tpool, thread_pool_parent);
+ pthread_mutex_unlock(&thread_pool_parent_mutex);
+ ap_create_pool(&ptrans, tpool);
+
+ ap_setup_poll(&pollset, num_listenfds+1, tpool);
+ for(n=0 ; n <= num_listenfds ; ++n)
+ ap_add_poll_socket(pollset, listenfds[n], APR_POLLIN);
+
+ while (!workers_may_exit) {
+ workers_may_exit |= (max_requests_per_child != 0) && (requests_this_child <= 0);
+ if (workers_may_exit) break;
+ if (!thread_just_started) {
+ pthread_mutex_lock(&idle_thread_count_mutex);
+ if (idle_thread_count < max_spare_threads) {
+ idle_thread_count++;
+ pthread_mutex_unlock(&idle_thread_count_mutex);
+ }
+ else {
+ pthread_mutex_unlock(&idle_thread_count_mutex);
+ break;
+ }
+ }
+ else {
+ thread_just_started = 0;
+ }
+ pthread_mutex_lock(&thread_accept_mutex);
+ if (workers_may_exit) {
+ pthread_mutex_unlock(&thread_accept_mutex);
+ break;
+ }
+ if ((rv = SAFE_ACCEPT(ap_lock(process_accept_mutex)))
+ != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ "ap_lock failed. Attempting to shutdown "
+ "process gracefully.");
+ workers_may_exit = 1;
+ }
+
+ while (!workers_may_exit) {
+ ap_int16_t event;
+ srv = ap_poll(pollset, &n, -1);
+
+ if (srv != APR_SUCCESS) {
+ if (ap_canonical_error(srv) == APR_EINTR) {
+ continue;
+ }
+
+ /* ap_poll() will only return errors in catastrophic
+ * circumstances. Let's try exiting gracefully, for now. */
+ ap_log_error(APLOG_MARK, APLOG_ERR, srv, (const server_rec *)
+ ap_server_conf, "ap_poll: (listen)");
+ workers_may_exit = 1;
+ }
+ if (workers_may_exit) break;
+
+ ap_get_revents(&event, listenfds[0], pollset);
+ if (event & APR_POLLIN) {
+ /* A process got a signal on the shutdown pipe. Check if we're
+ * the lucky process to die. */
+ check_pipe_of_death();
+ continue;
+ }
+
+ if (num_listenfds == 1) {
+ sd = ap_listeners->sd;
+ goto got_fd;
+ }
+ else {
+ /* find a listener */
+ curr_pollfd = last_pollfd;
+ do {
+ curr_pollfd++;
+ if (curr_pollfd > num_listenfds) {
+ curr_pollfd = 1;
+ }
+ /* XXX: Should we check for POLLERR? */
+ ap_get_revents(&event, listenfds[curr_pollfd], pollset);
+ if (event & APR_POLLIN) {
+ last_pollfd = curr_pollfd;
+ sd = listenfds[curr_pollfd];
+ goto got_fd;
+ }
+ } while (curr_pollfd != last_pollfd);
+ }
+ }
+ got_fd:
+ if (!workers_may_exit) {
+ if ((rv = ap_accept(&csd, sd, ptrans)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, "ap_accept");
+ }
+ if ((rv = SAFE_ACCEPT(ap_unlock(process_accept_mutex)))
+ != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ "ap_unlock failed. Attempting to shutdown "
+ "process gracefully.");
+ workers_may_exit = 1;
+ }
+ pthread_mutex_unlock(&thread_accept_mutex);
+ pthread_mutex_lock(&idle_thread_count_mutex);
+ if (idle_thread_count > min_spare_threads) {
+ idle_thread_count--;
+ }
+ else {
+ if (!start_thread()) {
+ idle_thread_count--;
+ }
+ }
+ pthread_mutex_unlock(&idle_thread_count_mutex);
+ process_socket(ptrans, csd, conn_id);
+ requests_this_child--;
+ } else {
+ if ((rv = SAFE_ACCEPT(ap_unlock(process_accept_mutex)))
+ != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ "ap_unlock failed. Attempting to shutdown "
+ "process gracefully.");
+ workers_may_exit = 1;
+ }
+ pthread_mutex_unlock(&thread_accept_mutex);
+ pthread_mutex_lock(&idle_thread_count_mutex);
+ idle_thread_count--;
+ pthread_mutex_unlock(&idle_thread_count_mutex);
+ break;
+ }
+ ap_clear_pool(ptrans);
+ }
+
+ pthread_mutex_lock(&thread_pool_parent_mutex);
+ ap_destroy_pool(tpool);
+ pthread_mutex_unlock(&thread_pool_parent_mutex);
+ pthread_mutex_lock(&worker_thread_count_mutex);
+ worker_thread_count--;
+ worker_thread_free_ids[worker_thread_count] = thread_num;
+ if (worker_thread_count == 0) {
+ /* All the threads have exited, now finish the shutdown process
+ * by signalling the sigwait thread */
+ kill(my_pid, SIGTERM);
+ }
+ pthread_mutex_unlock(&worker_thread_count_mutex);
+
+ return NULL;
+}
+
+static void child_main(int child_num_arg)
+{
+ sigset_t sig_mask;
+ int signal_received;
+ int i;
+ ap_listen_rec *lr;
+ ap_status_t rv;
+
+ my_pid = getpid();
+ child_num = child_num_arg;
+ ap_create_pool(&pchild, pconf);
+
+ /*stuff to do before we switch id's, so we have permissions.*/
+
+ rv = SAFE_ACCEPT(ap_child_init_lock(&process_accept_mutex, lock_fname,
+ pchild));
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ "Couldn't initialize cross-process lock in child");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ if (unixd_setup_child()) {
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ ap_child_init_hook(pchild, ap_server_conf);
+
+ /*done with init critical section */
+
+ /* All threads should mask signals out, accoring to sigwait(2) man page */
+ sigfillset(&sig_mask);
+
+#ifdef SIGPROCMASK_SETS_THREAD_MASK
+ if (sigprocmask(SIG_SETMASK, &sig_mask, NULL) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf, "sigprocmask");
+ }
+#else
+ if (pthread_sigmask(SIG_SETMASK, &sig_mask, NULL) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
+ "pthread_sigmask");
+ }
+#endif
+
+ requests_this_child = max_requests_per_child;
+
+ /* Set up the pollfd array */
+ listenfds = ap_pcalloc(pchild, sizeof(*listenfds) * (num_listenfds + 1));
+#if APR_FILES_AS_SOCKETS
+ ap_socket_from_file(&listenfds[0], pipe_of_death_in);
+#endif
+ for (lr = ap_listeners, i = 1; i <= num_listenfds; lr = lr->next, ++i)
+ listenfds[i]=lr->sd;
+
+ /* Setup worker threads */
+
+ if (threads_to_start > max_threads) {
+ threads_to_start = max_threads;
+ }
+ idle_thread_count = threads_to_start;
+ worker_thread_count = 0;
+ for (i = 0; i < max_threads; i++) {
+ worker_thread_free_ids[i] = i;
+ }
+ ap_create_pool(&thread_pool_parent, pchild);
+ pthread_mutex_init(&thread_pool_parent_mutex, NULL);
+ pthread_mutex_init(&idle_thread_count_mutex, NULL);
+ pthread_mutex_init(&worker_thread_count_mutex, NULL);
+ pthread_mutex_init(&pipe_of_death_mutex, NULL);
+ pthread_attr_init(&worker_thread_attr);
+#ifdef PTHREAD_ATTR_SETDETACHSTATE_ARG2_ADDR
+ {
+ int on = 1;
+
+ pthread_attr_setdetachstate(&worker_thread_attr, &on);
+ }
+#else
+ pthread_attr_setdetachstate(&worker_thread_attr, PTHREAD_CREATE_DETACHED);
+#endif
+
+ /* We are creating worker threads right now */
+ for (i=0; i < threads_to_start; i++) {
+ /* start_thread shouldn't fail here */
+ if (!start_thread()) {
+ break;
+ }
+ }
+
+ /* This thread will be the one responsible for handling signals */
+ sigemptyset(&sig_mask);
+ sigaddset(&sig_mask, SIGTERM);
+ sigaddset(&sig_mask, SIGINT);
+ ap_sigwait(&sig_mask, &signal_received);
+ switch (signal_received) {
+ case SIGTERM:
+ case SIGINT:
+ just_die(signal_received);
+ break;
+ default:
+ ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
+ "received impossible signal: %d", signal_received);
+ just_die(SIGTERM);
+ }
+}
+
+static int make_child(server_rec *s, int slot, time_t now)
+{
+ int pid;
+
+ if (slot + 1 > ap_max_daemons_limit) {
+ ap_max_daemons_limit = slot + 1;
+ }
+
+ if (one_process) {
+ set_signals();
+ ap_child_table[slot].pid = getpid();
+ ap_child_table[slot].status = SERVER_ALIVE;
+ child_main(slot);
+ }
+
+ if ((pid = fork()) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, s,
+ "fork: Unable to fork new process");
+ /* In case system resources are maxxed out, we don't want
+ Apache running away with the CPU trying to fork over and
+ over and over again. */
+ sleep(10);
+
+ return -1;
+ }
+
+ if (!pid) {
+#ifdef AIX_BIND_PROCESSOR
+ /* By default, AIX binds to a single processor. This bit unbinds
+ children which will then bind to another CPU.
+ */
+#include <sys/processor.h>
+ int status = bindprocessor(BINDPROCESS, (int)getpid(),
+ PROCESSOR_CLASS_ANY);
+ if (status != OK)
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, errno,
+ ap_server_conf, "processor unbind failed %d", status);
+#endif
+
+ RAISE_SIGSTOP(MAKE_CHILD);
+
+ /* XXX - For an unthreaded server, a signal handler will be necessary
+ ap_signal(SIGTERM, just_die);
+ */
+ child_main(slot);
+
+ return 0;
+ }
+ /* else */
+ ap_child_table[slot].pid = pid;
+ ap_child_table[slot].status = SERVER_ALIVE;
+
+ return 0;
+}
+
+/* start up a bunch of children */
+static int startup_children(int number_to_start)
+{
+ int i;
+
+ for (i = 0; number_to_start && i < num_daemons; ++i) {
+ if (ap_child_table[i].status != SERVER_DEAD) {
+ continue;
+ }
+ if (make_child(ap_server_conf, i, 0) < 0) {
+ break;
+ }
+ --number_to_start;
+ }
+ return number_to_start;
+}
+
+
+/*
+ * spawn_rate is the number of children that will be spawned on the
+ * next maintenance cycle if there aren't enough servers. It is
+ * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
+ * without the need to spawn.
+ */
+static int spawn_rate = 1;
+#ifndef MAX_SPAWN_RATE
+#define MAX_SPAWN_RATE (32)
+#endif
+static int hold_off_on_exponential_spawning;
+
+static void perform_child_maintenance(void)
+{
+ int i;
+ time_t now = 0;
+ int free_length;
+ int free_slots[MAX_SPAWN_RATE];
+ int last_non_dead = -1;
+
+ /* initialize the free_list */
+ free_length = 0;
+
+ for (i = 0; i < num_daemons; ++i) {
+ if (ap_child_table[i].status == SERVER_DEAD) {
+ if (free_length < spawn_rate) {
+ free_slots[free_length] = i;
+ ++free_length;
+ }
+ }
+ else {
+ last_non_dead = i;
+ }
+
+ if (i >= ap_max_daemons_limit && free_length >= spawn_rate) {
+ break;
+ }
+ }
+ ap_max_daemons_limit = last_non_dead + 1;
+
+ if (free_length > 0) {
+ for (i = 0; i < free_length; ++i) {
+ make_child(ap_server_conf, free_slots[i], now);
+ }
+ /* the next time around we want to spawn twice as many if this
+ * wasn't good enough, but not if we've just done a graceful
+ */
+ if (hold_off_on_exponential_spawning) {
+ --hold_off_on_exponential_spawning;
+ }
+ else if (spawn_rate < MAX_SPAWN_RATE) {
+ spawn_rate *= 2;
+ }
+ }
+ else {
+ spawn_rate = 1;
+ }
+}
+
+static void server_main_loop(int remaining_children_to_start)
+{
+ int child_slot;
+ ap_wait_t status;
+ ap_proc_t pid;
+ int i;
+
+ while (!restart_pending && !shutdown_pending) {
+ ap_wait_or_timeout(&status, &pid, pconf);
+
+ if (pid.pid != -1) {
+ ap_process_child_status(&pid, status);
+ /* non-fatal death... note that it's gone in the child table and
+ * clean out the status table. */
+ child_slot = -1;
+ for (i = 0; i < ap_max_daemons_limit; ++i) {
+ if (ap_child_table[i].pid == pid.pid) {
+ int j;
+
+ child_slot = i;
+ for (j = 0; j < HARD_THREAD_LIMIT; j++) {
+ ap_perchild_force_reset_connection_status(i * HARD_THREAD_LIMIT + j);
+ }
+ break;
+ }
+ }
+ if (child_slot >= 0) {
+ ap_child_table[child_slot].status = SERVER_DEAD;
+
+ if (remaining_children_to_start
+ && child_slot < num_daemons) {
+ /* we're still doing a 1-for-1 replacement of dead
+ * children with new children
+ */
+ make_child(ap_server_conf, child_slot, time(NULL));
+ --remaining_children_to_start;
+ }
+#if APR_HAS_OTHER_CHILD
+ }
+ else if (ap_reap_other_child(&pid, status) == 0) {
+ /* handled */
+#endif
+ }
+ else if (is_graceful) {
+ /* Great, we've probably just lost a slot in the
+ * child table. Somehow we don't know about this
+ * child.
+ */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0,
+ ap_server_conf,
+ "long lost child came home! (pid %ld)",
+ (long)pid.pid);
+ }
+ /* Don't perform idle maintenance when a child dies,
+ * only do it when there's a timeout. Remember only a
+ * finite number of children can die, and it's pretty
+ * pathological for a lot to die suddenly.
+ */
+ continue;
+ }
+ else if (remaining_children_to_start) {
+ /* we hit a 1 second timeout in which none of the previous
+ * generation of children needed to be reaped... so assume
+ * they're all done, and pick up the slack if any is left.
+ */
+ remaining_children_to_start = \
+ startup_children(remaining_children_to_start);
+ /* In any event we really shouldn't do the code below because
+ * few of the servers we just started are in the IDLE state
+ * yet, so we'd mistakenly create an extra server.
+ */
+ continue;
+ }
+
+ perform_child_maintenance();
+ }
+}
+
+int ap_mpm_run(ap_pool_t *_pconf, ap_pool_t *plog, server_rec *s)
+{
+ int remaining_children_to_start;
+ int i;
+ ap_status_t rv;
+ ap_ssize_t one = 1;
+
+ pconf = _pconf;
+ ap_server_conf = s;
+ if ((rv = ap_create_pipe(&pipe_of_death_in, &pipe_of_death_out, pconf))
+ != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv,
+ (const server_rec*) ap_server_conf,
+ "ap_create_pipe (pipe_of_death)");
+ exit(1);
+ }
+ if ((rv = ap_set_pipe_timeout(pipe_of_death_in, 0)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv,
+ (const server_rec*) ap_server_conf,
+ "ap_set_pipe_timeout (pipe_of_death)");
+ exit(1);
+ }
+ ap_server_conf = s;
+ if ((num_listenfds = ap_setup_listeners(ap_server_conf)) < 1) {
+ /* XXX: hey, what's the right way for the mpm to indicate a fatal error? */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s,
+ "no listening sockets available, shutting down");
+ return 1;
+ }
+ ap_log_pid(pconf, ap_pid_fname);
+
+ /* Initialize cross-process accept lock */
+ lock_fname = ap_psprintf(_pconf, "%s.%u",
+ ap_server_root_relative(_pconf, lock_fname),
+ my_pid);
+ rv = SAFE_ACCEPT(ap_create_lock(&process_accept_mutex, APR_MUTEX,
+ APR_CROSS_PROCESS, lock_fname, _pconf));
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
+ "Couldn't create cross-process lock");
+ return 1;
+ }
+
+ if (!is_graceful) {
+ reinit_scoreboard(pconf);
+ }
+ /* Initialize the child table */
+ if (!is_graceful) {
+ for (i = 0; i < HARD_SERVER_LIMIT; i++) {
+ ap_child_table[i].status = SERVER_DEAD;
+ }
+ }
+
+ set_signals();
+
+ /* If we're doing a graceful_restart then we're going to see a lot
+ * of children exiting immediately when we get into the main loop
+ * below (because we just sent them SIGWINCH). This happens pretty
+ * rapidly... and for each one that exits we'll start a new one until
+ * we reach at least daemons_min_free. But we may be permitted to
+ * start more than that, so we'll just keep track of how many we're
+ * supposed to start up without the 1 second penalty between each fork.
+ */
+ remaining_children_to_start = num_daemons;
+ if (!is_graceful) {
+ remaining_children_to_start = \
+ startup_children(remaining_children_to_start);
+ }
+ else {
+ /* give the system some time to recover before kicking into
+ * exponential mode */
+ hold_off_on_exponential_spawning = 10;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
+ "%s configured -- resuming normal operations",
+ ap_get_server_version());
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, ap_server_conf,
+ "Server built: %s", ap_get_server_built());
+ restart_pending = shutdown_pending = 0;
+
+ server_main_loop(remaining_children_to_start);
+
+ if (shutdown_pending) {
+ /* Time to gracefully shut down:
+ * Kill child processes, tell them to call child_exit, etc...
+ */
+ if (unixd_killpg(getpgrp(), SIGTERM) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ "killpg SIGTERM");
+ }
+ ap_reclaim_child_processes(1); /* Start with SIGTERM */
+
+ /* cleanup pid file on normal shutdown */
+ {
+ const char *pidfile = NULL;
+ pidfile = ap_server_root_relative (pconf, ap_pid_fname);
+ if ( pidfile != NULL && unlink(pidfile) == 0)
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0,
+ ap_server_conf,
+ "removed PID file %s (pid=%ld)",
+ pidfile, (long)getpid());
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0,
+ ap_server_conf, "caught SIGTERM, shutting down");
+
+ return 1;
+ }
+
+ /* we've been told to restart */
+ ap_signal(SIGHUP, SIG_IGN);
+
+ if (one_process) {
+ /* not worth thinking about */
+ return 1;
+ }
+
+ if (is_graceful) {
+ char char_of_death = '!';
+
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
+ "SIGWINCH received. Doing graceful restart");
+
+ /* This is mostly for debugging... so that we know what is still
+ * gracefully dealing with existing request.
+ */
+
+ for (i = 0; i < num_daemons; ++i) {
+ if (ap_child_table[i].status != SERVER_DEAD) {
+ ap_child_table[i].status = SERVER_DYING;
+ }
+ }
+ /* give the children the signal to die */
+ for (i = 0; i < num_daemons;) {
+ if ((rv = ap_write(pipe_of_death_out, &char_of_death, &one)) != APR_SUCCESS) {
+ if (ap_canonical_error(rv) == APR_EINTR) continue;
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
+ "write pipe_of_death");
+ }
+ i++;
+ }
+ }
+ else {
+ /* Kill 'em all. Since the child acts the same on the parents SIGTERM
+ * and a SIGHUP, we may as well use the same signal, because some user
+ * pthreads are stealing signals from us left and right.
+ */
+ if (unixd_killpg(getpgrp(), SIGTERM) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ "killpg SIGTERM");
+ }
+ ap_reclaim_child_processes(1); /* Start with SIGTERM */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0,
+ ap_server_conf, "SIGHUP received. Attempting to restart");
+ }
+ return 0;
+}
+
+static void perchild_pre_config(ap_pool_t *p, ap_pool_t *plog, ap_pool_t *ptemp)
+{
+ static int restart_num = 0;
+
+ one_process = !!getenv("ONE_PROCESS");
+
+ /* sigh, want this only the second time around */
+ if (restart_num++ == 1) {
+ is_graceful = 0;
+
+ if (!one_process) {
+ ap_detach();
+ }
+
+ my_pid = getpid();
+ }
+
+ unixd_pre_config();
+ ap_listen_pre_config();
+ num_daemons = DEFAULT_NUM_DAEMON;
+ threads_to_start = DEFAULT_START_THREAD;
+ min_spare_threads = DEFAULT_MIN_SPARE_THREAD;
+ max_spare_threads = DEFAULT_MAX_SPARE_THREAD;
+ max_threads = HARD_THREAD_LIMIT;
+ ap_pid_fname = DEFAULT_PIDLOG;
+ ap_scoreboard_fname = DEFAULT_SCOREBOARD;
+ lock_fname = DEFAULT_LOCKFILE;
+ max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
+ ap_perchild_set_maintain_connection_status(1);
+
+ ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
+}
+
+static void perchild_hooks(void)
+{
+ INIT_SIGLIST()
+ one_process = 0;
+
+ ap_hook_pre_config(perchild_pre_config, NULL, NULL, AP_HOOK_MIDDLE);
+}
+
+static const char *set_pidfile(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (cmd->server->is_virtual) {
+ return "PidFile directive not allowed in <VirtualHost>";
+ }
+ ap_pid_fname = arg;
+ return NULL;
+}
+
+static const char *set_scoreboard(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_scoreboard_fname = arg;
+ return NULL;
+}
+
+static const char *set_lockfile(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ lock_fname = arg;
+ return NULL;
+}
+static const char *set_num_daemons (cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ num_daemons = atoi(arg);
+ if (num_daemons > HARD_SERVER_LIMIT) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "WARNING: NumServers of %d exceeds compile time limit "
+ "of %d servers,", num_daemons, HARD_SERVER_LIMIT);
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ " lowering NumServers to %d. To increase, please "
+ "see the", HARD_SERVER_LIMIT);
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ " HARD_SERVER_LIMIT define in %s.",
+ AP_MPM_HARD_LIMITS_FILE);
+ num_daemons = HARD_SERVER_LIMIT;
+ }
+ else if (num_daemons < 1) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "WARNING: Require NumServers > 0, setting to 1");
+ num_daemons = 1;
+ }
+ return NULL;
+}
+
+static const char *set_threads_to_start (cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ threads_to_start = atoi(arg);
+ if (threads_to_start > HARD_THREAD_LIMIT) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "WARNING: StartThreads of %d exceeds compile time"
+ " limit of %d threads,", threads_to_start,
+ HARD_THREAD_LIMIT);
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ " lowering StartThreads to %d. To increase, please"
+ " see the", HARD_THREAD_LIMIT);
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ " HARD_THREAD_LIMIT define in %s.",
+ AP_MPM_HARD_LIMITS_FILE);
+ }
+ else if (threads_to_start < 1) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "WARNING: Require StartThreads > 0, setting to 1");
+ threads_to_start = 1;
+ }
+ return NULL;
+}
+
+static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ min_spare_threads = atoi(arg);
+ if (min_spare_threads <= 0) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "WARNING: detected MinSpareThreads set to non-positive.");
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "Resetting to 1 to avoid almost certain Apache failure.");
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "Please read the documentation.");
+ min_spare_threads = 1;
+ }
+
+ return NULL;
+}
+
+static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ max_spare_threads = atoi(arg);
+ if (max_spare_threads >= HARD_THREAD_LIMIT) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "WARNING: detected MinSpareThreads set higher than");
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "HARD_THREAD_LIMIT. Resetting to %d", HARD_THREAD_LIMIT);
+ max_spare_threads = HARD_THREAD_LIMIT;
+ }
+ return NULL;
+}
+
+static const char *set_max_threads(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ max_threads = atoi(arg);
+ if (max_threads > HARD_THREAD_LIMIT) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "WARNING: detected MaxThreadsPerChild set higher than");
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "HARD_THREAD_LIMIT. Resetting to %d", HARD_THREAD_LIMIT);
+ max_threads = HARD_THREAD_LIMIT;
+ }
+ return NULL;
+}
+
+static const char *set_max_requests(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ max_requests_per_child = atoi(arg);
+
+ return NULL;
+}
+
+static const char *set_maintain_connection_status(cmd_parms *cmd,
+ void *dummy, int arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_perchild_set_maintain_connection_status(arg != 0);
+ return NULL;
+}
+
+static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, const char *arg)
+{
+ ap_finfo_t finfo;
+ const char *fname;
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ fname = ap_server_root_relative(cmd->pool, arg);
+ if ((ap_stat(&finfo, fname, cmd->pool) != APR_SUCCESS) ||
+ (finfo.filetype != APR_DIR)) {
+ return ap_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
+ " does not exist or is not a directory", NULL);
+ }
+ ap_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir));
+ return NULL;
+}
+static const char *set_childprocess(cmd_parms *cmd, void *dummy, const char *arg)
+{
+}
+/*
+ if (unlink(sconf->sockname) < 0 &&
+ errno != ENOENT) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
+ "Couldn't unlink unix domain socket %s",
+ sconf->sockname);
+ /* just a warning; don't bail out
+ }
+
+ if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
+ "Couldn't create unix domain socket");
+ return errno;
+ }
+
+ memset(&unix_addr, 0, sizeof(unix_addr));
+ unix_addr.sun_family = AF_UNIX;
+ strcpy(unix_addr.sun_path, sconf->sockname);
+
+ omask = umask(0077); /* so that only Apache can use socket
+ rc = bind(sd, (struct sockaddr *)&unix_addr, sizeof(unix_addr));
+*/
+
+
+
+
+
+
+static const command_rec perchild_cmds[] = {
+UNIX_DAEMON_COMMANDS
+LISTEN_COMMANDS
+AP_INIT_TAKE1("PidFile", set_pidfile, NULL, RSRC_CONF,
+ "A file for logging the server process ID"),
+AP_INIT_TAKE1("ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF,
+ "A file for Apache to maintain runtime process management information"),
+AP_INIT_TAKE1("LockFile", set_lockfile, NULL, RSRC_CONF,
+ "The lockfile used when Apache needs to lock the accept() call"),
+AP_INIT_TAKE1("NumServers", set_num_daemons, NULL, RSRC_CONF,
+ "Number of children alive at the same time"),
+AP_INIT_TAKE1("StartThreads", set_threads_to_start, NULL, RSRC_CONF,
+ "Number of threads each child creates"),
+AP_INIT_TAKE1("MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF,
+ "Minimum number of idle threads per child, to handle request spikes"),
+AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
+ "Maximum number of idle threads per child"),
+AP_INIT_TAKE1("MaxThreadsPerChild", set_max_threads, NULL, RSRC_CONF,
+ "Maximum number of threads per child"),
+AP_INIT_TAKE1("MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF,
+ "Maximum number of requests a particular child serves before dying."),
+AP_INIT_FLAG("ConnectionStatus", set_maintain_connection_status, NULL, RSRC_CONF,
+ "Whether or not to maintain status information on current connections"),
+AP_INIT_TAKE1("CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF,
+ "The location of the directory Apache changes to before dumping core"),
+AP_INIT_TAKE1("ChildProcess", set_childprocess, NULL, RSRC_CONF,
+ "Which child process is responsible for serving this virtual host"),
+{ NULL }
+};
+
+module MODULE_VAR_EXPORT mpm_perchild_module = {
+ MPM20_MODULE_STUFF,
+ NULL, /* hook to run before apache parses args */
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ perchild_cmds, /* command ap_table_t */
+ NULL, /* handlers */
+ perchild_hooks /* register_hooks */
+};
+
diff --git a/server/mpm/monitoring-services.txt b/server/mpm/monitoring-services.txt
new file mode 100644
index 0000000000..2466589025
--- /dev/null
+++ b/server/mpm/monitoring-services.txt
@@ -0,0 +1,94 @@
+From: William A. Rowe, Jr.
+Date: June 7th '00
+Subject: service monitoring in Apache 1.3.13
+
+The concept for a taskbar monitor has been thrown around
+for a very long while. 1.3.13 introduced Win9x services,
+and that added fuel to the mix. Here are some sideband
+observations I've made for other developers...
+
+About Apache as a console, don't start Apache hidden without
+any command line arguments if you want to launch it yourself
+in a hidden window (it will do the classic test for
+AllocConsole/FreeConsole)... drop in some arguments such as
+the -f or -r option and it will fly without thinking it is a
+service under 9x and NT.
+
+Rule two, don't use --ntservice as an argument, ever. Only
+the Windows NT Service Control Manager is allowed to pass that
+flag, and only that flag, when it runs Apache.exe. Do use
+--ntservice as the sole argument to the executable name if
+you are installing an Apache NT service yourself.
+
+Rule three, use -k start and -n name when maintaining the
+HKLM/Software/Microsoft/Windows/CurrentVersion/RunServices
+list, since there is no other way for Apache to know what
+the service is named :) And look at any 9x installed service's
+RunServices entry in the registry for the start service semantic.
+
+Rule four, use the WinNT Service Control Manager exclusively
+for starting, stopping and restarting Apache as an NT service.
+The restart signal is the value 128, as documented in service.h
+and service.c - this will continue to work in Apache 2.0. If
+it fails, you are handling an older version (pre 1.3.13) of
+Apache, and need to stop and then start the service instead.
+
+Rule five, use the legacy pid-named events to signal Win9x
+service Apache to restart and stop the service. But don't
+bother looking for httpd.pid files... you can get the pid
+right from the hidden service control window. Apache 1.3.13
+and 2.x create a hidden window named for the name of the
+service (without the spaces), with a window class of
+"ApacheWin95ServiceMonitor", so can use FindWindow to track
+down running Win9x services. See the service.c code for how
+I accomplished this pretty simply in the -k stop/-k restart
+handler.
+
+Taskbar Monitor App
+-------------------
+
+Basic requirements: a C code application using strictly the
+Win32 API, and not MFC or other Win32 frameworks. Could use
+the service.c module to share some basic functions. That
+module could be extended in Apache 2.0 to make this all easier.
+
+I think we are looking for an external app that simply acts
+as a monitor or allows a stopped service to be started. If
+the user logs off, we loose the monitor app, but installed as
+a shortcut in the Start group or in the registry key
+HKLM/Software/Microsoft/Windows/CurrentVersion/Run
+we will be just fine. I'd like to see the monitor run only
+one instance to monitor all running services, for memory
+and resource conservation.
+
+I was thinking that the hover/iconbar title would tell them
+"Test service is running", or "Test service is stopped".
+If they left click, they could stop or restart, or simply
+start if it is stopped. There could be a preference that
+each service doesn't get it's own individual task icon unless
+it is running, if it is a manual start service (or missing
+from the RunServices list, which is the equivilant under 9x).
+
+If a specific service is set to Auto start or is in the
+RunServices Win9x registry key, we must show them the stopped
+icon, of course. We might also keep the icon for any running
+service that stops abruptly. But there could be a 'single
+icon' option for the taskbar icon monitor that says show only
+a single status icon, for simplicity if the administrator runs
+many Apache services.
+
+But I was hoping that any right click would provide a menu
+of all Apache services with their status. e.g.
+ Test service is stopped
+ Apache_2 service is running
+ MyWeb service is running
+and each would do the logical submenu, same as if that
+specific taskbar icon were left clicked, offering to start or
+offering to stop or restart the server, as appropriate.
+
+Finally, to identify all installed Apache services, just query
+the registry key HKLM\SYSTEM\CurrentControlSet\Services for any
+key that has the ImagePath value of "...\Apache.exe"... (quotes
+are significant here, if the leading quote is ommitted the
+entire string ends with the text \Apache.exe - based on Apache's
+own service installer in every released version.)
diff --git a/server/mpm/prefork/mpm.h b/server/mpm/prefork/mpm.h
new file mode 100644
index 0000000000..df8ea97d62
--- /dev/null
+++ b/server/mpm/prefork/mpm.h
@@ -0,0 +1,72 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "httpd.h"
+#include "mpm_default.h"
+#include "scoreboard.h"
+
+#ifndef APACHE_MPM_PREFORK_H
+#define APACHE_MPM_PREFORK_H
+
+#define PREFORK_MPM
+
+extern int ap_max_daemons_limit;
+extern scoreboard *ap_scoreboard_image;
+extern server_rec *ap_server_conf;
+extern int ap_my_pid;
+#endif /* APACHE_MPM_DEXTER_H */
diff --git a/server/mpm/winnt/Win9xConHook.c b/server/mpm/winnt/Win9xConHook.c
new file mode 100644
index 0000000000..7f1224ab6a
--- /dev/null
+++ b/server/mpm/winnt/Win9xConHook.c
@@ -0,0 +1,517 @@
+/* ====================================================================
+ * Copyright (c) 1995-2000 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://httpd.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://httpd.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+
+/*
+ * Win9xConHook.dll - a hook proc to clean up Win95/98 console behavior.
+ *
+ * It is well(?) documented by Microsoft that the Win9x HandlerRoutine
+ * hooked by the SetConsoleCtrlHandler never receives the CTRL_CLOSE_EVENT,
+ * CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT signals.
+ *
+ * It is possible to have a second window to monitor the WM_ENDSESSION
+ * message, but the close button still fails..
+ *
+ * There is a 16bit polling method for the close window option, but this
+ * is CPU intensive and requires thunking.
+ *
+ * Attempts to subclass the 'tty' console fail, since that message thread
+ * is actually owned by the 16 bit winoldap.mod process, although the
+ * window reports it is owned by the process/thread of the console app.
+ *
+ * Win9xConHook is thunks the WM_CLOSE and WM_ENDSESSION messages,
+ * first through a window hook procedure in the winoldap context, into
+ * a subclass WndProc, and on to a second hidden monitor window in the
+ * console application's context that dispatches them to the console app's
+ * registered HandlerRoutine.
+ */
+
+#define DBG 1
+
+#include <windows.h>
+
+/*
+ * is_tty flags this process; -1 == unknown, 1 == if tty, 0 == if not
+ * hw_tty is the handle of the top level tty in this process context
+ * is_subclassed is toggled to assure DllMain removes the subclass
+ * is_hooked is toggled to assure DllMain removes the subclass
+ */
+static int is_tty = -1;
+static HWND hwtty = NULL;
+static BOOL is_subclassed = 0;
+
+static HMODULE hmodHook = NULL;
+static HHOOK hhkGetMessage;
+//static HHOOK hhkCallWndProc;
+
+static LPCTSTR origwndprop = NULL;
+static LPCTSTR hookwndprop = NULL;
+
+#ifdef DBG
+static VOID DbgPrintf(LPTSTR fmt, ...);
+#endif
+
+static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd);
+
+
+BOOL __declspec(dllexport) APIENTRY DllMain(PVOID hModule, ULONG ulReason, PCONTEXT pctx)
+{
+ if (ulReason == DLL_PROCESS_ATTACH)
+ {
+#ifdef DBG
+ DbgPrintf("H ProcessAttach:%8.8x\r\n", GetCurrentProcessId());
+#endif
+ origwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookOrigProc"));
+ hookwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookThunkWnd"));
+ }
+ else if ( ulReason == DLL_PROCESS_DETACH )
+ {
+ HWND parent;
+#ifdef DBG
+ DbgPrintf("H ProcessDetach:%8.8x\r\n", GetCurrentProcessId());
+#endif
+ if (is_subclassed) {
+ WNDPROC origproc = (WNDPROC) GetProp(hwtty, origwndprop);
+ if (origproc) {
+ SetWindowLong(hwtty, GWL_WNDPROC, (LONG)origproc);
+ RemoveProp(hwtty, origwndprop);
+ }
+ }
+ EnumWindows(EnumttyWindow, (LPARAM)&parent);
+ if (parent) {
+ HWND child = (HWND)GetProp(parent, hookwndprop);
+ if (child)
+ SendMessage(child, WM_DESTROY, 0, 0);
+ }
+ if (hmodHook)
+ {
+ if (hhkGetMessage) {
+ UnhookWindowsHookEx(hhkGetMessage);
+ hhkGetMessage = NULL;
+ }
+ FreeLibrary(hmodHook);
+ hmodHook = NULL;
+ }
+ GlobalDeleteAtom((ATOM)origwndprop);
+ GlobalDeleteAtom((ATOM)hookwndprop);
+ }
+ return TRUE;
+}
+
+
+typedef struct {
+ PHANDLER_ROUTINE phandler;
+ HINSTANCE instance;
+ HWND parent;
+} tty_info;
+
+
+#define gwltty_phandler 0
+#define gwltty_ttywnd 4
+
+/* This is the WndProc procedure for our invisible window.
+ * When our tty subclass WndProc recieves the WM_CLOSE,
+ * or WM_QUERYENDSESSION messages, we call the installed
+ * HandlerRoutine that was registered with
+ * If a user logs off, the window is sent WM_QUERYENDSESSION
+ * as well, but with lParam != 0. We ignore this case.
+ */
+LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_CREATE)
+ {
+ tty_info *tty = (tty_info*)(((LPCREATESTRUCT)lParam)->lpCreateParams);
+ SetWindowLong(hwnd, gwltty_phandler, (LONG)tty->phandler);
+ SetWindowLong(hwnd, gwltty_ttywnd, (LONG)tty->parent);
+#ifdef DBG
+ DbgPrintf("S Created ttyConHookChild:%8.8x\r\n", hwnd);
+#endif
+ SetProp(((tty_info*)tty)->parent, hookwndprop, hwnd);
+ return 0;
+ }
+ else if (msg == WM_DESTROY)
+ {
+ HWND parent = (HWND)GetWindowLong(hwnd, gwltty_ttywnd);
+ RemoveProp(parent, hookwndprop);
+ }
+ else if (msg == WM_CLOSE)
+ {
+ PHANDLER_ROUTINE phandler =
+ (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
+#ifdef DBG
+ DbgPrintf("S Invoking CTRL_CLOSE_EVENT:%8.8x\r\n",
+ GetCurrentProcessId());
+#endif
+ return !phandler(CTRL_CLOSE_EVENT);
+ }
+ else if (msg == WM_QUERYENDSESSION)
+ {
+ if (lParam & ENDSESSION_LOGOFF)
+ {
+ PHANDLER_ROUTINE phandler =
+ (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
+#ifdef DBG
+ DbgPrintf("S Invoking CTRL_LOGOFF_EVENT:%8.8x\r\n",
+ GetCurrentProcessId());
+#endif
+ return !phandler(CTRL_LOGOFF_EVENT);
+ }
+ else
+ {
+ PHANDLER_ROUTINE phandler =
+ (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
+#ifdef DBG
+ DbgPrintf("S Invoking CTRL_SHUTDOWN_EVENT:%8.8x\r\n",
+ GetCurrentProcessId());
+#endif
+ return !phandler(CTRL_SHUTDOWN_EVENT);
+ }
+ }
+ return (DefWindowProc(hwnd, msg, wParam, lParam));
+}
+
+
+DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty)
+{
+ /* When running as a service under Windows 9x, ConsoleCtrlHandler
+ * does not respond properly when the user logs off or the system
+ * is shutdown. If the WatchWindow thread is created with a NULL
+ * service_name argument, then the ...SystemMonitor window class is
+ * used to create the "Apache" window to watch for logoff and shutdown.
+ * If the service_name is provided, the ...ServiceMonitor window class
+ * is used to create the window named by the service_name argument,
+ * and the logoff message is ignored.
+ */
+ WNDCLASS wc;
+ HWND hwnd;
+ MSG msg;
+ wc.style = CS_GLOBALCLASS;
+ wc.lpfnWndProc = ttyConsoleCtrlWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 8;
+ wc.hInstance = NULL;
+ wc.hIcon = NULL;
+ wc.hCursor = NULL;
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = "ttyConHookChild";
+
+ if (!RegisterClass(&wc)) {
+#ifdef DBG
+ DbgPrintf("S Created ttyConHookChild class\r\n");
+#endif
+ return 0;
+ }
+
+ /* Create an invisible window */
+ hwnd = CreateWindow(wc.lpszClassName, "",
+ WS_OVERLAPPED & ~WS_VISIBLE,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL,
+ ((tty_info*)tty)->instance, tty);
+
+ if (!hwnd) {
+#ifdef DBG
+ DbgPrintf("S Error Creating ttyConHookChild:%d\r\n", GetLastError());
+#endif
+ return 0;
+ }
+
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ if (msg.message == WM_DESTROY)
+ DestroyWindow(hwnd);
+ }
+ return 0;
+}
+
+
+/*
+ * This function only works when this process is the active process
+ * (e.g. once it is running a child process, it can no longer determine
+ * which console window is its own.)
+ */
+static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd)
+{
+ char tmp[4];
+ if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, "tty"))
+ {
+ DWORD wndproc, thisproc = GetCurrentProcessId();
+ GetWindowThreadProcessId(wnd, &wndproc);
+ if (wndproc == thisproc) {
+ *((HWND*)retwnd) = wnd;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+/*
+ * Exported function that sets up the fixup child window and dispatch
+ */
+BOOL __declspec(dllexport) WINAPI FixConsoleCtrlHandler(
+ PHANDLER_ROUTINE phandler,
+ BOOL add)
+{
+ HWND parent;
+ EnumWindows(EnumttyWindow, (LPARAM)&parent);
+
+ if (!parent)
+ return FALSE;
+
+ if (add)
+ {
+ HANDLE hThread;
+ DWORD tid;
+ static tty_info tty;
+ tty.phandler = phandler;
+ tty.parent = parent;
+ tty.instance = GetModuleHandle(NULL);
+
+ hmodHook = LoadLibrary("Win9xConHook.dll");
+ if (hmodHook)
+ {
+ hhkGetMessage = SetWindowsHookEx(WH_GETMESSAGE,
+ (HOOKPROC)GetProcAddress(hmodHook, "GetMsgProc"), hmodHook, 0);
+ //hhkCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC,
+ // (HOOKPROC)GetProcAddress(hmodHook, "CallWndProc"), hmodHook, 0);
+ }
+
+ hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread,
+ (LPVOID)&tty, 0, &tid);
+ if (hThread)
+ {
+ CloseHandle(hThread);
+ return TRUE;
+ }
+ }
+ else /* remove */
+ {
+ HWND child = FindWindowEx(parent, NULL, "ttyConHookChild", NULL);
+ if (child)
+ SendMessage(child, WM_DESTROY, 0, 0);
+ if (hmodHook)
+ {
+ if (hhkGetMessage) {
+ UnhookWindowsHookEx(hhkGetMessage);
+ hhkGetMessage = NULL;
+ }
+ FreeLibrary(hmodHook);
+ hmodHook = NULL;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ * Subclass message process for the tty window
+ */
+LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ WNDPROC origproc = (WNDPROC) GetProp(hwnd, origwndprop);
+ if (!origproc)
+ return 0;
+
+ switch (msg)
+ {
+ case WM_NCDESTROY:
+#ifdef DBG
+ DbgPrintf("W Proc %08x hwnd:%08x Subclass removed\r\n",
+ GetCurrentProcessId(), hwnd);
+#endif
+ is_subclassed = FALSE;
+ SetWindowLong(hwnd, GWL_WNDPROC, (LONG)origproc);
+ RemoveProp(hwnd, origwndprop);
+ break;
+
+ case WM_CLOSE:
+ case WM_ENDSESSION:
+ case WM_QUERYENDSESSION:
+ {
+ HWND child = (HWND)GetProp(hwnd, hookwndprop);
+#ifdef DBG
+ DbgPrintf("W Proc %08x hwnd:%08x msg:%d\r\n",
+ GetCurrentProcessId(), hwnd, msg);
+#endif
+ if (!child)
+ break;
+ return SendMessage(child, msg, wParam, lParam);
+ }
+ }
+ return CallWindowProc(origproc, hwnd, msg, wParam, lParam);
+}
+
+
+int HookProc(int hc, HWND *hwnd, UINT *msg, WPARAM *wParam, LPARAM *lParam)
+{
+ if (is_tty == -1 && *hwnd)
+ {
+ char ttybuf[4];
+ HWND htty;
+ hwtty = *hwnd;
+ while (htty = GetParent(hwtty))
+ hwtty = htty;
+ is_tty = (GetClassName(hwtty, ttybuf, sizeof(ttybuf))
+ && !strcmp(ttybuf, "tty"));
+ if (is_tty)
+ {
+ WNDPROC origproc = (WNDPROC)GetWindowLong(hwtty, GWL_WNDPROC);
+ SetProp(hwtty, origwndprop, origproc);
+ SetWindowLong(hwtty, GWL_WNDPROC, (LONG)WndProc);
+ is_subclassed = TRUE;
+#ifdef DBG
+ DbgPrintf("W Proc %08x hwnd:%08x Subclassed\r\n",
+ GetCurrentProcessId(), hwtty);
+#endif
+ }
+#ifdef DBG
+ DbgPrintf("H Proc %08x %s %08x\r\n", GetCurrentProcessId(),
+ is_tty ? "tracking" : "ignoring", hwtty);
+#endif
+ }
+
+ if (hc >= 0 && is_tty && *hwnd == hwtty)
+ {
+ if ((*msg == WM_CLOSE)
+ || (*msg == WM_ENDSESSION)) {
+ DWORD apppid, ttypid = GetCurrentProcessId();
+ GetWindowThreadProcessId(*hwnd, &apppid);
+#ifdef DBG
+ DbgPrintf("H Proc %08x hwnd:%08x owned by %08x msg:%d\r\n", ttypid, *hwnd, apppid, *msg);
+#endif
+ //*msg = WM_NULL;
+ /*
+ * Experimental, return 0 or 1 will bypass the next hook and return that
+ * value from the hook procedure, -1 continues to call the next hook.
+ */
+ return -1;
+ }
+ }
+ return -1;
+}
+
+
+/*
+ * PostMessage Hook:
+ */
+LRESULT __declspec(dllexport) CALLBACK GetMsgProc(INT hc, WPARAM wParam, LPARAM lParam)
+{
+ PMSG pmsg;
+
+ pmsg = (PMSG)lParam;
+
+ if (pmsg) {
+ int rv = HookProc(hc, &pmsg->hwnd, &pmsg->message, &pmsg->lParam, &pmsg->wParam);
+ if (rv != -1)
+ return rv;
+ }
+ /*
+ * CallNextHookEx apparently ignores the hhook argument, so pass NULL
+ */
+ return CallNextHookEx(NULL, hc, wParam, lParam);
+}
+
+
+/*
+ * SendMessage Hook:
+ */
+LRESULT __declspec(dllexport) CALLBACK CallWndProc(INT hc, WPARAM wParam, LPARAM lParam)
+{
+ PCWPSTRUCT pcwps = (PCWPSTRUCT)lParam;
+
+ if (pcwps) {
+ int rv = HookProc(hc, &pcwps->hwnd, &pcwps->message, &pcwps->wParam, &pcwps->lParam);
+ if (rv != -1)
+ return rv;
+ }
+ /*
+ * CallNextHookEx apparently ignores the hhook argument, so pass NULL
+ */
+ return CallNextHookEx(NULL, hc, wParam, lParam);
+}
+
+
+#ifdef DBG
+VOID DbgPrintf(
+ LPTSTR fmt,
+ ...
+ )
+{
+ va_list marker;
+ TCHAR szBuf[256];
+ DWORD t;
+ HANDLE gDbgOut;
+
+ va_start(marker, fmt);
+ wvsprintf(szBuf, fmt, marker);
+ va_end(marker);
+
+ gDbgOut = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL);
+ WriteFile(gDbgOut, szBuf, strlen(szBuf), &t, NULL);
+ CloseHandle(gDbgOut);
+}
+#endif
+
+
diff --git a/server/mpm/winnt/Win9xConHook.def b/server/mpm/winnt/Win9xConHook.def
new file mode 100644
index 0000000000..689eb75a2c
--- /dev/null
+++ b/server/mpm/winnt/Win9xConHook.def
@@ -0,0 +1,9 @@
+LIBRARY Win9xConHook
+
+EXETYPE WINDOWS
+
+EXPORTS
+ DllMain
+ GetMsgProc
+ CallWndProc
+ FixConsoleCtrlHandler \ No newline at end of file
diff --git a/server/mpm/winnt/Win9xConHook.dsp b/server/mpm/winnt/Win9xConHook.dsp
new file mode 100644
index 0000000000..3704f5b62e
--- /dev/null
+++ b/server/mpm/winnt/Win9xConHook.dsp
@@ -0,0 +1,103 @@
+# Microsoft Developer Studio Project File - Name="Win9xConHook" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=Win9xConHook - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "Win9xConHook.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "Win9xConHook.mak" CFG="Win9xConHook - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Win9xConHook - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "Win9xConHook - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "Win9xConHook - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Win9xConHookR"
+# PROP Intermediate_Dir "Win9xConHookR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 /base:@"BaseAddr.ref",mod_status
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /base:"0x1c0f0000" /subsystem:windows /dll /map /machine:I386
+
+!ELSEIF "$(CFG)" == "Win9xConHook - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Win9xConHookD"
+# PROP Intermediate_Dir "Win9xConHookD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /base:@"BaseAddr.ref",mod_status
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /base:"0x1c0f0000" /subsystem:windows /dll /incremental:no /map /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "Win9xConHook - Win32 Release"
+# Name "Win9xConHook - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\Win9xConHook.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\Win9xConHook.def
+# End Source File
+# Begin Source File
+
+SOURCE=.\Win9xConHook.h
+# End Source File
+# End Target
+# End Project
diff --git a/server/mpm/winnt/Win9xConHook.h b/server/mpm/winnt/Win9xConHook.h
new file mode 100644
index 0000000000..107cdbe2f5
--- /dev/null
+++ b/server/mpm/winnt/Win9xConHook.h
@@ -0,0 +1,24 @@
+
+
+
+/*
+ * FixConsoleControlHandler will register a handler routine with the
+ * Win9xConHook.dll, creating a hidden window and forwarding the
+ * WM_ENDSESSION and WM_CLOSE messages to the registered handler
+ * as CTRL_SHUTDOWN_EVENT, CTRL_LOGOFF_EVENT and CTRL_CLOSE_EVENT.
+ */
+BOOL WINAPI FixConsoleCtrlHandler(
+ PHANDLER_ROUTINE phandler,
+ BOOL add);
+
+/*
+ * PostMessage Hook:
+ */
+LRESULT CALLBACK GetMsgProc(INT hc, WPARAM wParam, LPARAM lParam);
+
+
+/*
+ * SendMessage Hook:
+ */
+LRESULT CALLBACK CallWndProc(INT hc, WPARAM wParam, LPARAM lParam);
+
diff --git a/server/mpm/winnt/mpm.h b/server/mpm/winnt/mpm.h
new file mode 100644
index 0000000000..a75dba6682
--- /dev/null
+++ b/server/mpm/winnt/mpm.h
@@ -0,0 +1,63 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef APACHE_MPM_WINNT_H
+#define APACHE_MPM_WINNT_H
+
+
+#endif /* APACHE_MPM_WINNT_H */
diff --git a/server/mpm_common.c b/server/mpm_common.c
new file mode 100644
index 0000000000..3201b4c72b
--- /dev/null
+++ b/server/mpm_common.c
@@ -0,0 +1,170 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/* The purpose of this file is to store the code that MOST mpm's will need
+ * this does not mean a function only goes into this file if every MPM needs
+ * it. It means that if a function is needed by more than one MPM, and
+ * future maintenance would be served by making the code common, then the
+ * function belongs here.
+ *
+ * This is going in src/main because it is not platform specific, it is
+ * specific to multi-process servers, but NOT to Unix. Which is why it
+ * does not belong in src/os/unix
+ */
+
+#include "apr_thread_proc.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "mpm.h"
+
+#ifdef DEXTER_MPM
+#define CHILD_INFO_TABLE ap_child_table
+#elif defined(MPMT_PTHREAD_MPM) || defined (PREFORK_MPM)
+#define CHILD_INFO_TABLE ap_scoreboard_image->parent
+#endif
+
+
+void ap_reclaim_child_processes(int terminate)
+{
+ int i, status;
+ long int waittime = 1024 * 16; /* in usecs */
+ struct timeval tv;
+ int waitret, tries;
+ int not_dead_yet;
+
+#ifndef DEXTER_MPM
+ ap_sync_scoreboard_image();
+#endif
+
+ for (tries = terminate ? 4 : 1; tries <= 9; ++tries) {
+ /* don't want to hold up progress any more than
+ * necessary, but we need to allow children a few moments to exit.
+ * Set delay with an exponential backoff.
+ */
+ tv.tv_sec = waittime / 1000000;
+ tv.tv_usec = waittime % 1000000;
+ waittime = waittime * 4;
+ ap_select(0, NULL, NULL, NULL, &tv);
+
+ /* now see who is done */
+ not_dead_yet = 0;
+ for (i = 0; i < ap_max_daemons_limit; ++i) {
+ int pid = CHILD_INFO_TABLE[i].pid;
+
+#ifdef DEXTER_MPM
+ if (ap_child_table[i].status == SERVER_DEAD)
+#elif defined(MPMT_PTHREAD_MPM) || defined (PREFORK_MPM)
+ if (pid == ap_my_pid || pid == 0)
+#endif
+ continue;
+
+ waitret = waitpid(pid, &status, WNOHANG);
+ if (waitret == pid || waitret == -1) {
+#ifdef DEXTER_MPM
+ ap_child_table[i].status = SERVER_DEAD;
+#elif defined(MPMT_PTHREAD_MPM) || defined(PREFORK_MPM)
+ ap_scoreboard_image->parent[i].pid = 0;
+#endif
+ continue;
+ }
+ ++not_dead_yet;
+ switch (tries) {
+ case 1: /* 16ms */
+ case 2: /* 82ms */
+ break;
+ case 3: /* 344ms */
+ case 4: /* 16ms */
+ case 5: /* 82ms */
+ case 6: /* 344ms */
+ case 7: /* 1.4sec */
+ /* ok, now it's being annoying */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING,
+ 0, ap_server_conf,
+ "child process %d still did not exit, sending a SIGTERM",
+ pid);
+ kill(pid, SIGTERM);
+ break;
+ case 8: /* 6 sec */
+ /* die child scum */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, ap_server_conf,
+ "child process %d still did not exit, sending a SIGKILL",
+ pid);
+ kill(pid, SIGKILL);
+ break;
+ case 9: /* 14 sec */
+ /* gave it our best shot, but alas... If this really
+ * is a child we are trying to kill and it really hasn't
+ * exited, we will likely fail to bind to the port
+ * after the restart.
+ */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, ap_server_conf,
+ "could not make child process %d exit, "
+ "attempting to continue anyway", pid);
+ break;
+ }
+ }
+ ap_check_other_child();
+ if (!not_dead_yet) {
+ /* nothing left to wait for */
+ break;
+ }
+ }
+}
+
+
diff --git a/server/protocol.c b/server/protocol.c
new file mode 100644
index 0000000000..f3b1e7e685
--- /dev/null
+++ b/server/protocol.c
@@ -0,0 +1,1594 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/*
+ * http_protocol.c --- routines which directly communicate with the client.
+ *
+ * Code originally by Rob McCool; much redone by Robert S. Thau
+ * and the Apache Software Foundation.
+ */
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_lib.h"
+#include "apr_signal.h"
+
+#define APR_WANT_STDIO /* for sscanf */
+#define APR_WANT_STRFUNC
+#define APR_WANT_MEMFUNC
+#include "apr_want.h"
+
+#define CORE_PRIVATE
+#include "util_filter.h"
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_request.h"
+#include "http_vhost.h"
+#include "http_log.h" /* For errors detected in basic auth common
+ * support code... */
+#include "util_date.h" /* For parseHTTPdate and BAD_DATE */
+#include "util_charset.h"
+#include "util_ebcdic.h"
+
+#if APR_HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+
+APR_HOOK_STRUCT(
+ APR_HOOK_LINK(post_read_request)
+ APR_HOOK_LINK(log_transaction)
+ APR_HOOK_LINK(http_method)
+ APR_HOOK_LINK(default_port)
+)
+
+/*
+ * Builds the content-type that should be sent to the client from the
+ * content-type specified. The following rules are followed:
+ * - if type is NULL, type is set to ap_default_type(r)
+ * - if charset adding is disabled, stop processing and return type.
+ * - then, if there are no parameters on type, add the default charset
+ * - return type
+ */
+const char *ap_make_content_type(request_rec *r, const char *type)
+{
+ static const char *needcset[] = {
+ "text/plain",
+ "text/html",
+ NULL };
+ const char **pcset;
+ core_dir_config *conf =
+ (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ if (!type) {
+ type = ap_default_type(r);
+ }
+ if (conf->add_default_charset != ADD_DEFAULT_CHARSET_ON) {
+ return type;
+ }
+
+ if (ap_strcasestr(type, "charset=") != NULL) {
+ /* already has parameter, do nothing */
+ /* XXX we don't check the validity */
+ ;
+ }
+ else {
+ /* see if it makes sense to add the charset. At present,
+ * we only add it if the Content-type is one of needcset[]
+ */
+ for (pcset = needcset; *pcset ; pcset++) {
+ if (ap_strcasestr(type, *pcset) != NULL) {
+ type = apr_pstrcat(r->pool, type, "; charset=",
+ conf->add_default_charset_name, NULL);
+ break;
+ }
+ }
+ }
+ return type;
+}
+
+static int parse_byterange(char *range, apr_off_t clength,
+ apr_off_t *start, apr_off_t *end)
+{
+ char *dash = strchr(range, '-');
+
+ if (!dash)
+ return 0;
+
+ if ((dash == range)) {
+ /* In the form "-5" */
+ *start = clength - atol(dash + 1);
+ *end = clength - 1;
+ }
+ else {
+ *dash = '\0';
+ dash++;
+ *start = atol(range);
+ if (*dash)
+ *end = atol(dash);
+ else /* "5-" */
+ *end = clength - 1;
+ }
+
+ if (*start < 0)
+ *start = 0;
+
+ if (*end >= clength)
+ *end = clength - 1;
+
+ if (*start > *end)
+ return -1;
+
+ return (*start > 0 || *end < clength);
+}
+
+static int ap_set_byterange(request_rec *r);
+
+typedef struct byterange_ctx {
+ apr_bucket_brigade *bb;
+ int num_ranges;
+ const char *orig_ct;
+} byterange_ctx;
+
+/*
+ * Here we try to be compatible with clients that want multipart/x-byteranges
+ * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
+ * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
+ * that the browser supports an older protocol. We also check User-Agent
+ * for Microsoft Internet Explorer 3, which needs this as well.
+ */
+static int use_range_x(request_rec *r)
+{
+ const char *ua;
+ return (apr_table_get(r->headers_in, "Request-Range") ||
+ ((ua = apr_table_get(r->headers_in, "User-Agent"))
+ && ap_strstr_c(ua, "MSIE 3")));
+}
+
+#define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT
+
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(
+ ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+#define MIN_LENGTH(len1, len2) ((len1 > len2) ? len2 : len1)
+ request_rec *r = f->r;
+ byterange_ctx *ctx = f->ctx;
+ apr_bucket *e;
+ apr_bucket_brigade *bsend;
+ apr_off_t range_start;
+ apr_off_t range_end;
+ char *current;
+ char *bound_head;
+ apr_ssize_t bb_length;
+ apr_off_t clength = 0;
+ apr_status_t rv;
+ int found = 0;
+
+ if (!ctx) {
+ int num_ranges = ap_set_byterange(r);
+
+ if (num_ranges == -1) {
+ ap_remove_output_filter(f);
+ bsend = apr_brigade_create(r->pool);
+ e = ap_bucket_error_create(HTTP_RANGE_NOT_SATISFIABLE, NULL, r->pool);
+ APR_BRIGADE_INSERT_TAIL(bsend, e);
+ e = apr_bucket_eos_create();
+ APR_BRIGADE_INSERT_TAIL(bsend, e);
+ return ap_pass_brigade(f->next, bsend);
+ }
+ if (num_ranges == 0) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
+ ctx->num_ranges = num_ranges;
+
+ if (num_ranges > 1) {
+ ctx->orig_ct = r->content_type;
+ r->content_type =
+ apr_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
+ "byteranges; boundary=", r->boundary, NULL);
+ }
+
+ /* create a brigade in case we never call ap_save_brigade() */
+ ctx->bb = apr_brigade_create(r->pool);
+ }
+
+ /* We can't actually deal with byte-ranges until we have the whole brigade
+ * because the byte-ranges can be in any order, and according to the RFC,
+ * we SHOULD return the data in the same order it was requested.
+ */
+ if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
+ ap_save_brigade(f, &ctx->bb, &bb);
+ return APR_SUCCESS;
+ }
+
+ /* compute this once (it is an invariant) */
+ bound_head = apr_pstrcat(r->pool,
+ CRLF "--", r->boundary,
+ CRLF "Content-type: ",
+ ap_make_content_type(r, ctx->orig_ct),
+ CRLF "Content-range: bytes ",
+ NULL);
+ ap_xlate_proto_to_ascii(bound_head, strlen(bound_head));
+
+ /* If we have a saved brigade from a previous run, concat the passed
+ * brigade with our saved brigade. Otherwise just continue.
+ */
+ if (ctx->bb) {
+ APR_BRIGADE_CONCAT(ctx->bb, bb);
+ bb = ctx->bb;
+ ctx->bb = NULL; /* ### strictly necessary? call brigade_destroy? */
+ }
+
+ /* It is possible that we won't have a content length yet, so we have to
+ * compute the length before we can actually do the byterange work.
+ */
+ (void) apr_brigade_length(bb, 1, &bb_length);
+ clength = (apr_off_t)bb_length;
+
+ /* this brigade holds what we will be sending */
+ bsend = apr_brigade_create(r->pool);
+
+ while ((current = ap_getword(r->pool, &r->range, ',')) &&
+ (rv = parse_byterange(current, clength, &range_start, &range_end))) {
+ apr_bucket *e2;
+ apr_bucket *ec;
+
+ if (rv == -1) {
+ continue;
+ }
+ else {
+ found = 1;
+ }
+
+ if (ctx->num_ranges > 1) {
+ char *ts;
+
+ e = apr_bucket_pool_create(bound_head,
+ strlen(bound_head), r->pool);
+ APR_BRIGADE_INSERT_TAIL(bsend, e);
+
+ ts = apr_psprintf(r->pool, BYTERANGE_FMT CRLF CRLF,
+ range_start, range_end, clength);
+ ap_xlate_proto_to_ascii(ts, strlen(ts));
+ e = apr_bucket_pool_create(ts, strlen(ts), r->pool);
+ APR_BRIGADE_INSERT_TAIL(bsend, e);
+ }
+
+ e = apr_brigade_partition(bb, range_start);
+ e2 = apr_brigade_partition(bb, range_end + 1);
+
+ ec = e;
+ do {
+ apr_bucket *foo;
+ const char *str;
+ apr_size_t len;
+
+ if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) {
+ apr_bucket_read(ec, &str, &len, APR_BLOCK_READ);
+ foo = apr_bucket_heap_create(str, len, 0, NULL);
+ }
+ APR_BRIGADE_INSERT_TAIL(bsend, foo);
+ ec = APR_BUCKET_NEXT(ec);
+ } while (ec != e2);
+ }
+
+ if (found == 0) {
+ ap_remove_output_filter(f);
+ r->status = HTTP_OK;
+ return HTTP_RANGE_NOT_SATISFIABLE;
+ }
+
+ if (ctx->num_ranges > 1) {
+ char *end;
+
+ /* add the final boundary */
+ end = apr_pstrcat(r->pool, CRLF "--", r->boundary, "--" CRLF, NULL);
+ ap_xlate_proto_to_ascii(end, strlen(end));
+ e = apr_bucket_pool_create(end, strlen(end), r->pool);
+ APR_BRIGADE_INSERT_TAIL(bsend, e);
+ }
+
+ e = apr_bucket_eos_create();
+ APR_BRIGADE_INSERT_TAIL(bsend, e);
+
+ /* we're done with the original content */
+ apr_brigade_destroy(bb);
+
+ /* send our multipart output */
+ return ap_pass_brigade(f->next, bsend);
+}
+
+AP_DECLARE(void) ap_set_content_length(request_rec *r, apr_off_t clength)
+{
+ r->clength = clength;
+ apr_table_setn(r->headers_out, "Content-Length",
+ apr_psprintf(r->pool, "%" APR_OFF_T_FMT, clength));
+}
+
+/*
+ * Return the latest rational time from a request/mtime (modification time)
+ * pair. We return the mtime unless it's in the future, in which case we
+ * return the current time. We use the request time as a reference in order
+ * to limit the number of calls to time(). We don't check for futurosity
+ * unless the mtime is at least as new as the reference.
+ */
+AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime)
+{
+ apr_time_t now;
+
+ /* For all static responses, it's almost certain that the file was
+ * last modified before the beginning of the request. So there's
+ * no reason to call time(NULL) again. But if the response has been
+ * created on demand, then it might be newer than the time the request
+ * started. In this event we really have to call time(NULL) again
+ * so that we can give the clients the most accurate Last-Modified. If we
+ * were given a time in the future, we return the current time - the
+ * Last-Modified can't be in the future.
+ */
+ now = (mtime < r->request_time) ? r->request_time : apr_time_now();
+ return (mtime > now) ? now : mtime;
+}
+
+/*
+ * Construct an entity tag (ETag) from resource information. If it's a real
+ * file, build in some of the file characteristics. If the modification time
+ * is newer than (request-time minus 1 second), mark the ETag as weak - it
+ * could be modified again in as short an interval. We rationalize the
+ * modification time we're given to keep it from being in the future.
+ */
+AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
+{
+ char *etag;
+ char *weak;
+
+ /*
+ * Make an ETag header out of various pieces of information. We use
+ * the last-modified date and, if we have a real file, the
+ * length and inode number - note that this doesn't have to match
+ * the content-length (i.e. includes), it just has to be unique
+ * for the file.
+ *
+ * If the request was made within a second of the last-modified date,
+ * we send a weak tag instead of a strong one, since it could
+ * be modified again later in the second, and the validation
+ * would be incorrect.
+ */
+
+ weak = ((r->request_time - r->mtime > APR_USEC_PER_SEC)
+ && !force_weak) ? "" : "W/";
+
+ if (r->finfo.filetype != 0) {
+ etag = apr_psprintf(r->pool,
+ "%s\"%lx-%lx-%lx\"", weak,
+ (unsigned long) r->finfo.inode,
+ (unsigned long) r->finfo.size,
+ (unsigned long) r->mtime);
+ }
+ else {
+ etag = apr_psprintf(r->pool, "%s\"%lx\"", weak,
+ (unsigned long) r->mtime);
+ }
+
+ return etag;
+}
+
+AP_DECLARE(void) ap_set_etag(request_rec *r)
+{
+ char *etag;
+ char *variant_etag, *vlv;
+ int vlv_weak;
+
+ if (!r->vlist_validator) {
+ etag = ap_make_etag(r, 0);
+ }
+ else {
+ /* If we have a variant list validator (vlv) due to the
+ * response being negotiated, then we create a structured
+ * entity tag which merges the variant etag with the variant
+ * list validator (vlv). This merging makes revalidation
+ * somewhat safer, ensures that caches which can deal with
+ * Vary will (eventually) be updated if the set of variants is
+ * changed, and is also a protocol requirement for transparent
+ * content negotiation.
+ */
+
+ /* if the variant list validator is weak, we make the whole
+ * structured etag weak. If we would not, then clients could
+ * have problems merging range responses if we have different
+ * variants with the same non-globally-unique strong etag.
+ */
+
+ vlv = r->vlist_validator;
+ vlv_weak = (vlv[0] == 'W');
+
+ variant_etag = ap_make_etag(r, vlv_weak);
+
+ /* merge variant_etag and vlv into a structured etag */
+
+ variant_etag[strlen(variant_etag) - 1] = '\0';
+ if (vlv_weak)
+ vlv += 3;
+ else
+ vlv++;
+ etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
+ }
+
+ apr_table_setn(r->headers_out, "ETag", etag);
+}
+
+/* Get a line of protocol input, including any continuation lines
+ * caused by MIME folding (or broken clients) if fold != 0, and place it
+ * in the buffer s, of size n bytes, without the ending newline.
+ *
+ * Returns -1 on error, or the length of s.
+ *
+ * Notes: Because the buffer uses 1 char for NUL, the most we can return is
+ * (n - 1) actual characters.
+ *
+ * If no LF is detected on the last line due to a dropped connection
+ * or a full buffer, that's considered an error.
+ */
+AP_CORE_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold)
+{
+ char *pos = s;
+ char *last_char;
+ char *beyond_buff = s + n;
+ const char *temp;
+ int retval;
+ int total = 0;
+ int looking_ahead = 0;
+ apr_size_t length;
+ conn_rec *c = r->connection;
+ core_request_config *req_cfg;
+ apr_bucket_brigade *b;
+ apr_bucket *e;
+
+ req_cfg = (core_request_config *)
+ ap_get_module_config(r->request_config, &core_module);
+ b = req_cfg->bb;
+ /* make sure it's empty unless we're folding */
+ AP_DEBUG_ASSERT(fold || APR_BRIGADE_EMPTY(b));
+
+ while (1) {
+ if (APR_BRIGADE_EMPTY(b)) {
+ if (ap_get_brigade(c->input_filters, b, AP_MODE_BLOCKING) != APR_SUCCESS ||
+ APR_BRIGADE_EMPTY(b)) {
+ return -1;
+ }
+ }
+ e = APR_BRIGADE_FIRST(b);
+ if (e->length == 0) {
+ apr_bucket_delete(e);
+ continue;
+ }
+ retval = apr_bucket_read(e, &temp, &length, APR_BLOCK_READ);
+
+ if (retval != APR_SUCCESS) {
+ total = ((length < 0) && (total == 0)) ? -1 : total;
+ break;
+ }
+
+ if ((looking_ahead) && (*temp != APR_ASCII_BLANK) && (*temp != APR_ASCII_TAB)) {
+ /* can't fold because next line isn't indented,
+ * so return what we have. lookahead brigade is
+ * stashed on req_cfg->bb
+ */
+ AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(req_cfg->bb));
+ break;
+ }
+ last_char = pos + length - 1;
+ if (last_char < beyond_buff) {
+ memcpy(pos, temp, length);
+ apr_bucket_delete(e);
+ }
+ else {
+ /* input line was larger than the caller's buffer */
+ apr_brigade_destroy(b);
+
+ /* don't need to worry about req_cfg->bb being bogus.
+ * the request is about to die, and ErrorDocument
+ * redirects get a new req_cfg->bb
+ */
+
+ return -1;
+ }
+
+ pos = last_char; /* Point at the last character */
+
+ if (*pos == APR_ASCII_LF) { /* Did we get a full line of input? */
+
+ if (pos > s && *(pos - 1) == APR_ASCII_CR) {
+ --pos; /* zap optional CR before LF */
+ }
+
+ /*
+ * Trim any extra trailing spaces or tabs except for the first
+ * space or tab at the beginning of a blank string. This makes
+ * it much easier to check field values for exact matches, and
+ * saves memory as well. Terminate string at end of line.
+ */
+ while (pos > (s + 1) &&
+ (*(pos - 1) == APR_ASCII_BLANK || *(pos - 1) == APR_ASCII_TAB)) {
+ --pos; /* trim extra trailing spaces or tabs */
+ }
+ *pos = '\0'; /* zap end of string */
+ total = pos - s; /* update total string length */
+
+ /* look ahead another line if line folding is desired
+ * and this line isn't empty
+ */
+ if (fold && total) {
+ looking_ahead = 1;
+ }
+ else {
+ AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(req_cfg->bb));
+ break;
+ }
+ }
+ else {
+ /* no LF yet...character mode client (telnet)...keep going
+ * bump past last character read,
+ * and set total in case we bail before finding a LF
+ */
+ total = ++pos - s;
+ looking_ahead = 0; /* only appropriate right after LF */
+ }
+ }
+ ap_xlate_proto_from_ascii(s, total);
+ return total;
+}
+
+/* parse_uri: break apart the uri
+ * Side Effects:
+ * - sets r->args to rest after '?' (or NULL if no '?')
+ * - sets r->uri to request uri (without r->args part)
+ * - sets r->hostname (if not set already) from request (scheme://host:port)
+ */
+AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri)
+{
+ int status = HTTP_OK;
+
+ r->unparsed_uri = apr_pstrdup(r->pool, uri);
+
+ if (r->method_number == M_CONNECT) {
+ status = ap_parse_hostinfo_components(r->pool, uri, &r->parsed_uri);
+ }
+ else {
+ /* Simple syntax Errors in URLs are trapped by parse_uri_components(). */
+ status = ap_parse_uri_components(r->pool, uri, &r->parsed_uri);
+ }
+
+ if (ap_is_HTTP_SUCCESS(status)) {
+ /* if it has a scheme we may need to do absoluteURI vhost stuff */
+ if (r->parsed_uri.scheme
+ && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))) {
+ r->hostname = r->parsed_uri.hostname;
+ }
+ else if (r->method_number == M_CONNECT) {
+ r->hostname = r->parsed_uri.hostname;
+ }
+ r->args = r->parsed_uri.query;
+ r->uri = r->parsed_uri.path ? r->parsed_uri.path
+ : apr_pstrdup(r->pool, "/");
+#if defined(OS2) || defined(WIN32)
+ /* Handle path translations for OS/2 and plug security hole.
+ * This will prevent "http://www.wherever.com/..\..\/" from
+ * returning a directory for the root drive.
+ */
+ {
+ char *x;
+
+ for (x = r->uri; (x = strchr(x, '\\')) != NULL; )
+ *x = '/';
+ }
+#endif /* OS2 || WIN32 */
+ }
+ else {
+ r->args = NULL;
+ r->hostname = NULL;
+ r->status = status; /* set error status */
+ r->uri = apr_pstrdup(r->pool, uri);
+ }
+}
+
+static int read_request_line(request_rec *r)
+{
+ char l[DEFAULT_LIMIT_REQUEST_LINE + 2]; /* getline's two extra for \n\0 */
+ const char *ll = l;
+ const char *uri;
+#if 0
+ conn_rec *conn = r->connection;
+#endif
+ int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol */
+ int len;
+
+ /* Read past empty lines until we get a real request line,
+ * a read error, the connection closes (EOF), or we timeout.
+ *
+ * We skip empty lines because browsers have to tack a CRLF on to the end
+ * of POSTs to support old CERN webservers. But note that we may not
+ * have flushed any previous response completely to the client yet.
+ * We delay the flush as long as possible so that we can improve
+ * performance for clients that are pipelining requests. If a request
+ * is pipelined then we won't block during the (implicit) read() below.
+ * If the requests aren't pipelined, then the client is still waiting
+ * for the final buffer flush from us, and we will block in the implicit
+ * read(). B_SAFEREAD ensures that the BUFF layer flushes if it will
+ * have to block during a read.
+ */
+
+ while ((len = ap_getline(l, sizeof(l), r, 0)) <= 0) {
+ if (len < 0) { /* includes EOF */
+ /* this is a hack to make sure that request time is set,
+ * it's not perfect, but it's better than nothing
+ */
+ r->request_time = apr_time_now();
+ return 0;
+ }
+ }
+ /* we've probably got something to do, ignore graceful restart requests */
+
+ /* XXX - sigwait doesn't work if the signal has been SIG_IGNed (under
+ * linux 2.0 w/ glibc 2.0, anyway), and this step isn't necessary when
+ * we're running a sigwait thread anyway. If/when unthreaded mode is
+ * put back in, we should make sure to ignore this signal iff a sigwait
+ * thread isn't used. - mvsk
+
+#ifdef SIGWINCH
+ apr_signal(SIGWINCH, SIG_IGN);
+#endif
+ */
+
+ r->request_time = apr_time_now();
+ r->the_request = apr_pstrdup(r->pool, l);
+ r->method = ap_getword_white(r->pool, &ll);
+
+#if 0
+/* XXX If we want to keep track of the Method, the protocol module should do
+ * it. That support isn't in the scoreboard yet. Hopefully next week
+ * sometime. rbb */
+ ap_update_connection_status(AP_CHILD_THREAD_FROM_ID(conn->id), "Method", r->method);
+#endif
+ uri = ap_getword_white(r->pool, &ll);
+
+ /* Provide quick information about the request method as soon as known */
+
+ r->method_number = ap_method_number_of(r->method);
+ if (r->method_number == M_GET && r->method[0] == 'H') {
+ r->header_only = 1;
+ }
+
+ ap_parse_uri(r, uri);
+
+ /* ap_getline returns (size of max buffer - 1) if it fills up the
+ * buffer before finding the end-of-line. This is only going to
+ * happen if it exceeds the configured limit for a request-line.
+ */
+ if (len > r->server->limit_req_line) {
+ r->status = HTTP_REQUEST_URI_TOO_LARGE;
+ r->proto_num = HTTP_VERSION(1,0);
+ r->protocol = apr_pstrdup(r->pool, "HTTP/1.0");
+ return 0;
+ }
+
+ r->assbackwards = (ll[0] == '\0');
+ r->protocol = apr_pstrdup(r->pool, ll[0] ? ll : "HTTP/0.9");
+/* XXX If we want to keep track of the Method, the protocol module should do
+ * it. That support isn't in the scoreboard yet. Hopefully next week
+ * sometime. rbb
+ ap_update_connection_status(conn->id, "Protocol", r->protocol);
+ */
+
+ if (2 == sscanf(r->protocol, "HTTP/%u.%u", &major, &minor)
+ && minor < HTTP_VERSION(1,0)) /* don't allow HTTP/0.1000 */
+ r->proto_num = HTTP_VERSION(major, minor);
+ else
+ r->proto_num = HTTP_VERSION(1,0);
+
+ return 1;
+}
+
+static void get_mime_headers(request_rec *r)
+{
+ char field[DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2]; /* getline's two extra */
+ char *value;
+ char *copy;
+ int len;
+ int fields_read = 0;
+ apr_table_t *tmp_headers;
+
+ /* We'll use apr_table_overlap later to merge these into r->headers_in. */
+ tmp_headers = apr_table_make(r->pool, 50);
+
+ /*
+ * Read header lines until we get the empty separator line, a read error,
+ * the connection closes (EOF), reach the server limit, or we timeout.
+ */
+ while ((len = ap_getline(field, sizeof(field), r, 1)) > 0) {
+
+ if (r->server->limit_req_fields &&
+ (++fields_read > r->server->limit_req_fields)) {
+ r->status = HTTP_BAD_REQUEST;
+ apr_table_setn(r->notes, "error-notes",
+ "The number of request header fields exceeds "
+ "this server's limit.<P>\n");
+ return;
+ }
+ /* ap_getline returns (size of max buffer - 1) if it fills up the
+ * buffer before finding the end-of-line. This is only going to
+ * happen if it exceeds the configured limit for a field size.
+ */
+ if (len > r->server->limit_req_fieldsize) {
+ r->status = HTTP_BAD_REQUEST;
+ apr_table_setn(r->notes, "error-notes",
+ apr_pstrcat(r->pool,
+ "Size of a request header field "
+ "exceeds server limit.<P>\n"
+ "<PRE>\n",
+ ap_escape_html(r->pool, field),
+ "</PRE>\n", NULL));
+ return;
+ }
+ copy = apr_palloc(r->pool, len + 1);
+ memcpy(copy, field, len + 1);
+
+ if (!(value = strchr(copy, ':'))) { /* Find the colon separator */
+ r->status = HTTP_BAD_REQUEST; /* or abort the bad request */
+ apr_table_setn(r->notes, "error-notes",
+ apr_pstrcat(r->pool,
+ "Request header field is missing "
+ "colon separator.<P>\n"
+ "<PRE>\n",
+ ap_escape_html(r->pool, copy),
+ "</PRE>\n", NULL));
+ return;
+ }
+
+ *value = '\0';
+ ++value;
+ while (*value == ' ' || *value == '\t') {
+ ++value; /* Skip to start of value */
+ }
+
+ apr_table_addn(tmp_headers, copy, value);
+ }
+
+ apr_table_overlap(r->headers_in, tmp_headers, APR_OVERLAP_TABLES_MERGE);
+}
+
+request_rec *ap_read_request(conn_rec *conn)
+{
+ request_rec *r;
+ apr_pool_t *p;
+ const char *expect;
+ int access_status;
+ core_request_config *req_cfg;
+
+ apr_pool_create(&p, conn->pool);
+ r = apr_pcalloc(p, sizeof(request_rec));
+ r->pool = p;
+ r->connection = conn;
+ r->server = conn->base_server;
+
+ conn->keptalive = conn->keepalive == 1;
+ conn->keepalive = 0;
+
+ r->user = NULL;
+ r->ap_auth_type = NULL;
+
+ r->allowed_methods = ap_make_method_list(p, 2);
+
+ r->headers_in = apr_table_make(r->pool, 50);
+ r->subprocess_env = apr_table_make(r->pool, 50);
+ r->headers_out = apr_table_make(r->pool, 12);
+ r->err_headers_out = apr_table_make(r->pool, 5);
+ r->notes = apr_table_make(r->pool, 5);
+
+ r->request_config = ap_create_request_config(r->pool);
+ req_cfg = apr_pcalloc(r->pool, sizeof(core_request_config));
+ req_cfg->bb = apr_brigade_create(r->pool);
+ ap_set_module_config(r->request_config, &core_module, req_cfg);
+
+ r->per_dir_config = r->server->lookup_defaults;
+
+ r->sent_bodyct = 0; /* bytect isn't for body */
+
+ r->read_length = 0;
+ r->read_body = REQUEST_NO_BODY;
+
+ r->status = HTTP_REQUEST_TIME_OUT; /* Until we get a request */
+ r->the_request = NULL;
+ r->output_filters = conn->output_filters;
+ r->input_filters = conn->input_filters;
+
+ apr_setsocketopt(conn->client_socket, APR_SO_TIMEOUT,
+ (int)(conn->keptalive
+ ? r->server->keep_alive_timeout * APR_USEC_PER_SEC
+ : r->server->timeout * APR_USEC_PER_SEC));
+
+ ap_add_output_filter("BYTERANGE", NULL, r, r->connection);
+ ap_add_output_filter("CONTENT_LENGTH", NULL, r, r->connection);
+ ap_add_output_filter("HTTP_HEADER", NULL, r, r->connection);
+
+ /* Get the request... */
+ if (!read_request_line(r)) {
+ if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+ "request failed: URI too long");
+ ap_send_error_response(r, 0);
+ ap_run_log_transaction(r);
+ return r;
+ }
+ return NULL;
+ }
+ if (r->connection->keptalive) {
+ apr_setsocketopt(r->connection->client_socket, APR_SO_TIMEOUT,
+ (int)(r->server->timeout * APR_USEC_PER_SEC));
+ }
+ if (!r->assbackwards) {
+ get_mime_headers(r);
+ if (r->status != HTTP_REQUEST_TIME_OUT) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+ "request failed: error reading the headers");
+ ap_send_error_response(r, 0);
+ ap_run_log_transaction(r);
+ return r;
+ }
+ }
+ else {
+ if (r->header_only) {
+ /*
+ * Client asked for headers only with HTTP/0.9, which doesn't send
+ * headers! Have to dink things just to make sure the error message
+ * comes through...
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+ "client sent invalid HTTP/0.9 request: HEAD %s",
+ r->uri);
+ r->header_only = 0;
+ r->status = HTTP_BAD_REQUEST;
+ ap_send_error_response(r, 0);
+ ap_run_log_transaction(r);
+ return r;
+ }
+ }
+
+ r->status = HTTP_OK; /* Until further notice. */
+
+ /* update what we think the virtual host is based on the headers we've
+ * now read. may update status.
+ */
+ ap_update_vhost_from_headers(r);
+
+ /* we may have switched to another server */
+ r->per_dir_config = r->server->lookup_defaults;
+
+ conn->keptalive = 0; /* We now have a request to play with */
+
+ if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1,1))) ||
+ ((r->proto_num == HTTP_VERSION(1,1)) &&
+ !apr_table_get(r->headers_in, "Host"))) {
+ /*
+ * Client sent us an HTTP/1.1 or later request without telling us the
+ * hostname, either with a full URL or a Host: header. We therefore
+ * need to (as per the 1.1 spec) send an error. As a special case,
+ * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain
+ * a Host: header, and the server MUST respond with 400 if it doesn't.
+ */
+ r->status = HTTP_BAD_REQUEST;
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+ "client sent HTTP/1.1 request without hostname "
+ "(see RFC2616 section 14.23): %s", r->uri);
+ }
+ if (r->status != HTTP_OK) {
+ ap_send_error_response(r, 0);
+ ap_run_log_transaction(r);
+ return r;
+ }
+ if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL) &&
+ (expect[0] != '\0')) {
+ /*
+ * The Expect header field was added to HTTP/1.1 after RFC 2068
+ * as a means to signal when a 100 response is desired and,
+ * unfortunately, to signal a poor man's mandatory extension that
+ * the server must understand or return 417 Expectation Failed.
+ */
+ if (strcasecmp(expect, "100-continue") == 0) {
+ r->expecting_100 = 1;
+ }
+ else {
+ r->status = HTTP_EXPECTATION_FAILED;
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r,
+ "client sent an unrecognized expectation value of "
+ "Expect: %s", expect);
+ ap_send_error_response(r, 0);
+ (void) ap_discard_request_body(r);
+ ap_run_log_transaction(r);
+ return r;
+ }
+ }
+
+ if ((access_status = ap_run_post_read_request(r))) {
+ ap_die(access_status, r);
+ ap_run_log_transaction(r);
+ return NULL;
+ }
+
+ return r;
+}
+
+/*
+ * A couple of other functions which initialize some of the fields of
+ * a request structure, as appropriate for adjuncts of one kind or another
+ * to a request in progress. Best here, rather than elsewhere, since
+ * *someone* has to set the protocol-specific fields...
+ */
+
+void ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r)
+{
+ rnew->the_request = r->the_request; /* Keep original request-line */
+
+ rnew->assbackwards = 1; /* Don't send headers from this. */
+ rnew->no_local_copy = 1; /* Don't try to send HTTP_NOT_MODIFIED for a
+ * fragment. */
+ rnew->method = "GET";
+ rnew->method_number = M_GET;
+ rnew->protocol = "INCLUDED";
+
+ rnew->status = HTTP_OK;
+
+ rnew->headers_in = r->headers_in;
+ rnew->subprocess_env = apr_table_copy(rnew->pool, r->subprocess_env);
+ rnew->headers_out = apr_table_make(rnew->pool, 5);
+ rnew->err_headers_out = apr_table_make(rnew->pool, 5);
+ rnew->notes = apr_table_make(rnew->pool, 5);
+
+ rnew->expecting_100 = r->expecting_100;
+ rnew->read_length = r->read_length;
+ rnew->read_body = REQUEST_NO_BODY;
+
+ rnew->main = (request_rec *) r;
+}
+
+static void end_output_stream(request_rec *r)
+{
+ apr_bucket_brigade *bb;
+ apr_bucket *b;
+
+ bb = apr_brigade_create(r->pool);
+ b = apr_bucket_eos_create();
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ ap_pass_brigade(r->output_filters, bb);
+}
+
+void ap_finalize_sub_req_protocol(request_rec *sub)
+{
+ end_output_stream(sub);
+}
+
+/* finalize_request_protocol is called at completion of sending the
+ * response. Its sole purpose is to send the terminating protocol
+ * information for any wrappers around the response message body
+ * (i.e., transfer encodings). It should have been named finalize_response.
+ */
+AP_DECLARE(void) ap_finalize_request_protocol(request_rec *r)
+{
+ while (r->next) {
+ r = r->next;
+ }
+ /* tell the filter chain there is no more content coming */
+ if (!r->eos_sent) {
+ end_output_stream(r);
+ }
+}
+
+/*
+ * Support for the Basic authentication protocol, and a bit for Digest.
+ */
+
+AP_DECLARE(void) ap_note_auth_failure(request_rec *r)
+{
+ if (!strcasecmp(ap_auth_type(r), "Basic"))
+ ap_note_basic_auth_failure(r);
+ else if (!strcasecmp(ap_auth_type(r), "Digest"))
+ ap_note_digest_auth_failure(r);
+}
+
+AP_DECLARE(void) ap_note_basic_auth_failure(request_rec *r)
+{
+ if (strcasecmp(ap_auth_type(r), "Basic"))
+ ap_note_auth_failure(r);
+ else
+ apr_table_setn(r->err_headers_out,
+ r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+ apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), "\"",
+ NULL));
+}
+
+AP_DECLARE(void) ap_note_digest_auth_failure(request_rec *r)
+{
+ apr_table_setn(r->err_headers_out,
+ r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+ apr_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%llx\"",
+ ap_auth_name(r), r->request_time));
+}
+
+AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
+{
+ const char *auth_line = apr_table_get(r->headers_in,
+ r->proxyreq ? "Proxy-Authorization"
+ : "Authorization");
+ const char *t;
+
+ if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic"))
+ return DECLINED;
+
+ if (!ap_auth_name(r)) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
+ 0, r, "need AuthName: %s", r->uri);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (!auth_line) {
+ ap_note_basic_auth_failure(r);
+ return HTTP_UNAUTHORIZED;
+ }
+
+ if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
+ /* Client tried to authenticate using wrong auth scheme */
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+ "client used wrong authentication scheme: %s", r->uri);
+ ap_note_basic_auth_failure(r);
+ return HTTP_UNAUTHORIZED;
+ }
+
+ while (*auth_line== ' ' || *auth_line== '\t') {
+ auth_line++;
+ }
+
+ t = ap_pbase64decode(r->pool, auth_line);
+ /* Note that this allocation has to be made from r->connection->pool
+ * because it has the lifetime of the connection. The other allocations
+ * are temporary and can be tossed away any time.
+ */
+ r->user = ap_getword_nulls (r->pool, &t, ':');
+ r->ap_auth_type = "Basic";
+
+ *pw = t;
+
+ return OK;
+}
+
+struct content_length_ctx {
+ apr_bucket_brigade *saved;
+ int compute_len;
+ apr_size_t curr_len;
+};
+
+/* This filter computes the content length, but it also computes the number
+ * of bytes sent to the client. This means that this filter will always run
+ * through all of the buckets in all brigades
+ */
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *f,
+ apr_bucket_brigade *b)
+{
+ request_rec *r = f->r;
+ struct content_length_ctx *ctx;
+ apr_status_t rv;
+ apr_bucket *e;
+ int send_it = 0;
+
+ ctx = f->ctx;
+ if (!ctx) { /* first time through */
+ f->ctx = ctx = apr_pcalloc(r->pool, sizeof(struct content_length_ctx));
+ }
+
+ APR_BRIGADE_FOREACH(e, b) {
+ const char *ignored;
+ apr_size_t length;
+
+ if (APR_BUCKET_IS_EOS(e) || APR_BUCKET_IS_FLUSH(e)) {
+ send_it = 1;
+ }
+ if (e->length == -1) { /* if length unknown */
+ rv = apr_bucket_read(e, &ignored, &length, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ else {
+ length = e->length;
+ }
+ ctx->curr_len += length;
+ r->bytes_sent += length;
+ }
+
+ if ((ctx->curr_len < AP_MIN_BYTES_TO_WRITE) && !send_it) {
+ return ap_save_brigade(f, &ctx->saved, &b);
+ }
+
+ /* We will compute a content length if:
+ * We already have all the data
+ * This is a bit confusing, because we will always buffer up
+ * to AP_MIN_BYTES_TO_WRITE, so if we get all the data while
+ * we are buffering that much data, we set the c-l.
+ * or We are in a 1.1 request and we can't chunk
+ * or This is a keepalive connection
+ * We may want to change this later to just close the connection
+ */
+ if ((r->proto_num == HTTP_VERSION(1,1)
+ && !ap_find_last_token(f->r->pool,
+ apr_table_get(r->headers_out,
+ "Transfer-Encoding"),
+ "chunked"))
+ || (f->r->connection->keepalive)
+ || (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(b)))) {
+ ctx->compute_len = 1;
+ }
+ else {
+ ctx->compute_len = 0;
+ }
+
+ if (ctx->compute_len) {
+ /* save the brigade; we can't pass any data to the next
+ * filter until we have the entire content length
+ */
+ if (!send_it) {
+ ap_save_brigade(f, &ctx->saved, &b);
+ return APR_SUCCESS;
+ }
+ ap_set_content_length(r, r->bytes_sent);
+ }
+ if (ctx->saved) {
+ APR_BRIGADE_CONCAT(ctx->saved, b);
+ apr_brigade_destroy(b);
+ b = ctx->saved;
+ ctx->saved = NULL;
+ }
+
+ ctx->curr_len = 0;
+ return ap_pass_brigade(f->next, b);
+}
+
+static int ap_set_byterange(request_rec *r)
+{
+ const char *range;
+ const char *if_range;
+ const char *match;
+ const char *ct;
+ apr_off_t range_start;
+ apr_off_t range_end;
+ int num_ranges;
+
+ if (r->assbackwards)
+ return 0;
+
+ /* Check for Range request-header (HTTP/1.1) or Request-Range for
+ * backwards-compatibility with second-draft Luotonen/Franks
+ * byte-ranges (e.g. Netscape Navigator 2-3).
+ *
+ * We support this form, with Request-Range, and (farther down) we
+ * send multipart/x-byteranges instead of multipart/byteranges for
+ * Request-Range based requests to work around a bug in Netscape
+ * Navigator 2-3 and MSIE 3.
+ */
+
+ if (!(range = apr_table_get(r->headers_in, "Range")))
+ range = apr_table_get(r->headers_in, "Request-Range");
+
+ if (!range || strncasecmp(range, "bytes=", 6)) {
+ return 0;
+ }
+
+ /* Check the If-Range header for Etag or Date.
+ * Note that this check will return false (as required) if either
+ * of the two etags are weak.
+ */
+ if ((if_range = apr_table_get(r->headers_in, "If-Range"))) {
+ if (if_range[0] == '"') {
+ if (!(match = apr_table_get(r->headers_out, "Etag")) ||
+ (strcmp(if_range, match) != 0))
+ return 0;
+ }
+ else if (!(match = apr_table_get(r->headers_out, "Last-Modified")) ||
+ (strcmp(if_range, match) != 0))
+ return 0;
+ }
+
+ /* would be nice to pick this up from f->ctx */
+ ct = ap_make_content_type(r, r->content_type);
+
+ if (!ap_strchr_c(range, ',')) {
+ int rv;
+ /* A single range */
+
+ /* rvarse_byterange() modifies the contents, so make a copy */
+ if ((rv = parse_byterange(apr_pstrdup(r->pool, range + 6), r->clength,
+ &range_start, &range_end)) <= 0) {
+ return rv;
+ }
+ apr_table_setn(r->headers_out, "Content-Range",
+ apr_psprintf(r->pool, "bytes " BYTERANGE_FMT,
+ range_start, range_end, r->clength));
+ apr_table_setn(r->headers_out, "Content-Type", ct);
+
+ num_ranges = 1;
+ }
+ else {
+ /* a multiple range */
+
+ num_ranges = 2;
+
+ /* ### it would be nice if r->boundary was in f->ctx */
+ r->boundary = apr_psprintf(r->pool, "%qx%lx",
+ r->request_time, (long) getpid());
+
+ apr_table_setn(r->headers_out, "Content-Type",
+ apr_pstrcat(r->pool,
+ "multipart", use_range_x(r) ? "/x-" : "/",
+ "byteranges; boundary=", r->boundary,
+ NULL));
+ }
+
+ r->status = HTTP_PARTIAL_CONTENT;
+ r->range = range + 6;
+
+ return num_ranges;
+}
+
+/*
+ * Send the body of a response to the client.
+ */
+AP_DECLARE(apr_status_t) ap_send_fd(apr_file_t *fd, request_rec *r, apr_off_t offset,
+ apr_size_t len, apr_size_t *nbytes)
+{
+ apr_bucket_brigade *bb = NULL;
+ apr_bucket *b;
+ apr_status_t rv;
+
+ bb = apr_brigade_create(r->pool);
+ b = apr_bucket_file_create(fd, offset, len);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+
+ rv = ap_pass_brigade(r->output_filters, bb);
+ if (rv != APR_SUCCESS) {
+ *nbytes = 0; /* no way to tell how many were actually sent */
+ }
+ else {
+ *nbytes = len;
+ }
+
+ return rv;
+}
+
+#if APR_HAS_MMAP
+/* send data from an in-memory buffer */
+AP_DECLARE(size_t) ap_send_mmap(apr_mmap_t *mm, request_rec *r, size_t offset,
+ size_t length)
+{
+ apr_bucket_brigade *bb = NULL;
+ apr_bucket *b;
+
+ bb = apr_brigade_create(r->pool);
+ b = apr_bucket_mmap_create(mm, offset, length);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ ap_pass_brigade(r->output_filters, bb);
+
+ return mm->size; /* XXX - change API to report apr_status_t? */
+}
+#endif /* APR_HAS_MMAP */
+
+typedef struct {
+ char *buf;
+ char *cur;
+ apr_size_t avail;
+} old_write_filter_ctx;
+
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_old_write_filter(
+ ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ old_write_filter_ctx *ctx = f->ctx;
+
+ AP_DEBUG_ASSERT(ctx);
+
+ if (ctx->buf != NULL) {
+ apr_size_t nbyte = ctx->cur - ctx->buf;
+
+ if (nbyte != 0) {
+ /* whatever is coming down the pipe (we don't care), we
+ can simply insert our buffered data at the front and
+ pass the whole bundle down the chain. */
+ apr_bucket *b = apr_bucket_heap_create(ctx->buf, nbyte, 0, NULL);
+ APR_BRIGADE_INSERT_HEAD(bb, b);
+ ctx->buf = NULL;
+ }
+ }
+
+ return ap_pass_brigade(f->next, bb);
+}
+
+static apr_status_t flush_buffer(request_rec *r, old_write_filter_ctx *ctx,
+ const char *extra, apr_size_t extra_len)
+{
+ apr_bucket_brigade *bb = apr_brigade_create(r->pool);
+ apr_size_t nbyte = ctx->cur - ctx->buf;
+ apr_bucket *b = apr_bucket_heap_create(ctx->buf, nbyte, 0, NULL);
+
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ ctx->buf = NULL;
+
+ /* if there is extra data, then send that, too */
+ if (extra != NULL) {
+ b = apr_bucket_transient_create(extra, extra_len);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ }
+
+ return ap_pass_brigade(r->output_filters, bb);
+}
+
+static apr_status_t buffer_output(request_rec *r,
+ const char *str, apr_size_t len)
+{
+ ap_filter_t *f;
+ old_write_filter_ctx *ctx;
+ apr_size_t amt;
+ apr_status_t status;
+
+ if (len == 0)
+ return APR_SUCCESS;
+
+ /* ### future optimization: record some flags in the request_rec to
+ ### say whether we've added our filter, and whether it is first. */
+
+ /* this will typically exit on the first test */
+ for (f = r->output_filters; f != NULL; f = f->next)
+ if (strcmp("OLD_WRITE", f->frec->name) == 0)
+ break;
+ if (f == NULL) {
+ /* our filter hasn't been added yet */
+ ctx = apr_pcalloc(r->pool, sizeof(*ctx));
+ ap_add_output_filter("OLD_WRITE", ctx, r, r->connection);
+ }
+
+ /* if the first filter is not our buffering filter, then we have to
+ deliver the content through the normal filter chain */
+ if (strcmp("OLD_WRITE", r->output_filters->frec->name) != 0) {
+ apr_bucket_brigade *bb = apr_brigade_create(r->pool);
+ apr_bucket *b = apr_bucket_transient_create(str, len);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+
+ return ap_pass_brigade(r->output_filters, bb);
+ }
+
+ /* grab the context from our filter */
+ ctx = r->output_filters->ctx;
+
+ /* if there isn't a buffer in the context yet, put one there. */
+ if (ctx->buf == NULL) {
+ /* use the heap so it will get free'd after being flushed */
+ ctx->avail = AP_MIN_BYTES_TO_WRITE;
+ ctx->buf = ctx->cur = malloc(ctx->avail);
+ }
+
+ /* squeeze the data into the existing buffer */
+ if (len <= ctx->avail) {
+ memcpy(ctx->cur, str, len);
+ ctx->cur += len;
+ if ((ctx->avail -= len) == 0)
+ return flush_buffer(r, ctx, NULL, 0);
+ return APR_SUCCESS;
+ }
+
+ /* the new content can't fit in the existing buffer */
+
+ if (len >= AP_MIN_BYTES_TO_WRITE) {
+ /* it is really big. send what we have, and the new stuff. */
+ return flush_buffer(r, ctx, str, len);
+ }
+
+ /* the new data is small. put some into the current buffer, flush it,
+ and then drop the remaining into a new buffer. */
+ amt = ctx->avail;
+ memcpy(ctx->cur, str, amt);
+ ctx->cur += amt;
+ ctx->avail = 0;
+ if ((status = flush_buffer(r, ctx, NULL, 0)) != APR_SUCCESS)
+ return status;
+
+ ctx->buf = malloc(AP_MIN_BYTES_TO_WRITE);
+ memcpy(ctx->buf, str + amt, len - amt);
+ ctx->cur = ctx->buf + (len - amt);
+ ctx->avail = AP_MIN_BYTES_TO_WRITE - (len - amt);
+
+ return APR_SUCCESS;
+}
+
+AP_DECLARE(int) ap_rputc(int c, request_rec *r)
+{
+ char c2 = (char)c;
+
+ if (r->connection->aborted) {
+ return -1;
+ }
+
+ if (buffer_output(r, &c2, 1) != APR_SUCCESS)
+ return -1;
+
+ return c;
+}
+
+AP_DECLARE(int) ap_rputs(const char *str, request_rec *r)
+{
+ apr_size_t len;
+
+ if (r->connection->aborted)
+ return -1;
+
+ if (buffer_output(r, str, len = strlen(str)) != APR_SUCCESS)
+ return -1;
+
+ return len;
+}
+
+AP_DECLARE(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
+{
+ if (r->connection->aborted)
+ return -1;
+
+ if (buffer_output(r, buf, nbyte) != APR_SUCCESS)
+ return -1;
+
+ return nbyte;
+}
+
+AP_DECLARE(int) ap_vrprintf(request_rec *r, const char *fmt, va_list va)
+{
+ char buf[4096];
+ apr_size_t written;
+
+ if (r->connection->aborted)
+ return -1;
+
+ /* ### fix this mechanism to allow more than 4K of output */
+ written = apr_vsnprintf(buf, sizeof(buf), fmt, va);
+ if (buffer_output(r, buf, written) != APR_SUCCESS)
+ return -1;
+
+ return written;
+}
+
+AP_DECLARE_NONSTD(int) ap_rprintf(request_rec *r, const char *fmt, ...)
+{
+ va_list va;
+ int n;
+
+ if (r->connection->aborted)
+ return -1;
+
+ va_start(va, fmt);
+ n = ap_vrprintf(r, fmt, va);
+ va_end(va);
+
+ return n;
+}
+
+AP_DECLARE_NONSTD(int) ap_rvputs(request_rec *r, ...)
+{
+ va_list va;
+ const char *s;
+ apr_size_t len;
+ apr_size_t written = 0;
+
+ if (r->connection->aborted)
+ return -1;
+
+ /* ### TODO: if the total output is large, put all the strings
+ ### into a single brigade, rather than flushing each time we
+ ### fill the buffer */
+ va_start(va, r);
+ while (1) {
+ s = va_arg(va, const char *);
+ if (s == NULL)
+ break;
+
+ len = strlen(s);
+ if (buffer_output(r, s, len) != APR_SUCCESS) {
+ return -1;
+ }
+
+ written += len;
+ }
+ va_end(va);
+
+ return written;
+}
+
+AP_DECLARE(int) ap_rflush(request_rec *r)
+{
+ apr_bucket_brigade *bb;
+ apr_bucket *b;
+
+ bb = apr_brigade_create(r->pool);
+ b = apr_bucket_flush_create();
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS)
+ return -1;
+ return 0;
+}
+
+AP_IMPLEMENT_HOOK_RUN_ALL(int,post_read_request,
+ (request_rec *r),(r),OK,DECLINED)
+AP_IMPLEMENT_HOOK_RUN_ALL(int,log_transaction,
+ (request_rec *r),(r),OK,DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(const char *,http_method,
+ (const request_rec *r),(r),NULL)
+AP_IMPLEMENT_HOOK_RUN_FIRST(unsigned short,default_port,
+ (const request_rec *r),(r),0)
diff --git a/server/request.c b/server/request.c
new file mode 100644
index 0000000000..bacf8fc608
--- /dev/null
+++ b/server/request.c
@@ -0,0 +1,1124 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/*
+ * http_request.c: functions to get and process requests
+ *
+ * Rob McCool 3/21/93
+ *
+ * Thoroughly revamped by rst for Apache. NB this file reads
+ * best from the bottom up.
+ *
+ */
+
+#include "apr_strings.h"
+#include "apr_file_io.h"
+#include "apr_fnmatch.h"
+
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#define CORE_PRIVATE
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_filter.h"
+#include "util_charset.h"
+
+#include "mod_core.h"
+
+#if APR_HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+APR_HOOK_STRUCT(
+ APR_HOOK_LINK(translate_name)
+ APR_HOOK_LINK(check_user_id)
+ APR_HOOK_LINK(fixups)
+ APR_HOOK_LINK(type_checker)
+ APR_HOOK_LINK(access_checker)
+ APR_HOOK_LINK(auth_checker)
+ APR_HOOK_LINK(insert_filter)
+)
+
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,translate_name,
+ (request_rec *r),(r),DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,check_user_id,
+ (request_rec *r),(r),DECLINED)
+AP_IMPLEMENT_HOOK_RUN_ALL(int,fixups,
+ (request_rec *r),(r),OK,DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,type_checker,
+ (request_rec *r),(r),DECLINED)
+AP_IMPLEMENT_HOOK_RUN_ALL(int,access_checker,
+ (request_rec *r),(r),OK,DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,auth_checker,
+ (request_rec *r),(r),DECLINED)
+AP_IMPLEMENT_HOOK_VOID(insert_filter, (request_rec *r), (r))
+
+/*****************************************************************
+ *
+ * Getting and checking directory configuration. Also checks the
+ * FollowSymlinks and FollowSymOwner stuff, since this is really the
+ * only place that can happen (barring a new mid_dir_walk callout).
+ *
+ * We can't do it as an access_checker module function which gets
+ * called with the final per_dir_config, since we could have a directory
+ * with FollowSymLinks disabled, which contains a symlink to another
+ * with a .htaccess file which turns FollowSymLinks back on --- and
+ * access in such a case must be denied. So, whatever it is that
+ * checks FollowSymLinks needs to know the state of the options as
+ * they change, all the way down.
+ */
+
+/*
+ * We don't want people able to serve up pipes, or unix sockets, or other
+ * scary things. Note that symlink tests are performed later.
+ */
+static int check_safe_file(request_rec *r)
+{
+
+ if (r->finfo.filetype == 0 /* doesn't exist */
+ || r->finfo.filetype == APR_DIR
+ || r->finfo.filetype == APR_REG
+ || r->finfo.filetype == APR_LNK) {
+ return OK;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+ "object is not a file, directory or symlink: %s",
+ r->filename);
+ return HTTP_FORBIDDEN;
+}
+
+
+static int check_symlinks(char *d, int opts, apr_pool_t *p)
+{
+#if defined(OS2)
+ /* OS/2 doesn't have symlinks */
+ return OK;
+#else
+ apr_finfo_t lfi, fi;
+ char *lastp;
+ int res;
+
+ if (opts & OPT_SYM_LINKS)
+ return OK;
+
+ /*
+ * Strip trailing '/', if any, off what we're checking; trailing slashes
+ * make some systems follow symlinks to directories even in lstat().
+ * After we've done the lstat, put it back. Also, don't bother checking
+ * '/' at all...
+ *
+ * Note that we don't have to worry about multiple slashes here because of
+ * no2slash() below...
+ */
+
+ lastp = d + strlen(d) - 1;
+ if (lastp == d)
+ return OK; /* Root directory, '/' */
+
+ if (*lastp == '/')
+ *lastp = '\0';
+ else
+ lastp = NULL;
+
+ res = apr_lstat(&lfi, d, APR_FINFO_TYPE | APR_FINFO_OWNER, p);
+
+ if (lastp)
+ *lastp = '/';
+
+ /*
+ * Note that we don't reject accesses to nonexistent files (multiviews or
+ * the like may cons up a way to run the transaction anyway)...
+ */
+
+ if ((res != APR_SUCCESS && res != APR_INCOMPLETE)
+ || (lfi.filetype != APR_LNK))
+ return OK;
+
+ /* OK, it's a symlink. May still be OK with OPT_SYM_OWNER */
+
+ if (!(opts & OPT_SYM_OWNER))
+ return HTTP_FORBIDDEN;
+
+ /* OPT_SYM_OWNER only works if we can get the owner from the file */
+
+ if (res != APR_SUCCESS)
+ return HTTP_FORBIDDEN;
+
+ if (apr_stat(&fi, d, APR_FINFO_OWNER, p) != APR_SUCCESS)
+ return HTTP_FORBIDDEN;
+
+ /* TODO: replace with an apr_compare_users() fn */
+ return (fi.user == lfi.user) ? OK : HTTP_FORBIDDEN;
+
+#endif
+}
+
+/* Dealing with the file system to get PATH_INFO
+ */
+static int get_path_info(request_rec *r)
+{
+ char *cp;
+ char *path = r->filename;
+ char *end = &path[strlen(path)];
+ char *last_cp = NULL;
+ int rv;
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ char bStripSlash=1;
+#endif
+
+ if (r->finfo.filetype) {
+ /* assume path_info already set */
+ return OK;
+ }
+
+#ifdef HAVE_DRIVE_LETTERS
+ /* If the directory is x:\, then we don't want to strip
+ * the trailing slash since x: is not a valid directory.
+ */
+ if (strlen(path) == 3 && path[1] == ':' && path[2] == '/')
+ bStripSlash = 0;
+#endif
+
+#ifdef HAVE_UNC_PATHS
+ /* If UNC name == //machine/share/, do not
+ * advance over the trailing slash. Any other
+ * UNC name is OK to strip the slash.
+ */
+ cp = end;
+ if (path[0] == '/' && path[1] == '/' &&
+ path[2] != '/' && cp[-1] == '/') {
+ char *p;
+ int iCount=0;
+ p = path;
+ while ((p = strchr(p,'/')) != NULL) {
+ p++;
+ iCount++;
+ }
+
+ if (iCount == 4)
+ bStripSlash = 0;
+ }
+#endif
+
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ if (bStripSlash)
+#endif
+ /* Advance over trailing slashes ... NOT part of filename
+ * if file is not a UNC name (Win32 only).
+ */
+ for (cp = end; cp > path && cp[-1] == '/'; --cp)
+ continue;
+
+ while (cp > path) {
+
+ /* See if the pathname ending here exists... */
+ *cp = '\0';
+
+ /* ### We no longer need the test ap_os_is_filename_valid() here
+ * since apr_stat isn't a posix thing - it's apr_stat's responsibility
+ * to handle whatever path string arrives at it's door - by platform
+ * and volume restrictions as applicable...
+ * TODO: This code becomes even simpler if apr_stat grows
+ * an APR_PATHINCOMPLETE result to indicate that we are staring at
+ * an partial virtual root. Only OS2/Win32/Netware need apply it :-)
+ */
+ rv = apr_stat(&r->finfo, path, APR_FINFO_MIN, r->pool);
+
+ if (cp != end)
+ *cp = '/';
+
+ if (rv == APR_SUCCESS || rv == APR_INCOMPLETE) {
+ /*
+ * Aha! Found something. If it was a directory, we will search
+ * contents of that directory for a multi_match, so the PATH_INFO
+ * argument starts with the component after that.
+ */
+ if (r->finfo.filetype == APR_DIR && last_cp) {
+ r->finfo.filetype = APR_NOFILE; /* No such file... */
+ cp = last_cp;
+ }
+
+ r->path_info = apr_pstrdup(r->pool, cp);
+ *cp = '\0';
+ return OK;
+ }
+
+ if (APR_STATUS_IS_ENOENT(rv) || APR_STATUS_IS_ENOTDIR(rv)) {
+ last_cp = cp;
+
+ while (--cp > path && *cp != '/')
+ continue;
+
+ while (cp > path && cp[-1] == '/')
+ --cp;
+ }
+ else {
+ if (APR_STATUS_IS_EACCES(rv))
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ "access to %s denied", r->uri);
+ else
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ "access to %s failed", r->uri);
+ return HTTP_FORBIDDEN;
+ }
+ }
+ return OK;
+}
+
+AP_DECLARE(int) directory_walk(request_rec *r)
+{
+ core_server_config *sconf = ap_get_module_config(r->server->module_config,
+ &core_module);
+ ap_conf_vector_t *per_dir_defaults = r->server->lookup_defaults;
+ ap_conf_vector_t **sec = (ap_conf_vector_t **) sconf->sec->elts;
+ int num_sec = sconf->sec->nelts;
+ char *test_filename;
+ char *test_dirname;
+ int res;
+ unsigned i, num_dirs;
+ int j, test_filename_len;
+#if defined(HAVE_UNC_PATHS) || defined(NETWARE)
+ unsigned iStart = 1;
+#endif
+ ap_conf_vector_t *entry_config;
+ ap_conf_vector_t *this_conf;
+ core_dir_config *entry_core;
+
+ /*
+ * Are we dealing with a file? If not, we can (hopefuly) safely assume we
+ * have a handler that doesn't require one, but for safety's sake, and so
+ * we have something find_types() can get something out of, fake one. But
+ * don't run through the directory entries.
+ */
+
+ if (r->filename == NULL) {
+ r->filename = apr_pstrdup(r->pool, r->uri);
+ r->finfo.filetype = APR_NOFILE;
+ r->per_dir_config = per_dir_defaults;
+
+ return OK;
+ }
+
+ /*
+ * Go down the directory hierarchy. Where we have to check for symlinks,
+ * do so. Where a .htaccess file has permission to override anything,
+ * try to find one. If either of these things fails, we could poke
+ * around, see why, and adjust the lookup_rec accordingly --- this might
+ * save us a call to get_path_info (with the attendant stat()s); however,
+ * for the moment, that's not worth the trouble.
+ *
+ * Fake filenames (i.e. proxy:) only match Directory sections.
+ */
+
+ if (!ap_os_is_path_absolute(r->filename))
+ {
+ const char *entry_dir;
+
+ for (j = 0; j < num_sec; ++j) {
+
+ entry_config = sec[j];
+ entry_core = ap_get_module_config(entry_config, &core_module);
+ entry_dir = entry_core->d;
+
+ this_conf = NULL;
+ if (entry_core->r) {
+ if (!ap_regexec(entry_core->r, r->filename, 0, NULL, 0))
+ this_conf = entry_config;
+ }
+ else if (entry_core->d_is_fnmatch) {
+ if (!apr_fnmatch(entry_dir, r->filename, 0))
+ this_conf = entry_config;
+ }
+ else if (!strncmp(r->filename, entry_dir, strlen(entry_dir)))
+ this_conf = entry_config;
+
+ if (this_conf)
+ per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+ per_dir_defaults,
+ this_conf);
+ }
+
+ r->per_dir_config = per_dir_defaults;
+
+ return OK;
+ }
+
+ /* XXX This needs to be rolled into APR, the APR function will not
+ * be allowed to fold the case of any non-existant segment of the path:
+ */
+ r->filename = ap_os_case_canonical_filename(r->pool, r->filename);
+
+ /* TODO This is rather silly right here, we should simply be setting
+ * filename and path_info at the end of our directory_walk
+ */
+ res = get_path_info(r);
+ if (res != OK) {
+ return res;
+ }
+
+ /* XXX This becomes moot, and will already happen above for elements
+ * that actually exist:
+ */
+ r->filename = ap_os_canonical_filename(r->pool, r->filename);
+
+ test_filename = apr_pstrdup(r->pool, r->filename);
+
+ /* XXX This becomes mute, since the APR canonical parsing will handle
+ * 2slash and dot directory issues:
+ */
+ ap_no2slash(test_filename);
+ num_dirs = ap_count_dirs(test_filename);
+
+ /* XXX This needs to be rolled into APR: */
+ if ((res = check_safe_file(r))) {
+ return res;
+ }
+
+ test_filename_len = strlen(test_filename);
+ if (test_filename[test_filename_len - 1] == '/')
+ --num_dirs;
+
+ if (r->finfo.filetype == APR_DIR)
+ ++num_dirs;
+
+ /*
+ * We will use test_dirname as scratch space while we build directory
+ * names during the walk. Profiling shows directory_walk to be a busy
+ * function so we try to avoid allocating lots of extra memory here.
+ * We need 2 extra bytes, one for trailing \0 and one because
+ * make_dirstr_prefix will add potentially one extra /.
+ */
+ test_dirname = apr_palloc(r->pool, test_filename_len + 2);
+
+ /* XXX These exception cases go away if apr_stat() returns the
+ * APR_PATHINCOMPLETE status, so we skip hard filesystem testing
+ * of the initial 'pseudo' elements:
+ */
+
+#if defined(HAVE_UNC_PATHS)
+ /* If the name is a UNC name, then do not perform any true file test
+ * against the machine name (start at //machine/share/)
+ * This is optimized to use the normal walk (skips the redundant '/' root)
+ */
+ if (num_dirs > 3 && test_filename[0] == '/' && test_filename[1] == '/')
+ iStart = 4;
+#endif
+
+#if defined(NETWARE)
+ /* If the name is a fully qualified volume name, then do not perform any
+ * true file test on the machine name (start at machine/share:/)
+ * XXX: The implementation eludes me at this moment...
+ * Does this make sense? Please test!
+ */
+ if (num_dirs > 1 && strchr(test_filename, '/') < strchr(test_filename, ':'))
+ iStart = 2;
+#endif
+
+#if defined(HAVE_DRIVE_LETTERS) || defined(NETWARE)
+ /* Should match <Directory> sections starting from '/', not 'e:/'
+ * (for example). WIN32/OS2/NETWARE do not have a single root directory,
+ * they have one for each filesystem. Traditionally, Apache has treated
+ * <Directory /> permissions as the base for the whole server, and this
+ * tradition should probably be preserved.
+ *
+ * NOTE: MUST SYNC WITH ap_make_dirstr_prefix() CHANGE IN src/main/util.c
+ */
+ if (test_filename[0] == '/')
+ i = 1;
+ else
+ i = 0;
+#else
+ /* Normal File Systems are rooted at / */
+ i = 1;
+#endif /* def HAVE_DRIVE_LETTERS || NETWARE */
+
+ /* j keeps track of which section we're on, see core_reorder_directories */
+ j = 0;
+ for (; i <= num_dirs; ++i) {
+ int overrides_here;
+ core_dir_config *core_dir = ap_get_module_config(per_dir_defaults,
+ &core_module);
+
+ /*
+ * XXX: this could be made faster by only copying the next component
+ * rather than copying the entire thing all over.
+ */
+ ap_make_dirstr_prefix(test_dirname, test_filename, i);
+
+ /*
+ * Do symlink checks first, because they are done with the
+ * permissions appropriate to the *parent* directory...
+ */
+
+#if defined(HAVE_UNC_PATHS) || defined(NETWARE)
+ /* Test only legal names against the real filesystem */
+ if (i >= iStart)
+#endif
+ if ((res = check_symlinks(test_dirname, core_dir->opts, r->pool))) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+ "Symbolic link not allowed: %s", test_dirname);
+ return res;
+ }
+
+ /*
+ * Begin *this* level by looking for matching <Directory> sections
+ * from access.conf.
+ */
+
+ for (; j < num_sec; ++j) {
+ char *entry_dir;
+
+ entry_config = sec[j];
+ entry_core = ap_get_module_config(entry_config, &core_module);
+ entry_dir = entry_core->d;
+
+ if (entry_core->r
+ || !ap_os_is_path_absolute(entry_dir)
+#if defined(HAVE_DRIVE_LETTERS) || defined(NETWARE)
+ /* To account for the top-level "/" directory when i == 0
+ * XXX: The net test may be wrong... may fail ap_os_is_path_absolute
+ */
+ || (entry_core->d_components > 1
+ && entry_core->d_components > i)
+#else
+ || entry_core->d_components > i
+#endif /* def HAVE_DRIVE_LETTERS || NETWARE */
+ )
+ break;
+
+ this_conf = NULL;
+ if (entry_core->d_is_fnmatch) {
+ if (!apr_fnmatch(entry_dir, test_dirname, FNM_PATHNAME)) {
+ this_conf = entry_config;
+ }
+ }
+ else if (!strcmp(test_dirname, entry_dir))
+ this_conf = entry_config;
+
+ if (this_conf) {
+ per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+ per_dir_defaults,
+ this_conf);
+ core_dir = ap_get_module_config(per_dir_defaults,
+ &core_module);
+ }
+#if defined(HAVE_DRIVE_LETTERS) || defined(NETWARE)
+ /* So that other top-level directory sections (e.g. "e:/") aren't
+ * skipped when i == 0
+ * XXX: I don't get you here, Tim... That's a level 1 section, but
+ * we are at level 0. Did you mean fast-forward to the next?
+ */
+ else if (!i)
+ break;
+#endif /* def HAVE_DRIVE_LETTERS || NETWARE */
+ }
+ overrides_here = core_dir->override;
+
+ /* If .htaccess files are enabled, check for one. */
+
+#if defined(HAVE_UNC_PATHS) || defined(NETWARE)
+ /* Test only legal names against the real filesystem */
+ if (i >= iStart)
+#endif
+ if (overrides_here) {
+ ap_conf_vector_t *htaccess_conf = NULL;
+
+ res = ap_parse_htaccess(&htaccess_conf, r, overrides_here,
+ apr_pstrdup(r->pool, test_dirname),
+ sconf->access_name);
+ if (res)
+ return res;
+
+ if (htaccess_conf) {
+ per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+ per_dir_defaults,
+ htaccess_conf);
+ r->per_dir_config = per_dir_defaults;
+ }
+ }
+ }
+
+ /*
+ * There's two types of IS_SPECIAL sections (see http_core.c), and we've
+ * already handled the proxy:-style stuff. Now we'll deal with the
+ * regexes.
+ */
+ for (; j < num_sec; ++j) {
+
+ entry_config = sec[j];
+ entry_core = ap_get_module_config(entry_config, &core_module);
+
+ if (entry_core->r) {
+ if (!ap_regexec(entry_core->r, test_dirname, 0, NULL, REG_NOTEOL)) {
+ per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+ per_dir_defaults,
+ entry_config);
+ }
+ }
+ }
+ r->per_dir_config = per_dir_defaults;
+
+ /*
+ * Symlink permissions are determined by the parent. If the request is
+ * for a directory then applying the symlink test here would use the
+ * permissions of the directory as opposed to its parent. Consider a
+ * symlink pointing to a dir with a .htaccess disallowing symlinks. If
+ * you access /symlink (or /symlink/) you would get a 403 without this
+ * S_ISDIR test. But if you accessed /symlink/index.html, for example,
+ * you would *not* get the 403.
+ */
+ if (r->finfo.filetype != APR_DIR
+ && (res = check_symlinks(r->filename, ap_allow_options(r), r->pool))) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+ "Symbolic link not allowed: %s", r->filename);
+ return res;
+ }
+ return OK; /* Can only "fail" if access denied by the
+ * symlink goop. */
+}
+
+AP_DECLARE(int) location_walk(request_rec *r)
+{
+ core_server_config *sconf = ap_get_module_config(r->server->module_config,
+ &core_module);
+ ap_conf_vector_t *per_dir_defaults = r->per_dir_config;
+ ap_conf_vector_t **url = (ap_conf_vector_t **) sconf->sec_url->elts;
+ int len, num_url = sconf->sec_url->nelts;
+ char *test_location;
+ ap_conf_vector_t *this_conf;
+ ap_conf_vector_t *entry_config;
+ core_dir_config *entry_core;
+ char *entry_url;
+ int j;
+
+ if (!num_url) {
+ return OK;
+ }
+
+ /* Location and LocationMatch differ on their behaviour w.r.t. multiple
+ * slashes. Location matches multiple slashes with a single slash,
+ * LocationMatch doesn't. An exception, for backwards brokenness is
+ * absoluteURIs... in which case neither match multiple slashes.
+ */
+ if (r->uri[0] != '/') {
+ test_location = r->uri;
+ }
+ else {
+ test_location = apr_pstrdup(r->pool, r->uri);
+ ap_no2slash(test_location);
+ }
+
+ /* Go through the location entries, and check for matches. */
+
+ /* we apply the directive sections in some order;
+ * should really try them with the most general first.
+ */
+ for (j = 0; j < num_url; ++j) {
+
+ entry_config = url[j];
+
+ entry_core = ap_get_module_config(entry_config, &core_module);
+ entry_url = entry_core->d;
+
+ len = strlen(entry_url);
+
+ this_conf = NULL;
+
+ if (entry_core->r) {
+ if (!ap_regexec(entry_core->r, r->uri, 0, NULL, 0))
+ this_conf = entry_config;
+ }
+ else if (entry_core->d_is_fnmatch) {
+ if (!apr_fnmatch(entry_url, test_location, FNM_PATHNAME)) {
+ this_conf = entry_config;
+ }
+ }
+ else if (!strncmp(test_location, entry_url, len) &&
+ (entry_url[len - 1] == '/' ||
+ test_location[len] == '/' || test_location[len] == '\0'))
+ this_conf = entry_config;
+
+ if (this_conf)
+ per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+ per_dir_defaults,
+ this_conf);
+ }
+ r->per_dir_config = per_dir_defaults;
+
+ return OK;
+}
+
+AP_DECLARE(int) file_walk(request_rec *r)
+{
+ core_dir_config *conf = ap_get_module_config(r->per_dir_config,
+ &core_module);
+ ap_conf_vector_t *per_dir_defaults = r->per_dir_config;
+ ap_conf_vector_t **file = (ap_conf_vector_t **) conf->sec->elts;
+ int num_files = conf->sec->nelts;
+ char *test_file;
+
+ /* get the basename */
+ test_file = strrchr(r->filename, '/');
+ if (test_file == NULL) {
+ test_file = r->filename;
+ }
+ else {
+ ++test_file;
+ }
+
+ /* Go through the file entries, and check for matches. */
+
+ if (num_files) {
+ ap_conf_vector_t *this_conf;
+ ap_conf_vector_t *entry_config;
+ core_dir_config *entry_core;
+ char *entry_file;
+ int j;
+
+ /* we apply the directive sections in some order;
+ * should really try them with the most general first.
+ */
+ for (j = 0; j < num_files; ++j) {
+
+ entry_config = file[j];
+
+ entry_core = ap_get_module_config(entry_config, &core_module);
+ entry_file = entry_core->d;
+
+ this_conf = NULL;
+
+ if (entry_core->r) {
+ if (!ap_regexec(entry_core->r, test_file, 0, NULL, 0))
+ this_conf = entry_config;
+ }
+ else if (entry_core->d_is_fnmatch) {
+ if (!apr_fnmatch(entry_file, test_file, FNM_PATHNAME)) {
+ this_conf = entry_config;
+ }
+ }
+ else if (!strcmp(test_file, entry_file)) {
+ this_conf = entry_config;
+ }
+
+ if (this_conf)
+ per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+ per_dir_defaults,
+ this_conf);
+ }
+ r->per_dir_config = per_dir_defaults;
+ }
+ return OK;
+}
+
+/*****************************************************************
+ *
+ * The sub_request mechanism.
+ *
+ * Fns to look up a relative URI from, e.g., a map file or SSI document.
+ * These do all access checks, etc., but don't actually run the transaction
+ * ... use run_sub_req below for that. Also, be sure to use destroy_sub_req
+ * as appropriate if you're likely to be creating more than a few of these.
+ * (An early Apache version didn't destroy the sub_reqs used in directory
+ * indexing. The result, when indexing a directory with 800-odd files in
+ * it, was massively excessive storage allocation).
+ *
+ * Note more manipulation of protocol-specific vars in the request
+ * structure...
+ */
+
+static request_rec *make_sub_request(const request_rec *r)
+{
+ apr_pool_t *rrp;
+ request_rec *rr;
+
+ apr_pool_create(&rrp, r->pool);
+ rr = apr_pcalloc(rrp, sizeof(request_rec));
+ rr->pool = rrp;
+ return rr;
+}
+
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_sub_req_output_filter(ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ apr_bucket *e = APR_BRIGADE_LAST(bb);
+
+ if (APR_BUCKET_IS_EOS(e)) {
+ apr_bucket_delete(e);
+ }
+ ap_pass_brigade(f->next, bb);
+ return APR_SUCCESS;
+}
+
+
+AP_DECLARE(int) ap_some_auth_required(request_rec *r)
+{
+ /* Is there a require line configured for the type of *this* req? */
+
+ const apr_array_header_t *reqs_arr = ap_requires(r);
+ require_line *reqs;
+ int i;
+
+ if (!reqs_arr)
+ return 0;
+
+ reqs = (require_line *) reqs_arr->elts;
+
+ for (i = 0; i < reqs_arr->nelts; ++i)
+ if (reqs[i].method_mask & (1 << r->method_number))
+ return 1;
+
+ return 0;
+}
+
+AP_DECLARE(request_rec *) ap_sub_req_method_uri(const char *method,
+ const char *new_file,
+ const request_rec *r,
+ ap_filter_t *next_filter)
+{
+ request_rec *rnew;
+ int res;
+ char *udir;
+
+ rnew = make_sub_request(r);
+ rnew->hostname = r->hostname;
+ rnew->request_time = r->request_time;
+ rnew->connection = r->connection;
+ rnew->server = r->server;
+
+ rnew->request_config = ap_create_request_config(rnew->pool);
+ ap_set_module_config(rnew->request_config, &core_module,
+ ap_get_module_config(r->request_config, &core_module));
+
+ rnew->htaccess = r->htaccess;
+ rnew->per_dir_config = r->server->lookup_defaults;
+ rnew->allowed_methods = ap_make_method_list(rnew->pool, 2);
+
+ /* make a copy of the allowed-methods list */
+ ap_copy_method_list(rnew->allowed_methods, r->allowed_methods);
+
+ /* start with the same set of output filters */
+ if (next_filter) {
+ rnew->output_filters = next_filter;
+ }
+ else {
+ rnew->output_filters = r->output_filters;
+ }
+ ap_add_output_filter("SUBREQ_CORE", NULL, rnew, rnew->connection);
+
+ /* no input filters for a subrequest */
+
+ ap_set_sub_req_protocol(rnew, r);
+
+ /* would be nicer to pass "method" to ap_set_sub_req_protocol */
+ rnew->method = method;
+ rnew->method_number = ap_method_number_of(method);
+
+ if (new_file[0] == '/')
+ ap_parse_uri(rnew, new_file);
+ else {
+ udir = ap_make_dirstr_parent(rnew->pool, r->uri);
+ udir = ap_escape_uri(rnew->pool, udir); /* re-escape it */
+ ap_parse_uri(rnew, ap_make_full_path(rnew->pool, udir, new_file));
+ }
+
+ res = ap_unescape_url(rnew->uri);
+ if (res) {
+ rnew->status = res;
+ return rnew;
+ }
+
+ ap_getparents(rnew->uri);
+
+ if ((res = location_walk(rnew))) {
+ rnew->status = res;
+ return rnew;
+ }
+
+ res = ap_run_translate_name(rnew);
+ if (res) {
+ rnew->status = res;
+ return rnew;
+ }
+
+ /*
+ * We could be clever at this point, and avoid calling directory_walk,
+ * etc. However, we'd need to test that the old and new filenames contain
+ * the same directory components, so it would require duplicating the
+ * start of translate_name. Instead we rely on the cache of .htaccess
+ * results.
+ *
+ * NB: directory_walk() clears the per_dir_config, so we don't inherit
+ * from location_walk() above
+ */
+
+ if ((res = directory_walk(rnew))
+ || (res = file_walk(rnew))
+ || (res = location_walk(rnew))
+ || ((ap_satisfies(rnew) == SATISFY_ALL
+ || ap_satisfies(rnew) == SATISFY_NOSPEC)
+ ? ((res = ap_run_access_checker(rnew))
+ || (ap_some_auth_required(rnew)
+ && ((res = ap_run_check_user_id(rnew))
+ || (res = ap_run_auth_checker(rnew)))))
+ : ((res = ap_run_access_checker(rnew))
+ && (!ap_some_auth_required(rnew)
+ || ((res = ap_run_check_user_id(rnew))
+ || (res = ap_run_auth_checker(rnew)))))
+ )
+ || (res = ap_run_type_checker(rnew))
+ || (res = ap_run_fixups(rnew))
+ ) {
+ rnew->status = res;
+ }
+ return rnew;
+}
+
+AP_DECLARE(request_rec *) ap_sub_req_lookup_uri(const char *new_file,
+ const request_rec *r,
+ ap_filter_t *next_filter)
+{
+ return ap_sub_req_method_uri("GET", new_file, r, next_filter);
+}
+
+AP_DECLARE(request_rec *) ap_sub_req_lookup_file(const char *new_file,
+ const request_rec *r,
+ ap_filter_t *next_filter)
+{
+ request_rec *rnew;
+ int res;
+ char *fdir;
+
+ rnew = make_sub_request(r);
+ rnew->hostname = r->hostname;
+ rnew->request_time = r->request_time;
+ rnew->connection = r->connection;
+ rnew->server = r->server;
+
+ rnew->request_config = ap_create_request_config(rnew->pool);
+ ap_set_module_config(rnew->request_config, &core_module,
+ ap_get_module_config(r->request_config, &core_module));
+
+ rnew->htaccess = r->htaccess;
+ rnew->chunked = r->chunked;
+ rnew->allowed_methods = ap_make_method_list(rnew->pool, 2);
+
+ /* make a copy of the allowed-methods list */
+ ap_copy_method_list(rnew->allowed_methods, r->allowed_methods);
+
+ /* start with the same set of output filters */
+ if (next_filter) {
+ rnew->output_filters = next_filter;
+ }
+ else {
+ rnew->output_filters = r->output_filters;
+ }
+ ap_add_output_filter("SUBREQ_CORE", NULL, rnew, rnew->connection);
+
+ /* no input filters for a subrequest */
+
+ ap_set_sub_req_protocol(rnew, r);
+ fdir = ap_make_dirstr_parent(rnew->pool, r->filename);
+
+ /*
+ * Check for a special case... if there are no '/' characters in new_file
+ * at all, then we are looking at a relative lookup in the same
+ * directory. That means we won't have to redo directory_walk, and we may
+ * not even have to redo access checks.
+ */
+
+ if (ap_strchr_c(new_file, '/') == NULL) {
+ char *udir = ap_make_dirstr_parent(rnew->pool, r->uri);
+ apr_status_t rv;
+
+ rnew->uri = ap_make_full_path(rnew->pool, udir, new_file);
+ rnew->filename = ap_make_full_path(rnew->pool, fdir, new_file);
+ ap_parse_uri(rnew, rnew->uri); /* fill in parsed_uri values */
+
+ if (((rv = apr_stat(&rnew->finfo, rnew->filename,
+ APR_FINFO_MIN, rnew->pool)) != APR_SUCCESS)
+ && (rv != APR_INCOMPLETE)) {
+ rnew->finfo.filetype = 0;
+ }
+
+ if ((res = check_safe_file(rnew))) {
+ rnew->status = res;
+ return rnew;
+ }
+
+ rnew->per_dir_config = r->per_dir_config;
+
+ /*
+ * no matter what, if it's a subdirectory, we need to re-run
+ * directory_walk
+ */
+ if (rnew->finfo.filetype == APR_DIR) {
+ res = directory_walk(rnew);
+ if (!res) {
+ res = file_walk(rnew);
+ }
+ }
+ else {
+ if ((res = check_symlinks(rnew->filename, ap_allow_options(rnew),
+ rnew->pool))) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, rnew,
+ "Symbolic link not allowed: %s", rnew->filename);
+ rnew->status = res;
+ return rnew;
+ }
+ /*
+ * do a file_walk, if it doesn't change the per_dir_config then
+ * we know that we don't have to redo all the access checks
+ */
+ if ((res = file_walk(rnew))) {
+ rnew->status = res;
+ return rnew;
+ }
+ if (rnew->per_dir_config == r->per_dir_config) {
+ if ((res = ap_run_type_checker(rnew)) || (res = ap_run_fixups(rnew))) {
+ rnew->status = res;
+ }
+ return rnew;
+ }
+ }
+ }
+ else {
+ /* XXX: @@@: What should be done with the parsed_uri values? */
+ ap_parse_uri(rnew, new_file); /* fill in parsed_uri values */
+ /*
+ * XXX: this should be set properly like it is in the same-dir case
+ * but it's actually sometimes to impossible to do it... because the
+ * file may not have a uri associated with it -djg
+ */
+ rnew->uri = "INTERNALLY GENERATED file-relative req";
+ rnew->filename = ((ap_os_is_path_absolute(new_file)) ?
+ apr_pstrdup(rnew->pool, new_file) :
+ ap_make_full_path(rnew->pool, fdir, new_file));
+ rnew->per_dir_config = r->server->lookup_defaults;
+ res = directory_walk(rnew);
+ if (!res) {
+ res = file_walk(rnew);
+ }
+ }
+
+ if (res
+ || ((ap_satisfies(rnew) == SATISFY_ALL
+ || ap_satisfies(rnew) == SATISFY_NOSPEC)
+ ? ((res = ap_run_access_checker(rnew))
+ || (ap_some_auth_required(rnew)
+ && ((res = ap_run_check_user_id(rnew))
+ || (res = ap_run_auth_checker(rnew)))))
+ : ((res = ap_run_access_checker(rnew))
+ && (!ap_some_auth_required(rnew)
+ || ((res = ap_run_check_user_id(rnew))
+ || (res = ap_run_auth_checker(rnew)))))
+ )
+ || (res = ap_run_type_checker(rnew))
+ || (res = ap_run_fixups(rnew))
+ ) {
+ rnew->status = res;
+ }
+ return rnew;
+}
+
+AP_DECLARE(int) ap_run_sub_req(request_rec *r)
+{
+ int retval;
+
+ /* see comments in process_request_internal() */
+ ap_run_insert_filter(r);
+ retval = ap_invoke_handler(r);
+ ap_finalize_sub_req_protocol(r);
+ return retval;
+}
+
+AP_DECLARE(void) ap_destroy_sub_req(request_rec *r)
+{
+ /* Reclaim the space */
+ apr_pool_destroy(r->pool);
+}
+
+/*
+ * Function to set the r->mtime field to the specified value if it's later
+ * than what's already there.
+ */
+AP_DECLARE(void) ap_update_mtime(request_rec *r, apr_time_t dependency_mtime)
+{
+ if (r->mtime < dependency_mtime) {
+ r->mtime = dependency_mtime;
+ }
+}
+
+/*
+ * Is it the initial main request, which we only get *once* per HTTP request?
+ */
+AP_DECLARE(int) ap_is_initial_req(request_rec *r)
+{
+ return
+ (r->main == NULL) /* otherwise, this is a sub-request */
+ &&
+ (r->prev == NULL); /* otherwise, this is an internal redirect */
+}
+
diff --git a/server/scoreboard.c b/server/scoreboard.c
new file mode 100644
index 0000000000..e36bb89222
--- /dev/null
+++ b/server/scoreboard.c
@@ -0,0 +1,283 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "apr_strings.h"
+#include "apr_portable.h"
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "http_core.h"
+#include "http_config.h"
+#include "unixd.h"
+#include "http_conf_globals.h"
+#include "mpm.h"
+#include "scoreboard.h"
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+AP_DECLARE_DATA scoreboard *ap_scoreboard_image = NULL;
+AP_DECLARE_DATA const char *ap_scoreboard_fname=NULL;
+AP_DECLARE_DATA int ap_extended_status = 0;
+AP_DECLARE_DATA apr_time_t ap_restart_time = 0;
+
+#if APR_HAS_SHARED_MEMORY
+#include "apr_shmem.h"
+
+static apr_shmem_t *scoreboard_shm = NULL;
+
+apr_status_t ap_cleanup_shared_mem(void *d)
+{
+ apr_shm_free(scoreboard_shm, ap_scoreboard_image);
+ ap_scoreboard_image = NULL;
+ apr_shm_destroy(scoreboard_shm);
+
+ return APR_SUCCESS;
+}
+
+static void setup_shared_mem(apr_pool_t *p)
+{
+ char buf[512];
+ char errmsg[120];
+ const char *fname;
+ apr_status_t rv;
+
+ fname = ap_server_root_relative(p, ap_scoreboard_fname);
+ rv = apr_shm_init(&scoreboard_shm, SCOREBOARD_SIZE, fname, p);
+ if (rv != APR_SUCCESS) {
+ apr_snprintf(buf, sizeof(buf), "%s: could not open(create) scoreboard: %s",
+ ap_server_argv0, apr_strerror(rv, errmsg, sizeof errmsg));
+ fprintf(stderr, "%s\n", buf);
+ exit(APEXIT_INIT);
+ }
+ ap_scoreboard_image = apr_shm_malloc(scoreboard_shm, SCOREBOARD_SIZE);
+ if (ap_scoreboard_image == NULL) {
+ apr_snprintf(buf, sizeof(buf), "%s: cannot allocate scoreboard",
+ ap_server_argv0);
+ perror(buf); /* o.k. since MM sets errno */
+ apr_shm_destroy(scoreboard_shm);
+ exit(APEXIT_INIT);
+ }
+ apr_register_cleanup(p, NULL, ap_cleanup_shared_mem, apr_null_cleanup);
+ ap_scoreboard_image->global.running_generation = 0;
+}
+
+void reopen_scoreboard(apr_pool_t *p)
+{
+}
+#endif /* APR_SHARED_MEM */
+
+/* Called by parent process */
+void reinit_scoreboard(apr_pool_t *p)
+{
+ int running_gen = 0;
+ if (ap_scoreboard_image)
+ running_gen = ap_scoreboard_image->global.running_generation;
+ if (ap_scoreboard_image == NULL) {
+ setup_shared_mem(p);
+ }
+ memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE);
+ ap_scoreboard_image->global.running_generation = running_gen;
+}
+
+/* Routines called to deal with the scoreboard image
+ * --- note that we do *not* need write locks, since update_child_status
+ * only updates a *single* record in place, and only one process writes to
+ * a given scoreboard slot at a time (either the child process owning that
+ * slot, or the parent, noting that the child has died).
+ *
+ * As a final note --- setting the score entry to getpid() is always safe,
+ * since when the parent is writing an entry, it's only noting SERVER_DEAD
+ * anyway.
+ */
+
+apr_inline void ap_sync_scoreboard_image(void)
+{
+}
+
+AP_DECLARE(int) ap_exists_scoreboard_image(void)
+{
+ return (ap_scoreboard_image ? 1 : 0);
+}
+
+static apr_inline void put_scoreboard_info(int child_num, int thread_num,
+ short_score *new_score_rec)
+{
+ /* XXX - needs to be fixed to account for threads */
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd, (long) child_num * sizeof(short_score), 0);
+ force_write(scoreboard_fd, new_score_rec, sizeof(short_score));
+#endif
+}
+
+void update_scoreboard_global(void)
+{
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd,
+ (char *) &ap_scoreboard_image->global -(char *) ap_scoreboard_image, 0);
+ force_write(scoreboard_fd, &ap_scoreboard_image->global,
+ sizeof ap_scoreboard_image->global);
+#endif
+}
+
+void increment_counts(int child_num, int thread_num, request_rec *r)
+{
+ short_score *ss;
+
+ ss = &ap_scoreboard_image->servers[child_num][thread_num];
+
+#ifdef HAVE_TIMES
+ times(&ss->times);
+#endif
+ ss->access_count++;
+ ss->my_access_count++;
+ ss->conn_count++;
+ ss->bytes_served += r->bytes_sent;
+ ss->my_bytes_served += r->bytes_sent;
+ ss->conn_bytes += r->bytes_sent;
+
+ put_scoreboard_info(child_num, thread_num, ss);
+}
+
+AP_DECLARE(int) find_child_by_pid(apr_proc_t *pid)
+{
+ int i;
+ int max_daemons_limit = ap_get_max_daemons();
+
+ for (i = 0; i < max_daemons_limit; ++i)
+ if (ap_scoreboard_image->parent[i].pid == pid->pid)
+ return i;
+
+ return -1;
+}
+
+int ap_update_child_status(int child_num, int thread_num, int status, request_rec *r)
+{
+ int old_status;
+ short_score *ss;
+ parent_score *ps;
+
+ if (child_num < 0)
+ return -1;
+
+ ss = &ap_scoreboard_image->servers[child_num][thread_num];
+ old_status = ss->status;
+ ss->status = status;
+
+ ps = &ap_scoreboard_image->parent[child_num];
+
+ if ((status == SERVER_READY || status == SERVER_ACCEPTING)
+ && old_status == SERVER_STARTING) {
+ ss->thread_num = child_num * HARD_SERVER_LIMIT + thread_num;
+ ps->worker_threads = ap_threads_per_child;
+ }
+
+ if (ap_extended_status) {
+ if (status == SERVER_READY || status == SERVER_DEAD) {
+ /*
+ * Reset individual counters
+ */
+ if (status == SERVER_DEAD) {
+ ss->my_access_count = 0L;
+ ss->my_bytes_served = 0L;
+ }
+ ss->conn_count = (unsigned short) 0;
+ ss->conn_bytes = (unsigned long) 0;
+ }
+ if (r) {
+ conn_rec *c = r->connection;
+ apr_cpystrn(ss->client, ap_get_remote_host(c, r->per_dir_config,
+ REMOTE_NOLOOKUP), sizeof(ss->client));
+ if (r->the_request == NULL) {
+ apr_cpystrn(ss->request, "NULL", sizeof(ss->request));
+ } else if (r->parsed_uri.password == NULL) {
+ apr_cpystrn(ss->request, r->the_request, sizeof(ss->request));
+ } else {
+ /* Don't reveal the password in the server-status view */
+ apr_cpystrn(ss->request, apr_pstrcat(r->pool, r->method, " ",
+ ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITPASSWORD),
+ r->assbackwards ? NULL : " ", r->protocol, NULL),
+ sizeof(ss->request));
+ }
+ ss->vhostrec = r->server;
+ }
+ }
+
+ put_scoreboard_info(child_num, thread_num, ss);
+ return old_status;
+}
+
+void ap_time_process_request(int child_num, int thread_num, int status)
+{
+ short_score *ss;
+
+ if (child_num < 0)
+ return;
+
+ ss = &ap_scoreboard_image->servers[child_num][thread_num];
+
+ if (status == START_PREQUEST) {
+ ss->start_time = apr_now();
+ }
+ else if (status == STOP_PREQUEST) {
+ ss->stop_time = apr_now();
+ }
+ put_scoreboard_info(child_num, thread_num, ss);
+}
+
diff --git a/server/util_charset.c b/server/util_charset.c
new file mode 100644
index 0000000000..8a613f9075
--- /dev/null
+++ b/server/util_charset.c
@@ -0,0 +1,122 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "ap_config.h"
+
+#ifdef APACHE_XLATE
+
+#include "httpd.h"
+#include "http_log.h"
+#include "http_core.h"
+#include "util_charset.h"
+
+/* ap_hdrs_to_ascii, ap_hdrs_from_ascii
+ *
+ * These are the translation handles used to translate between the network
+ * format of protocol headers and the local machine format.
+ *
+ * For an EBCDIC machine, these are valid handles which are set up at
+ * initialization to translate between ISO-8859-1 and the code page of
+ * the source code.
+ *
+ * For an ASCII machine, these remain NULL so that when they are stored
+ * in the BUFF via ap_bsetop(BO_WXLATE or BO_RXLATE) it ensures that no
+ * translation is performed.
+ */
+
+ap_xlate_t *ap_hdrs_to_ascii, *ap_hdrs_from_ascii;
+
+/* ap_locale_to_ascii, ap_locale_from_ascii
+ *
+ * These handles are used for the translation of content, unless a
+ * configuration module overrides them.
+ *
+ * For an EBCDIC machine, these are valid handles which are set up at
+ * initialization to translate between ISO-8859-1 and the code page of
+ * the httpd process's locale.
+ *
+ * For an ASCII machine, these remain NULL so that no translation is
+ * performed (unless a configuration module does something, of course).
+ */
+
+ap_xlate_t *ap_locale_to_ascii, *ap_locale_from_ascii;
+
+API_EXPORT(ap_status_t) ap_set_content_xlate(request_rec *r, int output,
+ ap_xlate_t *xlate)
+{
+ ap_status_t rv;
+
+ if (output) {
+ r->rrx->to_net = xlate;
+ rv = ap_bsetopt(r->connection->client, BO_WXLATE, &xlate);
+ }
+ else {
+ r->rrx->from_net = xlate;
+ rv = ap_bsetopt(r->connection->client, BO_RXLATE, &xlate);
+ }
+
+ if (rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ "BO_%sXLATE failed",
+ output ? "W" : "R");
+ }
+
+ return rv;
+}
+
+#endif /*APACHE_XLATE*/
diff --git a/server/util_debug.c b/server/util_debug.c
new file mode 100644
index 0000000000..415fdcac8e
--- /dev/null
+++ b/server/util_debug.c
@@ -0,0 +1,78 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "httpd.h"
+
+#ifdef AP_DEBUG
+# undef strchr
+
+char *ap_strchr(char *s, int c)
+{ return strchr(s,c); }
+
+const char *ap_strchr_c(const char *s, int c)
+{ return strchr(s,c); }
+
+# undef strrchr
+
+char *ap_strrchr(char *s, int c)
+{ return strrchr(s,c); }
+
+const char *ap_strrchr_c(const char *s, int c)
+{ return strrchr(s,c); }
+
+#endif
diff --git a/server/util_ebcdic.c b/server/util_ebcdic.c
new file mode 100644
index 0000000000..e1f5e4cebe
--- /dev/null
+++ b/server/util_ebcdic.c
@@ -0,0 +1,103 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#define CORE_PRIVATE
+#include "ap_config.h"
+
+#ifdef CHARSET_EBCDIC
+
+#include "httpd.h"
+#include "http_log.h"
+#include "util_ebcdic.h"
+
+ap_xlate_t *hdrs_to_ascii, *hdrs_from_ascii;
+ap_xlate_t *locale_to_ascii, *locale_from_ascii;
+
+ap_status_t ap_init_ebcdic(ap_pool_t *pool)
+{
+ ap_status_t rv;
+ char buf[80];
+
+ rv = ap_xlate_open(&hdrs_to_ascii, "ISO8859-1", APR_DEFAULT_CHARSET, pool);
+ if (rv) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "ap_xlate_open() failed");
+ return rv;
+ }
+
+ rv = ap_xlate_open(&hdrs_from_ascii, APR_DEFAULT_CHARSET, "ISO8859-1", pool);
+ if (rv) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "ap_xlate_open() failed");
+ return rv;
+ }
+
+ locale_to_ascii = hdrs_to_ascii; /* TODO: "ISO8859-1" to APR_LOCALE_CHARSET */
+ locale_from_ascii = hdrs_from_ascii; /* TODO: APR_LOCALE_CHARSET to "ISO8859-1" */
+
+ rv = ap_MD5InitEBCDIC(hdrs_to_ascii);
+ if (rv) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "ap_MD5InitEBCDIC() failed");
+ return rv;
+ }
+
+ return APR_SUCCESS;
+}
+
+#endif /* CHARSET_EBCDIC */
diff --git a/server/util_filter.c b/server/util_filter.c
new file mode 100644
index 0000000000..77bdc98728
--- /dev/null
+++ b/server/util_filter.c
@@ -0,0 +1,146 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+#include "util_filter.h"
+
+/*
+ * ap_filter_rec_t:
+ *
+ * This (internal) structure is used for recording information about the
+ * registered filters. It associates a name with the filter's callback
+ * and filter type.
+ *
+ * At the moment, these are simply linked in a chain, so a ->next pointer
+ * is available.
+ */
+typedef struct ap_filter_rec_t {
+ const char *name;
+ ap_filter_func filter_func;
+ ap_filter_type ftype;
+
+ struct ap_filter_rec_t *next;
+} ap_filter_rec_t;
+
+/* ### make this visible for direct manipulation?
+ ### use a hash table
+*/
+static ap_filter_rec_t *registered_filters = NULL;
+
+/* NOTE: Apache's current design doesn't allow a pool to be passed thu,
+ so we depend on a global to hold the correct pool
+*/
+#define FILTER_POOL ap_global_hook_pool
+#include "ap_hooks.h" /* for ap_global_hook_pool */
+
+/*
+** This macro returns true/false if a given filter should be inserted BEFORE
+** another filter. This will happen when one of: 1) there isn't another
+** filter; 2) that filter has a higher filter type (class); 3) that filter
+** corresponds to a different request.
+*/
+#define INSERT_BEFORE(f, before_this) ((before_this) == NULL \
+ || (before_this)->ftype > (f)->ftype)
+
+
+static ap_status_t filter_cleanup(void *ctx)
+{
+ registered_filters = NULL;
+ return APR_SUCCESS;
+}
+
+API_EXPORT(void) ap_register_filter(const char *name,
+ ap_filter_func filter_func,
+ ap_filter_type ftype)
+{
+ ap_filter_rec_t *frec = ap_palloc(FILTER_POOL, sizeof(*frec));
+
+ frec->name = name;
+ frec->filter_func = filter_func;
+ frec->ftype = ftype;
+
+ frec->next = registered_filters;
+ registered_filters = frec;
+
+ ap_register_cleanup(FILTER_POOL, NULL, filter_cleanup, NULL);
+}
+
+API_EXPORT(void) ap_add_filter(const char *name, void *ctx, request_rec *r)
+{
+ ap_filter_rec_t *frec = registered_filters;
+
+ for (; frec != NULL; frec = frec->next) {
+ if (!strcasecmp(name, frec->name)) {
+ ap_filter_t *f = ap_pcalloc(r->pool, sizeof(*f));
+
+ f->filter_func = frec->filter_func;
+ f->ctx = ctx;
+ f->ftype = frec->ftype;
+
+ if (INSERT_BEFORE(f, r->filters)) {
+ f->next = r->filters;
+ r->filters = f;
+ }
+ else {
+ ap_filter_t *fscan = r->filters;
+ while (!INSERT_BEFORE(f, fscan->next))
+ fscan = fscan->next;
+ f->next = fscan->next;
+ fscan->next = f;
+ }
+
+ break;
+ }
+ }
+}
+
diff --git a/server/util_xml.c b/server/util_xml.c
new file mode 100644
index 0000000000..26c8a0fc0c
--- /dev/null
+++ b/server/util_xml.c
@@ -0,0 +1,421 @@
+/*
+** Copyright (C) 1998-2000 Greg Stein. All Rights Reserved.
+**
+** By using this file, you agree to the terms and conditions set forth in
+** the LICENSE.html file which can be found at the top level of the mod_dav
+** distribution or at http://www.webdav.org/mod_dav/license-1.html.
+**
+** Contact information:
+** Greg Stein, PO Box 760, Palo Alto, CA, 94302
+** gstein@lyra.org, http://www.webdav.org/mod_dav/
+*/
+
+/*
+** DAV extension module for Apache 1.3.*
+** - XML parser for the body of a request
+**
+** Written by Greg Stein, gstein@lyra.org, http://www.lyra.org/
+*/
+
+/* James Clark's Expat parser */
+#include <xmlparse.h>
+
+#include "httpd.h"
+#include "http_protocol.h"
+#include "http_log.h"
+
+#include "mod_dav.h"
+
+
+/* errors related to namespace processing */
+#define DAV_NS_ERROR_UNKNOWN_PREFIX (DAV_NS_ERROR_BASE)
+
+/* test for a namespace prefix that begins with [Xx][Mm][Ll] */
+#define DAV_NS_IS_RESERVED(name) \
+ ( (name[0] == 'X' || name[0] == 'x') && \
+ (name[1] == 'M' || name[1] == 'm') && \
+ (name[2] == 'L' || name[2] == 'l') )
+
+/* content for parsing */
+typedef struct dav_xml_ctx {
+ dav_xml_doc *doc; /* the doc we're parsing */
+ ap_pool *p; /* the pool we allocate from */
+ dav_xml_elem *cur_elem; /* current element */
+
+ int error; /* an error has occurred */
+ /* errors may be DAV_NS_ERROR_* or other errors defined here (none yet) */
+
+} dav_xml_ctx;
+
+/* struct for scoping namespace declarations */
+typedef struct dav_xml_ns_scope {
+ const char *prefix; /* prefix used for this ns */
+ int ns; /* index into namespace table */
+ int emptyURI; /* the namespace URI is the empty string */
+ struct dav_xml_ns_scope *next; /* next scoped namespace */
+} dav_xml_ns_scope;
+
+/* ### need a similar mechanism for xml:lang values */
+
+
+/* return namespace table index for a given prefix */
+static int dav_find_prefix(dav_xml_ctx *ctx, const char *prefix)
+{
+ dav_xml_elem *elem = ctx->cur_elem;
+
+ /*
+ ** Walk up the tree, looking for a namespace scope that defines this
+ ** prefix.
+ */
+ for (; elem; elem = elem->parent) {
+ dav_xml_ns_scope *ns_scope = elem->ns_scope;
+
+ for (ns_scope = elem->ns_scope; ns_scope; ns_scope = ns_scope->next) {
+ if (strcmp(prefix, ns_scope->prefix) == 0) {
+ if (ns_scope->emptyURI) {
+ /*
+ ** It is possible to set the default namespace to an
+ ** empty URI string; this resets the default namespace
+ ** to mean "no namespace." We just found the prefix
+ ** refers to an empty URI, so return "no namespace."
+ */
+ return DAV_NS_NONE;
+ }
+
+ return ns_scope->ns;
+ }
+ }
+ }
+
+ /*
+ * If the prefix is empty (""), this means that a prefix was not
+ * specified in the element/attribute. The search that was performed
+ * just above did not locate a default namespace URI (which is stored
+ * into ns_scope with an empty prefix). This means the element/attribute
+ * has "no namespace". We have a reserved value for this.
+ */
+ if (*prefix == '\0') {
+ return DAV_NS_NONE;
+ }
+
+ /* not found */
+ return DAV_NS_ERROR_UNKNOWN_PREFIX;
+}
+
+static void dav_start_handler(void *userdata, const char *name, const char **attrs)
+{
+ dav_xml_ctx *ctx = userdata;
+ dav_xml_elem *elem;
+ dav_xml_attr *attr;
+ dav_xml_attr *prev;
+ char *colon;
+ const char *quoted;
+
+ /* punt once we find an error */
+ if (ctx->error)
+ return;
+
+ elem = ap_pcalloc(ctx->p, sizeof(*elem));
+
+ /* prep the element */
+ elem->name = ap_pstrdup(ctx->p, name);
+
+ /* fill in the attributes (note: ends up in reverse order) */
+ while (*attrs) {
+ attr = ap_palloc(ctx->p, sizeof(*attr));
+ attr->name = ap_pstrdup(ctx->p, *attrs++);
+ attr->value = ap_pstrdup(ctx->p, *attrs++);
+ attr->next = elem->attr;
+ elem->attr = attr;
+ }
+
+ /* hook the element into the tree */
+ if (ctx->cur_elem == NULL) {
+ /* no current element; this also becomes the root */
+ ctx->cur_elem = ctx->doc->root = elem;
+ }
+ else {
+ /* this element appeared within the current elem */
+ elem->parent = ctx->cur_elem;
+
+ /* set up the child/sibling links */
+ if (elem->parent->last_child == NULL) {
+ /* no first child either */
+ elem->parent->first_child = elem->parent->last_child = elem;
+ }
+ else {
+ /* hook onto the end of the parent's children */
+ elem->parent->last_child->next = elem;
+ elem->parent->last_child = elem;
+ }
+
+ /* this element is now the current element */
+ ctx->cur_elem = elem;
+ }
+
+ /* scan the attributes for namespace declarations */
+ for (prev = NULL, attr = elem->attr;
+ attr;
+ attr = attr->next) {
+ if (strncmp(attr->name, "xmlns", 5) == 0) {
+ const char *prefix = &attr->name[5];
+ dav_xml_ns_scope *ns_scope;
+
+ /* test for xmlns:foo= form and xmlns= form */
+ if (*prefix == ':')
+ ++prefix;
+ else if (*prefix != '\0') {
+ /* advance "prev" since "attr" is still present */
+ prev = attr;
+ continue;
+ }
+
+ /* quote the URI before we ever start working with it */
+ quoted = dav_quote_string(ctx->p, attr->value, 1);
+
+ /* build and insert the new scope */
+ ns_scope = ap_pcalloc(ctx->p, sizeof(*ns_scope));
+ ns_scope->prefix = prefix;
+ ns_scope->ns = dav_insert_uri(ctx->doc->namespaces, quoted);
+ ns_scope->emptyURI = *quoted == '\0';
+ ns_scope->next = elem->ns_scope;
+ elem->ns_scope = ns_scope;
+
+ /* remove this attribute from the element */
+ if (prev == NULL)
+ elem->attr = attr->next;
+ else
+ prev->next = attr->next;
+
+ /* Note: prev will not be advanced since we just removed "attr" */
+ }
+ else if (strcmp(attr->name, "xml:lang") == 0) {
+ /* save away the language (in quoted form) */
+ elem->lang = dav_quote_string(ctx->p, attr->value, 1);
+
+ /* remove this attribute from the element */
+ if (prev == NULL)
+ elem->attr = attr->next;
+ else
+ prev->next = attr->next;
+
+ /* Note: prev will not be advanced since we just removed "attr" */
+ }
+ else {
+ /* advance "prev" since "attr" is still present */
+ prev = attr;
+ }
+ }
+
+ /*
+ ** If an xml:lang attribute didn't exist (lang==NULL), then copy the
+ ** language from the parent element (if present).
+ **
+ ** NOTE: dav_elem_size() *depends* upon this pointer equality.
+ */
+ if (elem->lang == NULL && elem->parent != NULL)
+ elem->lang = elem->parent->lang;
+
+ /* adjust the element's namespace */
+ colon = strchr(elem->name, ':');
+ if (colon == NULL) {
+ /*
+ * The element is using the default namespace, which will always
+ * be found. Either it will be "no namespace", or a default
+ * namespace URI has been specified at some point.
+ */
+ elem->ns = dav_find_prefix(ctx, "");
+ }
+ else if (DAV_NS_IS_RESERVED(elem->name)) {
+ elem->ns = DAV_NS_NONE;
+ }
+ else {
+ *colon = '\0';
+ elem->ns = dav_find_prefix(ctx, elem->name);
+ elem->name = colon + 1;
+
+ if (DAV_NS_IS_ERROR(elem->ns)) {
+ ctx->error = elem->ns;
+ return;
+ }
+ }
+
+ /* adjust all remaining attributes' namespaces */
+ for (attr = elem->attr; attr; attr = attr->next) {
+ colon = strchr(attr->name, ':');
+ if (colon == NULL) {
+ /*
+ * Attributes do NOT use the default namespace. Therefore,
+ * we place them into the "no namespace" category.
+ */
+ attr->ns = DAV_NS_NONE;
+ }
+ else if (DAV_NS_IS_RESERVED(attr->name)) {
+ attr->ns = DAV_NS_NONE;
+ }
+ else {
+ *colon = '\0';
+ attr->ns = dav_find_prefix(ctx, attr->name);
+ attr->name = colon + 1;
+
+ if (DAV_NS_IS_ERROR(attr->ns)) {
+ ctx->error = attr->ns;
+ return;
+ }
+ }
+ }
+}
+
+static void dav_end_handler(void *userdata, const char *name)
+{
+ dav_xml_ctx *ctx = userdata;
+
+ /* punt once we find an error */
+ if (ctx->error)
+ return;
+
+ /* pop up one level */
+ ctx->cur_elem = ctx->cur_elem->parent;
+}
+
+static void dav_cdata_handler(void *userdata, const char *data, int len)
+{
+ dav_xml_ctx *ctx = userdata;
+ dav_xml_elem *elem;
+ dav_text_header *hdr;
+ const char *s;
+
+ /* punt once we find an error */
+ if (ctx->error)
+ return;
+
+ elem = ctx->cur_elem;
+ s = ap_pstrndup(ctx->p, data, len);
+
+ if (elem->last_child == NULL) {
+ /* no children yet. this cdata follows the start tag */
+ hdr = &elem->first_cdata;
+ }
+ else {
+ /* child elements exist. this cdata follows the last child. */
+ hdr = &elem->last_child->following_cdata;
+ }
+
+ dav_text_append(ctx->p, hdr, s);
+}
+
+int dav_parse_input(request_rec * r, dav_xml_doc **pdoc)
+{
+ int result;
+ dav_xml_ctx ctx =
+ {0};
+ XML_Parser parser;
+
+ if ((result = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) != OK)
+ return result;
+
+ if (r->remaining == 0) {
+ *pdoc = NULL;
+ return OK;
+ }
+
+ ctx.p = r->pool;
+ ctx.doc = ap_pcalloc(ctx.p, sizeof(*ctx.doc));
+
+ ctx.doc->namespaces = ap_make_array(ctx.p, 5, sizeof(const char *));
+ dav_insert_uri(ctx.doc->namespaces, "DAV:");
+
+ /* ### we should get the encoding from Content-Encoding */
+ parser = XML_ParserCreate(NULL);
+ if (parser == NULL) {
+ /* ### anything better to do? */
+ fprintf(stderr, "Ouch! XML_ParserCreate() failed!\n");
+ exit(1);
+ }
+
+ XML_SetUserData(parser, (void *) &ctx);
+ XML_SetElementHandler(parser, dav_start_handler, dav_end_handler);
+ XML_SetCharacterDataHandler(parser, dav_cdata_handler);
+
+ if (ap_should_client_block(r)) {
+ long len;
+ char *buffer;
+ char end;
+ int rv;
+ size_t total_read = 0;
+ size_t limit_xml_body = dav_get_limit_xml_body(r);
+
+ /* allocate our working buffer */
+ buffer = ap_palloc(r->pool, DAV_READ_BLOCKSIZE);
+
+ /* read the body, stuffing it into the parser */
+ while ((len = ap_get_client_block(r, buffer, DAV_READ_BLOCKSIZE)) > 0) {
+ total_read += len;
+ if (limit_xml_body && total_read > limit_xml_body) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r,
+ "XML request body is larger than the configured "
+ "limit of %lu", (unsigned long)limit_xml_body);
+ goto read_error;
+ }
+
+ rv = XML_Parse(parser, buffer, len, 0);
+ if (rv == 0)
+ goto parser_error;
+ }
+ if (len == -1) {
+ /* ap_get_client_block() has logged an error */
+ goto read_error;
+ }
+
+ /* tell the parser that we're done */
+ rv = XML_Parse(parser, &end, 0, 1);
+ if (rv == 0)
+ goto parser_error;
+ }
+
+ XML_ParserFree(parser);
+
+ if (ctx.error) {
+ switch (ctx.error) {
+ case DAV_NS_ERROR_UNKNOWN_PREFIX:
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "An undefined namespace prefix was used.");
+ break;
+
+ default:
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "There was an error within the XML request body.");
+ break;
+ }
+
+ /* Apache will supply a default error, plus the error log above. */
+ return HTTP_BAD_REQUEST;
+ }
+
+ /* ### assert: ctx.cur_elem == NULL */
+
+ *pdoc = ctx.doc;
+
+ return OK;
+
+ parser_error:
+ {
+ enum XML_Error err = XML_GetErrorCode(parser);
+
+ /* ### fix this error message (default vs special) */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
+ "XML parser error code: %s (%d).",
+ XML_ErrorString(err), err);
+
+ XML_ParserFree(parser);
+
+ /* Apache will supply a default error, plus the error log above. */
+ return HTTP_BAD_REQUEST;
+ }
+
+ read_error:
+ XML_ParserFree(parser);
+
+ /* Apache will supply a default error, plus whatever was logged. */
+ return HTTP_BAD_REQUEST;
+}
diff --git a/srclib/pcre/.cvsignore b/srclib/pcre/.cvsignore
new file mode 100644
index 0000000000..ba115cc749
--- /dev/null
+++ b/srclib/pcre/.cvsignore
@@ -0,0 +1,14 @@
+*.la
+*.lo
+.libs
+Makefile
+chartables.c
+config.h
+config.log
+config.status
+dftables
+libtool
+pcre-config
+pcre.h
+pcretest
+pgrep
diff --git a/srclib/pcre/config.hw b/srclib/pcre/config.hw
new file mode 100644
index 0000000000..124f5de2ce
--- /dev/null
+++ b/srclib/pcre/config.hw
@@ -0,0 +1,28 @@
+
+/* On Unix systems config.in is converted by configure into config.h. PCRE is
+written in Standard C, but there are a few non-standard things it can cope
+with, allowing it to run on SunOS4 and other "close to standard" systems.
+
+On a non-Unix system you should just copy this file into config.h and change
+the definitions of HAVE_STRERROR and HAVE_MEMMOVE to 1. Unfortunately, because
+of the way autoconf works, these cannot be made the defaults. */
+
+/* Define to empty if the keyword does not work. */
+
+#undef const
+
+/* Define to `unsigned' if <stddef.h> doesn't define size_t. */
+
+#undef size_t
+
+/* The following two definitions are mainly for the benefit of SunOS4, which
+doesn't have the strerror() or memmove() functions that should be present in
+all Standard C libraries. The macros should normally be defined with the value
+1 for other systems, but unfortunately we can't make this the default because
+"configure" files generated by autoconf will only change 0 to 1; they won't
+change 1 to 0 if the functions are not found. */
+
+#define HAVE_STRERROR 1
+#define HAVE_MEMMOVE 1
+
+/* End */
diff --git a/srclib/pcre/dftables.dsp b/srclib/pcre/dftables.dsp
new file mode 100644
index 0000000000..c2955cbf08
--- /dev/null
+++ b/srclib/pcre/dftables.dsp
@@ -0,0 +1,160 @@
+# Microsoft Developer Studio Project File - Name="dftables" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=dftables - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "dftables.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "dftables.mak" CFG="dftables - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "dftables - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "dftables - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "dftables - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir "dftables_R"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib /nologo /subsystem:console /pdb:none /machine:I386
+
+!ELSEIF "$(CFG)" == "dftables - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir "dftables_D"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX- /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib /nologo /subsystem:console /pdb:none /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "dftables - Win32 Release"
+# Name "dftables - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\dftables.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hw"
+# Begin Source File
+
+SOURCE=.\config.hw
+
+!IF "$(CFG)" == "dftables - Win32 Release"
+
+# Begin Custom Build
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ echo Creating pcre config.h from config.hw
+ copy .\config.hw .\config.h
+
+# End Custom Build
+
+!ELSEIF "$(CFG)" == "dftables - Win32 Debug"
+
+# Begin Custom Build
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ echo Creating pcre config.h from config.hw
+ copy .\config.hw .\config.h
+
+# End Custom Build
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\pcre.hw
+
+!IF "$(CFG)" == "dftables - Win32 Release"
+
+# Begin Custom Build
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ echo Creating pcre.h from pcre.hw
+ copy .\pcre.hw .\pcre.h
+
+# End Custom Build
+
+!ELSEIF "$(CFG)" == "dftables - Win32 Debug"
+
+# Begin Custom Build
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ echo Creating pcre.h from pcre.hw
+ copy .\pcre.hw .\pcre.h
+
+# End Custom Build
+
+!ENDIF
+
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/srclib/pcre/pcre.dsp b/srclib/pcre/pcre.dsp
new file mode 100644
index 0000000000..bb6781503a
--- /dev/null
+++ b/srclib/pcre/pcre.dsp
@@ -0,0 +1,193 @@
+# Microsoft Developer Studio Project File - Name="pcre" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=pcre - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "pcre.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "pcre.mak" CFG="pcre - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "pcre - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "pcre - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+
+!IF "$(CFG)" == "pcre - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "LibR"
+# PROP Intermediate_Dir "LibR"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "pcre - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "LibD"
+# PROP Intermediate_Dir "LibD"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "pcre - Win32 Release"
+# Name "pcre - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "*.c"
+# Begin Source File
+
+SOURCE=.\dftables.exe
+
+!IF "$(CFG)" == "pcre - Win32 Release"
+
+# Begin Custom Build
+InputPath=.\dftables.exe
+
+".\chartables.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ .\dftables.exe >.\chartables.c
+ Echo Creating pcre chartables.c from dftables
+
+# End Custom Build
+
+!ELSEIF "$(CFG)" == "pcre - Win32 Debug"
+
+# Begin Custom Build
+InputPath=.\dftables.exe
+
+".\chartables.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ .\dftables.exe >.\chartables.c
+ Echo Creating pcre chartables.c from dftables
+
+# End Custom Build
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\get.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\maketables.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\pcre.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\study.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "*.h"
+# Begin Source File
+
+SOURCE=.\config.hw
+
+!IF "$(CFG)" == "pcre - Win32 Release"
+
+# Begin Custom Build
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ echo Creating pcre config.h from config.hw
+ copy .\config.hw .\config.h
+
+# End Custom Build
+
+!ELSEIF "$(CFG)" == "pcre - Win32 Debug"
+
+# Begin Custom Build
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ echo Creating pcre config.h from config.hw
+ copy .\config.hw .\config.h
+
+# End Custom Build
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\pcre.hw
+
+!IF "$(CFG)" == "pcre - Win32 Release"
+
+# Begin Custom Build
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ echo Creating pcre.h from pcre.hw
+ copy .\pcre.hw .\pcre.h
+
+# End Custom Build
+
+!ELSEIF "$(CFG)" == "pcre - Win32 Debug"
+
+# Begin Custom Build
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ echo Creating pcre.h from pcre.hw
+ copy .\pcre.hw .\pcre.h
+
+# End Custom Build
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\pcreposix.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/srclib/pcre/pcre.hw b/srclib/pcre/pcre.hw
new file mode 100644
index 0000000000..b66e2de953
--- /dev/null
+++ b/srclib/pcre/pcre.hw
@@ -0,0 +1,105 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* Copyright (c) 1997-2000 University of Cambridge */
+
+#ifndef _PCRE_H
+#define _PCRE_H
+
+#define PCRE_MAJOR 3
+#define PCRE_MINOR 1
+#define PCRE_DATE 09-Feb-2000
+
+/* Win32 uses DLL by default */
+
+#ifdef _WIN32
+# ifdef STATIC
+# define PCRE_DL_IMPORT
+# else
+# define PCRE_DL_IMPORT __declspec(dllimport)
+# endif
+#else
+# define PCRE_DL_IMPORT
+#endif
+
+/* Have to include stdlib.h in order to ensure that size_t is defined;
+it is needed here for malloc. */
+
+#include <sys/types.h>
+#include <stdlib.h>
+
+/* Allow for C++ users */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Options */
+
+#define PCRE_CASELESS 0x0001
+#define PCRE_MULTILINE 0x0002
+#define PCRE_DOTALL 0x0004
+#define PCRE_EXTENDED 0x0008
+#define PCRE_ANCHORED 0x0010
+#define PCRE_DOLLAR_ENDONLY 0x0020
+#define PCRE_EXTRA 0x0040
+#define PCRE_NOTBOL 0x0080
+#define PCRE_NOTEOL 0x0100
+#define PCRE_UNGREEDY 0x0200
+#define PCRE_NOTEMPTY 0x0400
+
+/* Exec-time and get-time error codes */
+
+#define PCRE_ERROR_NOMATCH (-1)
+#define PCRE_ERROR_NULL (-2)
+#define PCRE_ERROR_BADOPTION (-3)
+#define PCRE_ERROR_BADMAGIC (-4)
+#define PCRE_ERROR_UNKNOWN_NODE (-5)
+#define PCRE_ERROR_NOMEMORY (-6)
+#define PCRE_ERROR_NOSUBSTRING (-7)
+
+/* Request types for pcre_fullinfo() */
+
+#define PCRE_INFO_OPTIONS 0
+#define PCRE_INFO_SIZE 1
+#define PCRE_INFO_CAPTURECOUNT 2
+#define PCRE_INFO_BACKREFMAX 3
+#define PCRE_INFO_FIRSTCHAR 4
+#define PCRE_INFO_FIRSTTABLE 5
+#define PCRE_INFO_LASTLITERAL 6
+
+/* Types */
+
+typedef void pcre;
+typedef void pcre_extra;
+
+/* Store get and free functions. These can be set to alternative malloc/free
+functions if required. Some magic is required for Win32 DLL; it is null on
+other OS. */
+
+PCRE_DL_IMPORT extern void *(*pcre_malloc)(size_t);
+PCRE_DL_IMPORT extern void (*pcre_free)(void *);
+
+#undef PCRE_DL_IMPORT
+
+/* Functions */
+
+extern pcre *pcre_compile(const char *, int, const char **, int *,
+ const unsigned char *);
+extern int pcre_copy_substring(const char *, int *, int, int, char *, int);
+extern int pcre_exec(const pcre *, const pcre_extra *, const char *,
+ int, int, int, int *, int);
+extern int pcre_get_substring(const char *, int *, int, int, const char **);
+extern int pcre_get_substring_list(const char *, int *, int, const char ***);
+extern int pcre_info(const pcre *, int *, int *);
+extern int pcre_fullinfo(const pcre *, const pcre_extra *, int, void *);
+extern unsigned const char *pcre_maketables(void);
+extern pcre_extra *pcre_study(const pcre *, int, const char **);
+extern const char *pcre_version(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* End of pcre.h */
diff --git a/srclib/pcre/pcreposix.dsp b/srclib/pcre/pcreposix.dsp
new file mode 100644
index 0000000000..651e4a19d7
--- /dev/null
+++ b/srclib/pcre/pcreposix.dsp
@@ -0,0 +1,152 @@
+# Microsoft Developer Studio Project File - Name="pcreposix" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=pcreposix - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "pcreposix.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "pcreposix.mak" CFG="pcreposix - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "pcreposix - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "pcreposix - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+
+!IF "$(CFG)" == "pcreposix - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "LibR"
+# PROP Intermediate_Dir "LibR/posix"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "pcreposix - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "LibD"
+# PROP Intermediate_Dir "LibD/posix"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Z7 /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "pcreposix - Win32 Release"
+# Name "pcreposix - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "*.c"
+# Begin Source File
+
+SOURCE=.\pcreposix.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "*.h"
+# Begin Source File
+
+SOURCE=.\config.hw
+
+!IF "$(CFG)" == "pcreposix - Win32 Release"
+
+# Begin Custom Build
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ echo Creating pcre config.h from config.hw
+ copy .\config.hw .\config.h
+
+# End Custom Build
+
+!ELSEIF "$(CFG)" == "pcreposix - Win32 Debug"
+
+# Begin Custom Build
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ echo Creating pcre config.h from config.hw
+ copy .\config.hw .\config.h
+
+# End Custom Build
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\pcre.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\pcre.hw
+
+!IF "$(CFG)" == "pcreposix - Win32 Release"
+
+# Begin Custom Build
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ echo Creating pcre.h from pcre.hw
+ copy .\pcre.hw .\pcre.h
+
+# End Custom Build
+
+!ELSEIF "$(CFG)" == "pcreposix - Win32 Debug"
+
+# Begin Custom Build
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ echo Creating pcre.h from pcre.hw
+ copy .\pcre.hw .\pcre.h
+
+# End Custom Build
+
+!ENDIF
+
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/support/apachectl.in b/support/apachectl.in
new file mode 100644
index 0000000000..6f9bc6f7b5
--- /dev/null
+++ b/support/apachectl.in
@@ -0,0 +1,234 @@
+#!/bin/sh
+#
+# Copyright (c) 2000 The Apache Software Foundation.
+# See license at the end of this file.
+#
+# Apache control script designed to allow an easy command line interface
+# to controlling Apache. Written by Marc Slemko, 1997/08/23
+#
+# The exit codes returned are:
+# 0 - operation completed successfully
+# 1 -
+# 2 - usage error
+# 3 - httpd could not be started
+# 4 - httpd could not be stopped
+# 5 - httpd could not be started during a restart
+# 6 - httpd could not be restarted during a restart
+# 7 - httpd could not be restarted during a graceful restart
+# 8 - configuration syntax error
+#
+# When multiple arguments are given, only the error from the _last_
+# one is reported. Run "apachectl help" for usage info
+#
+#
+# |||||||||||||||||||| START CONFIGURATION SECTION ||||||||||||||||||||
+# -------------------- --------------------
+#
+# the path to your PID file
+PIDFILE=@prefix@/logs/httpd.pid
+#
+# the path to your httpd binary, including options if necessary
+HTTPD='@prefix@/bin/httpd'
+#
+# a command that outputs a formatted text version of the HTML at the
+# url given on the command line. Designed for lynx, however other
+# programs may work.
+LYNX="lynx -dump"
+#
+# the URL to your server's mod_status status page. If you do not
+# have one, then status and fullstatus will not work.
+STATUSURL="http://localhost:@PORT@/server-status"
+#
+# -------------------- --------------------
+# |||||||||||||||||||| END CONFIGURATION SECTION ||||||||||||||||||||
+
+ERROR=0
+ARGV="$@"
+if [ "x$ARGV" = "x" ] ; then
+ ARGS="help"
+fi
+
+for ARG in $@ $ARGS
+do
+ # check for pidfile
+ if [ -f $PIDFILE ] ; then
+ PID=`cat $PIDFILE`
+ if [ "x$PID" != "x" ] && kill -0 $PID 2>/dev/null ; then
+ STATUS="httpd (pid $PID) running"
+ RUNNING=1
+ else
+ STATUS="httpd (pid $PID?) not running"
+ RUNNING=0
+ fi
+ else
+ STATUS="httpd (no pid file) not running"
+ RUNNING=0
+ fi
+
+ case $ARG in
+ start)
+ if [ $RUNNING -eq 1 ]; then
+ echo "$0 $ARG: httpd (pid $PID) already running"
+ continue
+ fi
+ if $HTTPD ; then
+ echo "$0 $ARG: httpd started"
+ else
+ echo "$0 $ARG: httpd could not be started"
+ ERROR=3
+ fi
+ ;;
+ stop)
+ if [ $RUNNING -eq 0 ]; then
+ echo "$0 $ARG: $STATUS"
+ continue
+ fi
+ if kill $PID ; then
+ echo "$0 $ARG: httpd stopped"
+ else
+ echo "$0 $ARG: httpd could not be stopped"
+ ERROR=4
+ fi
+ ;;
+ restart)
+ if [ $RUNNING -eq 0 ]; then
+ echo "$0 $ARG: httpd not running, trying to start"
+ if $HTTPD ; then
+ echo "$0 $ARG: httpd started"
+ else
+ echo "$0 $ARG: httpd could not be started"
+ ERROR=5
+ fi
+ else
+ if $HTTPD -t >/dev/null 2>&1; then
+ if kill -HUP $PID ; then
+ echo "$0 $ARG: httpd restarted"
+ else
+ echo "$0 $ARG: httpd could not be restarted"
+ ERROR=6
+ fi
+ else
+ echo "$0 $ARG: configuration broken, ignoring restart"
+ echo "$0 $ARG: (run 'apachectl configtest' for details)"
+ ERROR=6
+ fi
+ fi
+ ;;
+ graceful)
+ if [ $RUNNING -eq 0 ]; then
+ echo "$0 $ARG: httpd not running, trying to start"
+ if $HTTPD ; then
+ echo "$0 $ARG: httpd started"
+ else
+ echo "$0 $ARG: httpd could not be started"
+ ERROR=5
+ fi
+ else
+ if $HTTPD -t >/dev/null 2>&1; then
+ if kill -WINCH $PID ; then
+ echo "$0 $ARG: httpd gracefully restarted"
+ else
+ echo "$0 $ARG: httpd could not be restarted"
+ ERROR=7
+ fi
+ else
+ echo "$0 $ARG: configuration broken, ignoring restart"
+ echo "$0 $ARG: (run 'apachectl configtest' for details)"
+ ERROR=7
+ fi
+ fi
+ ;;
+ status)
+ $LYNX $STATUSURL | awk ' /process$/ { print; exit } { print } '
+ ;;
+ fullstatus)
+ $LYNX $STATUSURL
+ ;;
+ configtest)
+ if $HTTPD -t; then
+ :
+ else
+ ERROR=8
+ fi
+ ;;
+ *)
+ echo "usage: $0 (start|stop|restart|fullstatus|status|graceful|configtest|help)"
+ cat <<EOF
+
+start - start httpd
+stop - stop httpd
+restart - restart httpd if running by sending a SIGHUP or start if
+ not running
+fullstatus - dump a full status screen; requires lynx and mod_status enabled
+status - dump a short status screen; requires lynx and mod_status enabled
+graceful - do a graceful restart by sending a SIGWINCH or start if not running
+configtest - do a configuration syntax test
+help - this screen
+
+EOF
+ ERROR=2
+ ;;
+
+ esac
+
+done
+
+exit $ERROR
+
+# ====================================================================
+# The Apache Software License, Version 1.1
+#
+# Copyright (c) 2000 The Apache Software Foundation. All rights
+# reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. The end-user documentation included with the redistribution,
+# if any, must include the following acknowledgment:
+# "This product includes software developed by the
+# Apache Software Foundation (http://www.apache.org/)."
+# Alternately, this acknowledgment may appear in the software itself,
+# if and wherever such third-party acknowledgments normally appear.
+#
+# 4. The names "Apache" and "Apache Software Foundation" must
+# not be used to endorse or promote products derived from this
+# software without prior written permission. For written
+# permission, please contact apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache",
+# nor may "Apache" appear in their name, without prior written
+# permission of the Apache Software Foundation.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Software Foundation. For more
+# information on the Apache Software Foundation, please see
+# <http://www.apache.org/>.
+#
+# Portions of this software are based upon public domain software
+# originally written at the National Center for Supercomputing Applications,
+# University of Illinois, Urbana-Champaign.
+#
diff --git a/support/dbmmanage.in b/support/dbmmanage.in
new file mode 100644
index 0000000000..8c75637e1d
--- /dev/null
+++ b/support/dbmmanage.in
@@ -0,0 +1,350 @@
+#!@perlbin@
+# ====================================================================
+# The Apache Software License, Version 1.1
+#
+# Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+# reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. The end-user documentation included with the redistribution,
+# if any, must include the following acknowledgment:
+# "This product includes software developed by the
+# Apache Software Foundation (http://www.apache.org/)."
+# Alternately, this acknowledgment may appear in the software itself,
+# if and wherever such third-party acknowledgments normally appear.
+#
+# 4. The names "Apache" and "Apache Software Foundation" must
+# not be used to endorse or promote products derived from this
+# software without prior written permission. For written
+# permission, please contact apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache",
+# nor may "Apache" appear in their name, without prior written
+# permission of the Apache Software Foundation.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Software Foundation. For more
+# information on the Apache Software Foundation, please see
+# <http://www.apache.org/>.
+#
+
+#for more functionality see the HTTPD::UserAdmin module:
+# http://www.perl.com/CPAN/modules/by-module/HTTPD/HTTPD-Tools-x.xx.tar.gz
+#
+# usage: dbmmanage <DBMfile> <command> <user> <password> <groups> <comment>
+
+package dbmmanage;
+# -ldb -lndbm -lgdbm -lsdbm
+BEGIN { @AnyDBM_File::ISA = qw(DB_File NDBM_File GDBM_File SDBM_File) }
+use strict;
+use Fcntl;
+use AnyDBM_File ();
+
+sub usage {
+ my $cmds = join "|", sort keys %dbmc::;
+ die <<SYNTAX;
+Usage: dbmmanage [enc] dbname command [username [pw [group[,group] [comment]]]]
+
+ where enc is -d for crypt encryption (default except on Win32, Netware)
+ -m for MD5 encryption (default on Win32, Netware)
+ -s for SHA1 encryption
+ -p for plaintext
+
+ command is one of: $cmds
+
+ pw of . for update command retains the old password
+ pw of - (or blank) for update command prompts for the password
+
+ groups or comment of . (or blank) for update command retains old values
+ groups or comment of - for update command clears the existing value
+ groups or comment of - for add and adduser commands is the empty value
+SYNTAX
+}
+
+sub need_sha1_crypt {
+ if (!eval ('require "Digest/SHA1.pm";')) {
+ print STDERR <<SHAERR;
+dbmmanage SHA1 passwords require the interface or the module Digest::SHA1
+available from CPAN:
+
+ http://www.cpan.org/modules/by-module/Digest/Digest-MD5-2.12.tar.gz
+
+Please install Digest::SHA1 and try again, or use a different crypt option:
+
+SHAERR
+ usage();
+ }
+}
+
+sub need_md5_crypt {
+ if (!eval ('require "Crypt/PasswdMD5.pm";')) {
+ print STDERR <<MD5ERR;
+dbmmanage MD5 passwords require the module Crypt::PasswdMD5 available from CPAN
+
+ http://www.cpan.org/modules/by-module/Crypt/Crypt-PasswdMD5-1.1.tar.gz
+
+Please install Crypt::PasswdMD5 and try again, or use a different crypt option:
+
+MD5ERR
+ usage();
+ }
+}
+
+# if your osname is in $newstyle_salt, then use new style salt (starts with '_' and contains
+# four bytes of iteration count and four bytes of salt). Otherwise, just use
+# the traditional two-byte salt.
+# see the man page on your system to decide if you have a newer crypt() lib.
+# I believe that 4.4BSD derived systems do (at least BSD/OS 2.0 does).
+# The new style crypt() allows up to 20 characters of the password to be
+# significant rather than only 8.
+#
+my $newstyle_salt_platforms = join '|', qw{bsdos}; #others?
+my $newstyle_salt = $^O =~ /(?:$newstyle_salt_platforms)/;
+
+# Some platforms just can't crypt() for Apache
+#
+my $crypt_not_supported_platforms = join '|', qw{MSWin32 NetWare}; #others?
+my $crypt_not_supported = $^O =~ /(?:$crypt_not_supported_platforms)/;
+
+my $crypt_method = "crypt";
+
+if ($crypt_not_supported) {
+ $crypt_method = "md5";
+}
+
+# Some platforms won't jump through our favorite hoops
+#
+my $not_unix_platforms = join '|', qw{MSWin32 NetWare}; #others?
+my $not_unix = $^O =~ /(?:$not_unix_platforms)/;
+
+if ($crypt_not_supported) {
+ $crypt_method = "md5";
+}
+
+if (@ARGV[0] eq "-d") {
+ shift @ARGV;
+ if ($crypt_not_supported) {
+ print STDERR
+ "Warning: Apache/$^O does not support crypt()ed passwords!\n\n";
+ }
+ $crypt_method = "crypt";
+}
+
+if (@ARGV[0] eq "-m") {
+ shift @ARGV;
+ $crypt_method = "md5";
+}
+
+if (@ARGV[0] eq "-p") {
+ shift @ARGV;
+ if (!$crypt_not_supported) {
+ print STDERR
+ "Warning: Apache/$^O does not support plaintext passwords!\n\n";
+ }
+ $crypt_method = "plain";
+}
+
+if (@ARGV[0] eq "-s") {
+ shift @ARGV;
+ need_sha1_crypt();
+ $crypt_method = "sha1";
+}
+
+if ($crypt_method eq "md5") {
+ need_md5_crypt();
+}
+
+my($file,$command,$key,$crypted_pwd,$groups,$comment) = @ARGV;
+
+usage() unless $file and $command and defined &{$dbmc::{$command}};
+
+# remove extension if any
+my $chop = join '|', qw{db.? pag dir};
+$file =~ s/\.($chop)$//;
+
+my $is_update = $command eq "update";
+my %DB = ();
+my @range = ();
+my($mode, $flags) = $command =~
+ /^(?:view|check)$/ ? (0644, O_RDONLY) : (0644, O_RDWR|O_CREAT);
+
+tie (%DB, "AnyDBM_File", $file, $flags, $mode) || die "Can't tie $file: $!";
+dbmc->$command();
+untie %DB;
+
+
+my $x;
+sub genseed {
+ my $psf;
+ if ($not_unix) {
+ srand (time ^ $$ or time ^ ($$ + ($$ << 15)));
+ }
+ else {
+ for (qw(-xlwwa -le)) {
+ `ps $_ 2>/dev/null`;
+ $psf = $_, last unless $?;
+ }
+ srand (time ^ $$ ^ unpack("%L*", `ps $psf | gzip -f`));
+ }
+ @range = (qw(. /), '0'..'9','a'..'z','A'..'Z');
+ $x = int scalar @range;
+}
+
+sub randchar {
+ join '', map $range[rand $x], 1..shift||1;
+}
+
+sub saltpw_crypt {
+ genseed() unless @range;
+ return $newstyle_salt ?
+ join '', "_", randchar, "a..", randchar(4) :
+ randchar(2);
+}
+
+sub cryptpw_crypt {
+ my ($pw, $salt) = @_;
+ $salt = saltpw_crypt unless $salt;
+ crypt $pw, $salt;
+}
+
+sub saltpw_md5 {
+ genseed() unless @range;
+ randchar(8);
+}
+
+sub cryptpw_md5 {
+ my($pw, $salt) = @_;
+ $salt = saltpw_md5 unless $salt;
+ Crypt::PasswdMD5::apache_md5_crypt($pw, $salt);
+}
+
+sub cryptpw_sha1 {
+ my($pw, $salt) = @_;
+ '{SHA}' . Digest::SHA1::sha1_base64($pw) . "=";
+}
+
+sub cryptpw {
+ if ($crypt_method eq "md5") {
+ return cryptpw_md5(@_);
+ } elsif ($crypt_method eq "sha1") {
+ return cryptpw_sha1(@_);
+ } elsif ($crypt_method eq "crypt") {
+ return cryptpw_crypt(@_);
+ }
+ @_[0]; # otherwise return plaintext
+}
+
+sub getpass {
+ my $prompt = shift || "Enter password:";
+
+ unless($not_unix) {
+ open STDIN, "/dev/tty" or warn "couldn't open /dev/tty $!\n";
+ system "stty -echo;";
+ }
+
+ my($c,$pwd);
+ print STDERR $prompt;
+ while (($c = getc(STDIN)) ne '' and $c ne "\n" and $c ne "\r") {
+ $pwd .= $c;
+ }
+
+ system "stty echo" unless $not_unix;
+ print STDERR "\n";
+ die "Can't use empty password!\n" unless length $pwd;
+ return $pwd;
+}
+
+sub dbmc::update {
+ die "Sorry, user `$key' doesn't exist!\n" unless $DB{$key};
+ $crypted_pwd = (split /:/, $DB{$key}, 3)[0] if $crypted_pwd eq '.';
+ $groups = (split /:/, $DB{$key}, 3)[1] if !$groups || $groups eq '.';
+ $comment = (split /:/, $DB{$key}, 3)[2] if !$comment || $comment eq '.';
+ if (!$crypted_pwd || $crypted_pwd eq '-') {
+ dbmc->adduser;
+ }
+ else {
+ dbmc->add;
+ }
+}
+
+sub dbmc::add {
+ die "Can't use empty password!\n" unless $crypted_pwd;
+ unless($is_update) {
+ die "Sorry, user `$key' already exists!\n" if $DB{$key};
+ }
+ $groups = '' if $groups eq '-';
+ $comment = '' if $comment eq '-';
+ $groups .= ":" . $comment if $comment;
+ $crypted_pwd .= ":" . $groups if $groups;
+ $DB{$key} = $crypted_pwd;
+ my $action = $is_update ? "updated" : "added";
+ print "User $key $action with password encrypted to $DB{$key} using $crypt_method\n";
+}
+
+sub dbmc::adduser {
+ my $value = getpass "New password:";
+ die "They don't match, sorry.\n" unless getpass("Re-type new password:") eq $value;
+ $crypted_pwd = cryptpw $value;
+ dbmc->add;
+}
+
+sub dbmc::delete {
+ die "Sorry, user `$key' doesn't exist!\n" unless $DB{$key};
+ delete $DB{$key}, print "`$key' deleted\n";
+}
+
+sub dbmc::view {
+ print $key ? "$key:$DB{$key}\n" : map { "$_:$DB{$_}\n" if $DB{$_} } keys %DB;
+}
+
+sub dbmc::check {
+ die "Sorry, user `$key' doesn't exist!\n" unless $DB{$key};
+ my $chkpass = (split /:/, $DB{$key}, 3)[0];
+ my $testpass = getpass();
+ if (substr($chkpass, 0, 6) eq '$apr1$') {
+ need_md5_crypt;
+ $crypt_method = "md5";
+ } elsif (substr($chkpass, 0, 5) eq '{SHA}') {
+ need_sha1_crypt;
+ $crypt_method = "sha1";
+ } elsif (length($chkpass) == 13 && $chkpass ne $testpass) {
+ $crypt_method = "crypt";
+ } else {
+ $crypt_method = "plain";
+ }
+ print $crypt_method . (cryptpw($testpass, $chkpass) eq $chkpass
+ ? " password ok\n" : " password mismatch\n");
+}
+
+sub dbmc::import {
+ while(defined($_ = <STDIN>) and chomp) {
+ ($key,$crypted_pwd,$groups,$comment) = split /:/, $_, 4;
+ dbmc->add;
+ }
+}
+
diff --git a/support/log_server_status.in b/support/log_server_status.in
new file mode 100644
index 0000000000..f301604b76
--- /dev/null
+++ b/support/log_server_status.in
@@ -0,0 +1,114 @@
+#!@perlbin@
+# ====================================================================
+# The Apache Software License, Version 1.1
+#
+# Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+# reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. The end-user documentation included with the redistribution,
+# if any, must include the following acknowledgment:
+# "This product includes software developed by the
+# Apache Software Foundation (http://www.apache.org/)."
+# Alternately, this acknowledgment may appear in the software itself,
+# if and wherever such third-party acknowledgments normally appear.
+#
+# 4. The names "Apache" and "Apache Software Foundation" must
+# not be used to endorse or promote products derived from this
+# software without prior written permission. For written
+# permission, please contact apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache",
+# nor may "Apache" appear in their name, without prior written
+# permission of the Apache Software Foundation.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Software Foundation. For more
+# information on the Apache Software Foundation, please see
+# <http://www.apache.org/>.
+#
+# Log Server Status
+# Mark J Cox, UK Web Ltd 1996, mark@ukweb.com
+#
+# This script is designed to be run at a frequent interval by something
+# like cron. It connects to the server and downloads the status
+# information. It reformats the information to a single line and logs
+# it to a file. Make sure the directory $wherelog is writable by the
+# user who runs this script.
+#
+require 'sys/socket.ph';
+
+$wherelog = "/var/log/graph/"; # Logs will be like "/var/log/graph/19960312"
+$server = "localhost"; # Name of server, could be "www.foo.com"
+$port = "80"; # Port on server
+$request = "/status/?auto"; # Request to send
+
+sub tcp_connect
+{
+ local($host,$port) =@_;
+ $sockaddr='S n a4 x8';
+ chop($hostname=`hostname`);
+ $port=(getservbyname($port, 'tcp'))[2] unless $port =~ /^\d+$/;
+ $me=pack($sockaddr,&AF_INET,0,(gethostbyname($hostname))[4]);
+ $them=pack($sockaddr,&AF_INET,$port,(gethostbyname($host))[4]);
+ socket(S,&PF_INET,&SOCK_STREAM,(getprotobyname('tcp'))[2]) ||
+ die "socket: $!";
+ bind(S,$me) || return "bind: $!";
+ connect(S,$them) || return "connect: $!";
+ select(S);
+ $| = 1;
+ select(stdout);
+ return "";
+}
+
+### Main
+
+{
+ $year=`date +%y`;
+ chomp($year);
+ $year += ($year < 70) ? 2000 : 1900;
+ $date = $year . `date +%m%d:%H%M%S`;
+ chomp($date);
+ ($day,$time)=split(/:/,$date);
+ $res=&tcp_connect($server,$port);
+ open(OUT,">>$wherelog$day");
+ if ($res) {
+ print OUT "$time:-1:-1:-1:-1:$res\n";
+ exit 1;
+ }
+ print S "GET $request\n";
+ while (<S>) {
+ $requests=$1 if ( m|^BusyServers:\ (\S+)|);
+ $idle=$1 if ( m|^IdleServers:\ (\S+)|);
+ $number=$1 if ( m|sses:\ (\S+)|);
+ $cpu=$1 if (m|^CPULoad:\ (\S+)|);
+ }
+ print OUT "$time:$requests:$idle:$number:$cpu\n";
+}
+
+
diff --git a/support/logresolve.pl.in b/support/logresolve.pl.in
new file mode 100644
index 0000000000..c2142f161d
--- /dev/null
+++ b/support/logresolve.pl.in
@@ -0,0 +1,261 @@
+#!@perlbin@
+# ====================================================================
+# The Apache Software License, Version 1.1
+#
+# Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+# reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. The end-user documentation included with the redistribution,
+# if any, must include the following acknowledgment:
+# "This product includes software developed by the
+# Apache Software Foundation (http://www.apache.org/)."
+# Alternately, this acknowledgment may appear in the software itself,
+# if and wherever such third-party acknowledgments normally appear.
+#
+# 4. The names "Apache" and "Apache Software Foundation" must
+# not be used to endorse or promote products derived from this
+# software without prior written permission. For written
+# permission, please contact apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache",
+# nor may "Apache" appear in their name, without prior written
+# permission of the Apache Software Foundation.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Software Foundation. For more
+# information on the Apache Software Foundation, please see
+# <http://www.apache.org/>.
+#
+# logresolve.pl
+#
+# v 1.2 by robh @ imdb.com
+#
+# usage: logresolve.pl <infile >outfile
+#
+# input = Apache/NCSA/.. logfile with IP numbers at start of lines
+# output = same logfile with IP addresses resolved to hostnames where
+# name lookups succeeded.
+#
+# this differs from the C based 'logresolve' in that this script
+# spawns a number ($CHILDREN) of subprocesses to resolve addresses
+# concurrently and sets a short timeout ($TIMEOUT) for each lookup in
+# order to keep things moving quickly.
+#
+# the parent process handles caching of IP->hostnames using a Perl hash
+# it also avoids sending the same IP to multiple child processes to be
+# resolved multiple times concurrently.
+#
+# Depending on the settings of $CHILDREN and $TIMEOUT you should see
+# significant reductions in the overall time taken to resolve your
+# logfiles. With $CHILDREN=40 and $TIMEOUT=5 I've seen 200,000 - 300,000
+# logfile lines processed per hour compared to ~45,000 per hour
+# with 'logresolve'.
+#
+# I haven't yet seen any noticable reduction in the percentage of IPs
+# that fail to get resolved. Your mileage will no doubt vary. 5s is long
+# enough to wait IMO.
+#
+# Known to work with FreeBSD 2.2
+# Known to have problems with Solaris
+#
+# 980417 - use 'sockaddr_un' for bind/connect to make the script work
+# with linux. Fix from Luuk de Boer <luuk_de_boer@pi.net>
+
+require 5.004;
+
+$|=1;
+
+use FileHandle;
+use Socket;
+
+use strict;
+no strict 'refs';
+
+use vars qw($PROTOCOL);
+$PROTOCOL = 0;
+
+my $CHILDREN = 40;
+my $TIMEOUT = 5;
+
+my $filename;
+my %hash = ();
+my $parent = $$;
+
+my @children = ();
+for (my $child = 1; $child <=$CHILDREN; $child++) {
+ my $f = fork();
+ if (!$f) {
+ $filename = "./.socket.$parent.$child";
+ if (-e $filename) { unlink($filename) || warn "$filename .. $!\n";}
+ &child($child);
+ exit(0);
+ }
+ push(@children, $f);
+}
+
+&parent;
+&cleanup;
+
+## remove all temporary files before shutting down
+sub cleanup {
+ # die kiddies, die
+ kill(15, @children);
+ for (my $child = 1; $child <=$CHILDREN; $child++) {
+ if (-e "./.socket.$parent.$child") {
+ unlink("./.socket.$parent.$child")
+ || warn ".socket.$parent.$child $!";
+ }
+ }
+}
+
+sub parent {
+ # Trap some possible signals to trigger temp file cleanup
+ $SIG{'KILL'} = $SIG{'INT'} = $SIG{'PIPE'} = \&cleanup;
+
+ my %CHILDSOCK;
+ my $filename;
+
+ ## fork child processes. Each child will create a socket connection
+ ## to this parent and use an unique temp filename to do so.
+ for (my $child = 1; $child <=$CHILDREN; $child++) {
+ $CHILDSOCK{$child}= FileHandle->new;
+
+ if (!socket($CHILDSOCK{$child}, AF_UNIX, SOCK_STREAM, $PROTOCOL)) {
+ warn "parent socket to child failed $!";
+ }
+ $filename = "./.socket.$parent.$child";
+ my $response;
+ do {
+ $response = connect($CHILDSOCK{$child}, sockaddr_un($filename));
+ if ($response != 1) {
+ sleep(1);
+ }
+ } while ($response != 1);
+ $CHILDSOCK{$child}->autoflush;
+ }
+ ## All child processes should now be ready or at worst warming up
+
+ my (@buffer, $child, $ip, $rest, $hostname, $response);
+ ## read the logfile lines from STDIN
+ while(<STDIN>) {
+ @buffer = (); # empty the logfile line buffer array.
+ $child = 1; # children are numbered 1..N, start with #1
+
+ # while we have a child to talk to and data to give it..
+ do {
+ push(@buffer, $_); # buffer the line
+ ($ip, $rest) = split(/ /, $_, 2); # separate IP form rest
+
+ unless ($hash{$ip}) { # resolve if unseen IP
+ $CHILDSOCK{$child}->print("$ip\n"); # pass IP to next child
+ $hash{$ip} = $ip; # don't look it up again.
+ $child++;
+ }
+ } while (($child < ($CHILDREN-1)) and ($_ = <STDIN>));
+
+ ## now poll each child for a response
+ while (--$child > 0) {
+ $response = $CHILDSOCK{$child}->getline;
+ chomp($response);
+ # child sends us back both the IP and HOSTNAME, no need for us
+ # to remember what child received any given IP, and no worries
+ # what order we talk to the children
+ ($ip, $hostname) = split(/\|/, $response, 2);
+ $hash{$ip} = $hostname;
+ }
+
+ # resolve all the logfiles lines held in the log buffer array..
+ for (my $line = 0; $line <=$#buffer; $line++) {
+ # get next buffered line
+ ($ip, $rest) = split(/ /, $buffer[$line], 2);
+ # separate IP from rest and replace with cached hostname
+ printf STDOUT ("%s %s", $hash{$ip}, $rest);
+ }
+ }
+}
+
+########################################
+
+sub child {
+ # arg = numeric ID - how the parent refers to me
+ my $me = shift;
+
+ # add trap for alarm signals.
+ $SIG{'ALRM'} = sub { die "alarmed"; };
+
+ # create a socket to communicate with parent
+ socket(INBOUND, AF_UNIX, SOCK_STREAM, $PROTOCOL)
+ || die "Error with Socket: !$\n";
+ $filename = "./.socket.$parent.$me";
+ bind(INBOUND, sockaddr_un($filename))
+ || die "Error Binding $filename: $!\n";
+ listen(INBOUND, 5) || die "Error Listening: $!\n";
+
+ my ($ip, $send_back);
+ my $talk = FileHandle->new;
+
+ # accept a connection from the parent process. We only ever have
+ # have one connection where we exchange 1 line of info with the
+ # parent.. 1 line in (IP address), 1 line out (IP + hostname).
+ accept($talk, INBOUND) || die "Error Accepting: $!\n";
+ # disable I/O buffering just in case
+ $talk->autoflush;
+ # while the parent keeps sending data, we keep responding..
+ while(($ip = $talk->getline)) {
+ chomp($ip);
+ # resolve the IP if time permits and send back what we found..
+ $send_back = sprintf("%s|%s", $ip, &nslookup($ip));
+ $talk->print($send_back."\n");
+ }
+}
+
+# perform a time restricted hostname lookup.
+sub nslookup {
+ # get the IP as an arg
+ my $ip = shift;
+ my $hostname = undef;
+
+ # do the hostname lookup inside an eval. The eval will use the
+ # already configured SIGnal handler and drop out of the {} block
+ # regardless of whether the alarm occured or not.
+ eval {
+ alarm($TIMEOUT);
+ $hostname = gethostbyaddr(gethostbyname($ip), AF_INET);
+ alarm(0);
+ };
+ if ($@ =~ /alarm/) {
+ # useful for debugging perhaps..
+ # print "alarming, isn't it? ($ip)";
+ }
+
+ # return the hostname or the IP address itself if there is no hostname
+ $hostname ne "" ? $hostname : $ip;
+}
+
+
diff --git a/support/phf_abuse_log.cgi.in b/support/phf_abuse_log.cgi.in
new file mode 100644
index 0000000000..723f553bac
--- /dev/null
+++ b/support/phf_abuse_log.cgi.in
@@ -0,0 +1,22 @@
+#!@perlbin@
+
+# This script is used to detect people trying to abuse the security hole which
+# existed in A CGI script direstributed with Apache 1.0.3 and earlier versions.
+# You can redirect them to here using the "<Location /cgi-bin/phf*>" suggestion
+# in httpd.conf.
+#
+# The format logged to is
+# "[date] remote_addr remote_host [date] referrer user_agent".
+
+$LOG = "/var/log/phf_log";
+
+require "ctime.pl";
+$when = &ctime(time);
+$when =~ s/\n//go;
+$ENV{HTTP_USER_AGENT} .= " via $ENV{HTTP_VIA}" if($ENV{HTTP_VIA});
+
+open(LOG, ">>$LOG") || die "boo hoo, phf_log $!";
+print LOG "[$when] $ENV{REMOTE_ADDR} $ENV{REMOTE_HOST} $ENV{$HTTP_REFERER} $ENV{HTTP_USER_AGENT}\n";
+close(LOG);
+
+print "Content-type: text/html\r\n\r\n<BLINK>Smile, you're on Candid Camera.</BLINK>\n";
diff --git a/support/split-logfile.in b/support/split-logfile.in
new file mode 100644
index 0000000000..66de2ebfdd
--- /dev/null
+++ b/support/split-logfile.in
@@ -0,0 +1,98 @@
+#!@perlbin@
+# ====================================================================
+# The Apache Software License, Version 1.1
+#
+# Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+# reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. The end-user documentation included with the redistribution,
+# if any, must include the following acknowledgment:
+# "This product includes software developed by the
+# Apache Software Foundation (http://www.apache.org/)."
+# Alternately, this acknowledgment may appear in the software itself,
+# if and wherever such third-party acknowledgments normally appear.
+#
+# 4. The names "Apache" and "Apache Software Foundation" must
+# not be used to endorse or promote products derived from this
+# software without prior written permission. For written
+# permission, please contact apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache",
+# nor may "Apache" appear in their name, without prior written
+# permission of the Apache Software Foundation.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Software Foundation. For more
+# information on the Apache Software Foundation, please see
+# <http://www.apache.org/>.
+
+# This script will take a combined Web server access
+# log file and break its contents into separate files.
+# It assumes that the first field of each line is the
+# virtual host identity (put there by "%v"), and that
+# the logfiles should be named that+".log" in the current
+# directory.
+#
+# The combined log file is read from stdin. Records read
+# will be appended to any existing log files.
+#
+%is_open = ();
+
+while ($log_line = <STDIN>) {
+ #
+ # Get the first token from the log record; it's the
+ # identity of the virtual host to which the record
+ # applies.
+ #
+ ($vhost) = split (/\s/, $log_line);
+ #
+ # Normalize the virtual host name to all lowercase.
+ # If it's blank, the request was handled by the default
+ # server, so supply a default name. This shouldn't
+ # happen, but caution rocks.
+ #
+ $vhost = lc ($vhost) or "access";
+ #
+ # If the log file for this virtual host isn't opened
+ # yet, do it now.
+ #
+ if (! $is_open{$vhost}) {
+ open $vhost, ">>${vhost}.log"
+ or die ("Can't open ${vhost}.log");
+ $is_open{$vhost} = 1;
+ }
+ #
+ # Strip off the first token (which may be null in the
+ # case of the default server), and write the edited
+ # record to the current log file.
+ #
+ $log_line =~ s/^\S*\s+//;
+ printf $vhost "%s", $log_line;
+}
+exit 0;
diff --git a/test/Makefile.in b/test/Makefile.in
new file mode 100644
index 0000000000..e7770f7fbb
--- /dev/null
+++ b/test/Makefile.in
@@ -0,0 +1,15 @@
+
+PROGRAMS = dbu
+targets = $(PROGRAMS)
+
+PROGRAM_LDADD = $(EXTRA_LDFLAGS) $(PROGRAM_DEPENDENCIES) $(EXTRA_LIBS)
+PROGRAM_DEPENDENCIES = ../ap/libap.la \
+ ../lib/aputil/libaputil.la \
+ ../lib/sdbm/libsdbm.la \
+ ../lib/apr/$(LIBPRE)apr.a
+
+include $(top_srcdir)/build/rules.mk
+
+dbu_OBJECTS = dbu.lo
+dbu: $(dbu_OBJECTS)
+ $(LINK) $(dbu_OBJECTS) $(PROGRAM_LDADD)