Publish mongoose src and tests

CL: Mongoose Web Server: Publish sources and tests

Resolves https://github.com/cesanta/mongoose/issues/745

PUBLISHED_FROM=7ecd7a3c518cfa614a6ba0838678dcb91b75a8c0
This commit is contained in:
Dmitry Frank authored and Cesanta Bot committed 2018-02-02 15:49:55 +00:00
1 parent 0ab1c7ef76
commit 8742fac5d8
94 files changed
+24967

No files matched your search

+104
View File
@@ -0,0 +1,104 @@
# vim: ts=8 noet sw=8 ai cin smarttab
PROG = unit_test
REPO_ROOT ?= ../../
SRC_DIR = ../src
TEST_SOURCES = unit_test.c $(REPO_ROOT)/common/test_util.c $(REPO_ROOT)/tuna/dispatcher.c
AMALGAMATED_SOURCES = ../mongoose.c
KRYPTON_PATH = $(REPO_ROOT)/krypton
# or Krypton, or mbedTLS
SSL ?= OpenSSL
COMMON_FEATURE_FLAGS = \
-DKR_MODULE_LINES \
-DMG_ENABLE_COAP \
-DMG_ENABLE_DNS_SERVER \
-DMG_ENABLE_HTTP_SSI_EXEC \
-DMG_ENABLE_HTTP_WEBDAV \
-DMG_ENABLE_MQTT_BROKER \
-DMG_ENABLE_SOCKS \
-DMG_ENABLE_SYNC_RESOLVER \
-DMG_ENABLE_THREADS \
-DMG_DISABLE_DAV_AUTH \
-DMG_INTERNAL="" \
-DMG_MODULE_LINES \
-DMG_CALLOC=test_calloc \
-DMG_MALLOC=test_malloc \
-DMG_ENABLE_POLL_UNTIL \
-DMG_ENABLE_SNTP -DMG_SNTP_NO_DELAY_CORRECTION \
-DMG_ENABLE_HTTP_STREAMING_MULTIPART
UNIX_FEATURE_FLAGS=-DMG_ENABLE_IPV6 -DMG_ENABLE_SSL
CFLAGS = -W -Wall -Wundef -Werror -g -O0 -Wno-multichar -D__USE_MISC \
$(COMMON_FEATURE_FLAGS) $(UNIX_FEATURE_FLAGS) \
-I$(REPO_ROOT) -include unit_test.h -pthread $(CFLAGS_EXTRA)
LDFLAGS = -lm
ifeq "$(SSL)" "OpenSSL"
# OpenSSL is the default
LDFLAGS += -lcrypto -lssl
endif
ifeq "$(SSL)" "Krypton"
CFLAGS += $(KRYPTON_PATH)/krypton.c -I$(KRYPTON_PATH)
endif
ifeq "$(SSL)" "mbedTLS"
CFLAGS += -DMG_SSL_IF=MG_SSL_IF_MBEDTLS -DMG_SSL_MBED_DUMMY_RANDOM
LDFLAGS += -lmbedcrypto -lmbedtls -lmbedx509
endif
include test.mk
include $(SRC_DIR)/modules.mk
# http://crossgcc.rts-software.org/doku.php?id=compiling_for_win32
MINGW_GCC=/usr/local/gcc-4.8.0-qt-4.8.4-for-mingw32/win32-gcc/bin/i586-mingw32-gcc
.PHONY: $(PROG).exe $(PROG)_mingw.exe
$(PROG)_mingw.exe: Makefile
$(MINGW_GCC) $(AMALGAMATED_SOURCES) -o $(PROG)_mingw.exe -W -Wall -Werror
$(PROG).exe: $(AMALGAMATED_SOURCES) data/cgi/index.cgi.exe
ifndef VC6_DIR
$(error Please set VC6_DIR)
endif
Include=$(VC6_DIR)/include Lib=$(VC6_DIR)/lib \
wine $(VC6_DIR)/bin/cl \
$(TEST_SOURCES) $(AMALGAMATED_SOURCES) \
/MD /I. /I.. /Zi \
/FIunit_test.h \
/I$(REPO_ROOT) \
$(COMMON_FEATURE_FLAGS) $(CFLAGS_EXTRA) \
-DMG_ENABLE_SSL $(KRYPTON_PATH)/krypton.c -I$(KRYPTON_PATH) \
kernel32.lib advapi32.lib \
/Fe$@
cp data/cgi/index.cgi.exe data/cgi/index.cgi
win: $(PROG).exe
wine $(PROG).exe $(TEST_FILTER)
# CGI test program.
$(PROG)-%: data/cgi/index.cgi
data/cgi/index.cgi: index_cgi.c ccgi-1.2/ccgi.c
@echo -e "CC\tindex.cgi"
@mkdir -p data/cgi
@$(CC) -Wall -Werror -o $@ $^ -Iccgi-1.2
data/cgi/index.cgi.exe: index_cgi.c ccgi-1.2/ccgi.c
@echo -e "CC\tindex.cgi.exe"
@mkdir -p data/cgi
Include=$(VC6_DIR)/include Lib=$(VC6_DIR)/lib \
wine $(VC6_DIR)/bin/cl \
/MD /Iccgi-1.2 /Zi index_cgi.c ccgi-1.2/ccgi.c /Fe$@
clean: clean_index_cgi
clean_index_cgi:
@echo -e "CLEAN\tindex.cgi"
@rm -f data/cgi/index.cgi*
# Interactive:
# docker run -v $(CURDIR)/../..:/cesanta -t -i --entrypoint=/bin/bash cesanta/mongoose_test
docker:
docker run --rm -v $(CURDIR)/../..:/cesanta cesanta/mongoose_test
+49
View File
@@ -0,0 +1,49 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAv3/TSSi5hZDwMKG43eqe+GzR1lRMXVYt9I1Mr987v1DT99xR
Dcpfo/3aj6B/V/G67oPz+zbVZN/ZPvvA1Z82T7ixcBFsGIXgEWzxUm1UCUf51ftl
MlOaf24cdyegi0y8hRdkWLoC7w0vuMfrgR6cmpbI2LSDSMaXXX2qDoofQsFUYaJN
Nn3uqRK0ixs/jzbzbAT9q2BWYwySUX4VEgADpmi0FyANDjEhmdktxQW9l6IGGzF8
M9mA053hIgZwo+9qf9X3nfNUTWMvisMQtxm0mRYgvD53Oix08VLs6bodNTVOLQoc
H0uH3CTs+H3Z0CkcZaAJe/kwCLFhls9ee3M0nQIDAQABAoIBAQCsADPWUi3QOg6C
n79cE5AVsigHSlAMxYshTIjErs0LWZ4J0mk66bpdoXTd7Fp0szojYYGS8f1ZTXXj
jFv3g7lUgZ9d+UgN/rDy9dcLIgeJDozoFZUfTthF/LC0lXMtqw7ou8n1p51a+Y0T
ev2cS9J9R1G+0uPYSgdKgcRsqsLJQS4fu5CAk9d0aeTTl009uxcn9yfTUjwOaR5J
PuNmunAEvhE/DGSkt5oNXo7t8Q2L3mYSM0MwKdDFqoQdZAV6TMTv22Mjb6SxOOnJ
r5gNK2BmM6oNPWvzY0PoI0LcLgFNDWIMqIq4mg73MdzszakaNRDlOYtLAuKbTF3Q
SDq8OkZBAoGBAOn6B5jBxSa+5GcIIeGqtiRhDMExyblt3Gk2gaw6UIZpvVDXPWWm
r0tkGJrYecmqesN7DGmmdkyx8KONF+yzYLxSsIEGNesvVYe6PXTDZYYI57As4Z4W
DFlCDt2FaKuMXxyOlUCiXg94z8IJBJ2ldCmmG34gBSvuFe6V5x4XE3crAoGBANGG
P7AWw6uygfjog6B2dFT6n+9UhpyJlqwfPi5eD9V5JXtWlH6xWi3dRfuYAIafg95I
W8/OZGHrj44gNCgYjvZHud+H3NPJPZ7lftoay5KeShBAa/pCd67OMxp1SvvONYcp
7TSwm5s+hOJvQOpw2wg0cXnfrxGKpGLOFaRddp9XAoGAFdeXefUs2G8dl1i1AQIU
utSsgiSJtlvBJblG5bMT7VhVqgRN4P1sg9c2TM5EoETf7PvBruMxS/uYgUwcnaYp
M6tser7/rZLfoyoJrqrHAXo3VsT50u4v/O0jwh5AJTOXdW0CFeSSb1NR4cVBvw3B
CFpPWrjWgsFZHsqzpqV01b0CgYEAkDft4pDowmgumlvBLlQaotuX9q6hsWHrOjKP
JG9OSswGhq0DrWj5/5PNNe5cfk2SARChUZpo8hWoTFXSUL8GuHKKeFgWIhjkt1iU
RiAne5ZEuIb/S9UweDwqZM3TfRtlMNIlGh1uHh+cbBfUAQsJWM5wRUk4QcTCfdgI
gYhrvCUCgYBB6u8Q49RjrTBxWK8bcZOjVhYNrd3xrCunFVMt2QAXGGrRaXpqUMnp
xNUmGe9vGux+s0TRguZcLEX3vX+wFyBfFKwZY9hSU7PFY/da8echpu37JasKvAov
5+5XWI5RgF+SFVk+Q7St2BlSJa/vBAH8vtrX9Dt/hN/VSo2mAPAyMQ==
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDjjCCAnagAwIBAgIJAIOEuwkahzkOMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNV
BAMTAm5zMQswCQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1Ymxp
bjAeFw0xNDA4MzAxOTA3NDNaFw0yNDA4MjcxOTA3NDNaMDgxCzAJBgNVBAMTAm5z
MQswCQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9/00kouYWQ8DChuN3qnvhs0dZU
TF1WLfSNTK/fO79Q0/fcUQ3KX6P92o+gf1fxuu6D8/s21WTf2T77wNWfNk+4sXAR
bBiF4BFs8VJtVAlH+dX7ZTJTmn9uHHcnoItMvIUXZFi6Au8NL7jH64EenJqWyNi0
g0jGl119qg6KH0LBVGGiTTZ97qkStIsbP48282wE/atgVmMMklF+FRIAA6ZotBcg
DQ4xIZnZLcUFvZeiBhsxfDPZgNOd4SIGcKPvan/V953zVE1jL4rDELcZtJkWILw+
dzosdPFS7Om6HTU1Ti0KHB9Lh9wk7Ph92dApHGWgCXv5MAixYZbPXntzNJ0CAwEA
AaOBmjCBlzAdBgNVHQ4EFgQUsz/nOHpjMkV8pk9dFpy41batoTcwaAYDVR0jBGEw
X4AUsz/nOHpjMkV8pk9dFpy41batoTehPKQ6MDgxCzAJBgNVBAMTAm5zMQswCQYD
VQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpboIJAIOEuwkahzkO
MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEDOtAl7bgAXgcL3HRlV
H71tkUaok589PIqsTE4d8s8tFBZ92CyWD8ZPU46HbbyJXMFoFxiN7PvCzOBlgoZM
r80HJWZc9tSlqK0NIbIyk1aeM06+F8qB+8/vw/spIkdYzDv3avwyOrc6fFnEzbwz
5BFFrF2G9JajRKAP5snAV9iM8I2TD4l+w75MXXl7/DBEohdMBsTeDrrXj4q4sgoB
L/yLeCoK6inkMTU5DwcGbiqvNnZA+9T654qlAlKjPMObGGPphK5/QKcOnV7Qtdju
DHzDsDimdVbz9G1cxXs/AI/35GD7IDTdNTtmBhkf4/tsQ7Ua80xpIowb1fFUHmo1
UAo=
-----END CERTIFICATE-----
+1
View File
@@ -0,0 +1 @@
DisableFormat: true
+28
View File
@@ -0,0 +1,28 @@
Changes for C CGI Library
1.0
Initial Release
1.1
Added hexadecimal encoding: CGI_encode_hex() CGI_decode_hex()
Added simple cryptography: CGI_encrypt() CGI_decrypt()
Added SCGI server: CGI_prefork_server()
Modified CGI_decode_query() to parse query strings less
strictly.
1.2
Switched to the GNU Lesser General Public License
Fixed a bug when testing CONTENT_TYPE for
application/x-www-form-urlencoded. Changed strcasecmp()
to strncasecmp().
Changed t/test.c to terminate variable length argument
list passed to CGI_encode_query() with (char *)0. Type
cast needed for 64 bit platforms.
Changed doc.html to document that CGI_encode_query()
argument list must end with (char *)0.
+502
View File
@@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
+27
View File
@@ -0,0 +1,27 @@
# Set OPENSSL_INCLUDE to the directory that contains the
# "openssl" include directory.
OPENSSL_INCLUDE = /usr/include
# To build without openssl comment out the "CRYPT =" line
CRYPT = crypt.o
CFLAGS = -I . -I $(OPENSSL_INCLUDE)
libccgi.a: ccgi.o prefork.o $(CRYPT)
ar r libccgi.a ccgi.o prefork.o $(CRYPT)
ranlib libccgi.a
ccgi.o: ccgi.c ccgi.h
prefork.o: prefork.c
crypt.o: crypt.c ccgi.h
test: libccgi.a
cd t; make
clean:
rm -f *.o *.a */*.o
rm -f t/test examples/dump.cgi
+11
View File
@@ -0,0 +1,11 @@
You may need to edit Makefile and t/Makefile to specify where
your openssl include files and libraries are located. To build
without openssl see the comments in Makefile.
To build: make
To test: make test (requires openssl)
To install:
copy libccgi.a to a lib directory such as /usr/local/lib
copy ccgi.h to an include directory such as /usr/local/include
+1329
View File
File diff suppressed because it is too large. Load diff
+64
View File
@@ -0,0 +1,64 @@
/*
* C CGI Library version 1.2
*
* Copyright 2015 Stephen C. Losen. Distributed under the terms
* of the GNU Lesser General Public License (LGPL 2.1)
*/
#ifndef _CCGI_H
#define _CCGI_H
typedef struct CGI_varlist CGI_varlist;
typedef const char * const CGI_value;
char *CGI_decode_url(const char *p);
char *CGI_encode_url(const char *p, const char *keep);
char *CGI_encode_entity(const char *p);
char *CGI_encode_base64(const void *p, int len);
void *CGI_decode_base64(const char *p, int *len);
char *CGI_encode_hex(const void *p, int len);
void *CGI_decode_hex(const char *p, int *len);
char *CGI_encrypt(const void *p, int len, const char *password);
void *CGI_decrypt(const char *p, int *len, const char *password);
char *CGI_encode_query(const char *keep, ...);
char *CGI_encode_varlist(CGI_varlist *v, const char *keep);
CGI_varlist *CGI_decode_query(CGI_varlist *v, const char *query);
CGI_varlist *CGI_get_cookie(CGI_varlist *v);
CGI_varlist *CGI_get_query(CGI_varlist *v);
CGI_varlist *CGI_get_post(CGI_varlist *v, const char *template);
CGI_varlist *CGI_get_all(const char *template);
CGI_varlist *CGI_add_var(CGI_varlist *v, const char *varname,
const char *value);
void CGI_free_varlist(CGI_varlist *v);
CGI_value *CGI_lookup_all(CGI_varlist *v, const char *varname);
const char *CGI_lookup(CGI_varlist *v, const char *varname);
const char *CGI_first_name(CGI_varlist *v);
const char *CGI_next_name(CGI_varlist *v);
void CGI_prefork_server(const char *host, int port, const char *pidfile,
int maxproc, int minidle, int maxidle, int maxreq,
void (*callback)(void));
#endif
+170
View File
@@ -0,0 +1,170 @@
/*
* C CGI Library version 1.2
*
* Author: Stephen C. Losen, University of Virginia
*
* Copyright 2015 Stephen C. Losen. Distributed under the terms
* of the GNU Lesser General Public License (LGPL 2.1)
*
* CGI_encrypt() converts arbitrary input data to a secure,
* encrypted string, using a password provided by the user.
* CGI_decrypt() reverses the process and returns the original
* data. The secure string includes a message digest for
* verifying the data.
*/
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <ccgi.h>
#define SALT_SIZE 8
#define DIGEST_SIZE 20
/*
* init_salt() places 8 random bytes into salt with RAND_bytes()
* or else uses SHA1 digest of current time and process ID
*/
static void
init_salt(unsigned char *salt) {
EVP_MD_CTX ctx;
unsigned char md[DIGEST_SIZE];
struct timeval tv;
pid_t pid;
unsigned int rlen;
if (RAND_bytes(salt, SALT_SIZE) == 1) {
return;
}
gettimeofday(&tv, 0);
pid = getpid();
EVP_DigestInit(&ctx, EVP_sha1());
EVP_DigestUpdate(&ctx, &tv, sizeof(tv));
EVP_DigestUpdate(&ctx, &pid, sizeof(pid));
EVP_DigestFinal(&ctx, md, &rlen);
memcpy(salt, md, SALT_SIZE);
}
/*
* digest() feeds input p of length len, the password and
* the salt to SHA1 and returns the digest in md.
*/
static void
digest(const void *p, int len, const char *password,
const unsigned char *salt, unsigned char *md)
{
EVP_MD_CTX ctx;
unsigned int rlen;
EVP_DigestInit(&ctx, EVP_sha1());
EVP_DigestUpdate(&ctx, salt, SALT_SIZE);
EVP_DigestUpdate(&ctx, password, strlen(password));
EVP_DigestUpdate(&ctx, p, len);
EVP_DigestFinal(&ctx, md, &rlen);
}
/*
* CGI_encrypt() returns an encrypted base64 string using input p
* of length len and the password. We generate a random 8 byte
* salt. We compute a SHA1 message digest using p, the password,
* and the salt. We generate a cipher key using the password and
* salt. We encrypt the digest and input with the AES-256-CBC cipher
* and key. We base64 encode the return string, which consists of
* the unencrypted salt, encrypted digest and encrypted input.
*/
char *
CGI_encrypt(const void *p, int len, const char *password) {
EVP_CIPHER_CTX ctx;
unsigned char md[DIGEST_SIZE];
unsigned char iv[EVP_MAX_IV_LENGTH];
unsigned char key[EVP_MAX_KEY_LENGTH];
unsigned char *out;
char *b64;
int offset, rlen;
if (p == 0 || len <= 0 || password == 0 || *password == 0) {
return 0;
}
out = malloc(SALT_SIZE + DIGEST_SIZE + len + EVP_MAX_BLOCK_LENGTH);
init_salt(out);
digest(p, len, password, out, md);
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), out,
(unsigned char *) password, strlen(password), 1, key, iv);
EVP_EncryptInit(&ctx, EVP_aes_256_cbc(), key, iv);
offset = SALT_SIZE;
EVP_EncryptUpdate(&ctx, out + offset, &rlen, md, DIGEST_SIZE);
offset += rlen;
EVP_EncryptUpdate(&ctx, out + offset, &rlen, p, len);
offset += rlen;
EVP_EncryptFinal(&ctx, out + offset, &rlen);
b64 = CGI_encode_base64(out, offset + rlen);
free(out);
return b64;
}
/*
* CGI_decrypt() returns a pointer to data decrypted from
* base64 string p and the password. p was previously encrypted
* with CGI_encrypt(). We base64 decode p and the first 8 bytes
* is the salt followed by the encrypted message digest and
* encrypted data. We generate the cipher key using the salt
* and the password. We decrypt the message digest and data
* using the AES-256-CBC cipher and key. We re-compute the
* message digest using the decrypted data, password, and salt.
* If the two digests match then we return a pointer to the data
* and place its length in *len. Otherwise we return null.
*/
void *
CGI_decrypt(const char *p, int *len, const char *password) {
EVP_CIPHER_CTX ctx;
unsigned char md[DIGEST_SIZE];
unsigned char iv[EVP_MAX_IV_LENGTH];
unsigned char key[EVP_MAX_KEY_LENGTH];
unsigned char *ret, *out;
int offset, rlen = 0;
if (p == 0 || *p == 0 || password == 0 || *password == 0) {
return 0;
}
ret = CGI_decode_base64(p, &rlen);
if (rlen <= DIGEST_SIZE + SALT_SIZE) {
free(ret);
return 0;
}
out = malloc(rlen + EVP_MAX_BLOCK_LENGTH);
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), ret,
(unsigned char *) password, strlen(password), 1, key, iv);
EVP_DecryptInit(&ctx, EVP_aes_256_cbc(), key, iv);
EVP_DecryptUpdate(&ctx, out, &offset, ret + SALT_SIZE,
rlen - SALT_SIZE);
EVP_DecryptFinal(&ctx, out + offset, &rlen);
rlen += offset - DIGEST_SIZE;
/*
* The salt is in ret, the decrypted digest and decrypted
* data are in out, and the data length is rlen.
*/
if (rlen > 0) {
digest(out + DIGEST_SIZE, rlen, password, ret, md);
}
if (rlen > 0 && memcmp(out, md, DIGEST_SIZE) == 0) {
memcpy(ret, out + DIGEST_SIZE, rlen);
ret[rlen] = 0;
if (len != 0) {
*len = rlen;
}
}
else {
free(ret);
ret = 0;
}
free(out);
return ret;
}
+921
View File
@@ -0,0 +1,921 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="generator" content=
"HTML Tidy for Linux/x86 (vers 14 June 2007), see www.w3.org" />
<title>C CGI Library 1.2</title>
<style type="text/css">
/*<![CDATA[*/
pre {
font-family:courier;
background-color:#FFFFCC;
}
dd, li {
margin-top: 0.5em;
}
dt {
margin-top: 1em;
}
/*]]>*/
</style>
</head>
<body style="width:100ex;margin-left:3em">
<h1 style="text-align:center">C CGI Library 1.2</h1>
<h2>Contents</h2>
<ul>
<li><a href="#intro">Introduction</a></li>
<li><a href="#types">C CGI Library Data Types</a></li>
<li><a href="#func">C CGI Library Functions</a></li>
<li><a href="#using">Using the C CGI Library</a></li>
<li><a href="#upload">Uploading Files</a></li>
<li><a href="#crypto">Simple Cryptography Support</a></li>
<li><a href="#prefork">Pre Forking SCGI Server</a></li>
</ul>
<h2><a name="intro" id="intro"></a>Introduction</h2>
<p><i>C CGI</i> is a C language library for decoding, storing,
and retrieving CGI data passed by the web server via the CGI
interface. The library also has several handy data conversion
functions.</p>
<p>Author: Stephen C. Losen, University of Virginia</p>
<h3>C CGI Library Features</h3>
<ul>
<li>Decodes and stores CGI variables in <a href=
"#query">application/x-www-form-urlencoded</a> format, which
may come from the <i>QUERY_STRING</i> environment variable or
from standard input.</li>
<li>Decodes and stores CGI variables in
<i>multipart/form-data</i> format from standard input.</li>
<li>Handles <a href="#upload">file uploads</a>.</li>
<li>Parses and stores HTTP <i>cookies</i> from the
<i>HTTP_COOKIE</i> environment variable.</li>
<li>Stores CGI data in lookup tables, which can be accessed
directly by variable name, or accessed iteratively.</li>
<li>Allows strings to be stored in lookup tables.</li>
<li>Encodes/decodes strings in <a href=
"#query">application/x-www-form-urlencoded</a> format.</li>
<li>Encodes/decodes data in base64 format.</li>
<li>Encodes/decodes data in hexadecimal format.</li>
<li>Encodes text strings using HTML entity encodings such as
<b>&amp;lt;</b> and <b>&amp;amp;</b>.</li>
<li>Provides <i>openssl</i> <a href="#crypto">cryptography</a>
to encrypt/decrypt and verify data.</li>
<li>Provides a <a href="#prefork">pre forking SCGI
server</a>.</li>
</ul>
<h3><a name="query" id="query"></a>Query Strings and URL
Encoding</h3>
<p>A <i>query string</i> is a list of CGI variable names and
values in <i>application/x-www-form-urlencoded</i> format, which
looks like this:
<b>name1=value1&amp;name2=value2&amp;name3=value3</b>&nbsp;...
Each name and value is <i>URL encoded</i> as follows. Letters and
digits are not changed. Each <i>space</i> is converted to
<b>+</b>. Most other characters become <b>%xx</b> (percent
followed by two hexadecimal digits) where <i>xx</i> is the
numeric code of the character. For example, <b>Help&nbsp;Me!</b>
is URL encoded as <b>Help+Me%21</b>. Note that in a query string
the <b>=</b> and <b>&amp;</b> characters <b>between</b> names and
values are <b>not</b> URL encoded. However, if a name or value
itself contains a <b>=</b> or <b>&amp;</b>, then it is URL
encoded using <b>%xx</b>.</p>
<p>When decoding query strings, the C CGI Library is tolerant of
lax <b>%xx</b> encoding. It accepts most literal punctuation
characters except for <b>+</b> and <b>&amp;</b>, which must be
encoded. It accepts literal <b>=</b> in variable values and it
accepts literal <b>%</b> when not followed by two hexadecimal
digits. For example, <b>var=20%=1/5</b> is treated like
<b>var=20%25%3D1%2F5</b> and sets the value of <i>var</i> to
<b>20%=1/5</b>. A string that is not followed by <b>=</b> is a
variable name whose value is <b>""</b>, a zero length string. For
example, <b>str1&amp;str2&amp;</b>&nbsp;... is the same as
<b>str1=&amp;str2=&amp;</b>&nbsp;..., where <i>str1</i> and
<i>str2</i> are variable names whose value is <b>""</b>.</p>
<h3>CGI Data Representation and Conversion</h3>
<p>For simplicity and ease of use, most C CGI Library functions
accept and/or return null terminated strings. You can easily
convert a string to a numeric data type with the standard C
library functions <i>atoi()</i>, <i>atof()</i>, <i>strtol()</i>,
<i>strtod()</i>, etc. And you can convert a numeric data type to
a string with <i>sprintf()</i>.</p>
<p>Unfortunately, null terminated strings are not suitable for
storing raw binary data, because a null byte in the data is
mistaken for the string terminator. URL encoded strings
containing <b>%00</b> do not decode correctly because <b>%00</b>
results in a null byte. You can still manipulate binary data if
you encode it beforehand, and the C CGI library has functions for
encoding/decoding <a href="#CGI_encode_base64">base64</a> and
<a href="#CGI_encode_hex">hexadecimal</a>.</p>
<h2><a name="types" id="types"></a>C CGI Library Data Types</h2>
<p>In your C source you include the <i>ccgi.h</i> header file,
which declares these data types.</p>
<dl>
<dt>CGI_varlist</dt>
<dd>is a list (lookup table) of CGI variables and/or cookies.
Each list entry is a name and one or more values, where names
and values are all null terminated strings. A name may have
multiple values because 1) some HTML form elements, such as
<i>checkboxes</i> and <i>selections</i> allow the user to
choose multiple values and 2) the same name can be given to
multiple form input elements. A CGI_varlist lists variable
names and values in the same order that they are stored. In
practice this ends up being the order of the input tags in the
HTML form, but there is no requirement that browsers must
preserve this ordering.</dd>
<dt>CGI_value</dt>
<dd>is a read only pointer to a read only null terminated
string (<i>const char * const</i>). The <a href=
"#CGI_lookup_all">CGI_lookup_all()</a> function returns a null
terminated array of these pointers.</dd>
</dl>
<h2><a name="func" id="func"></a>C CGI Library Functions</h2>
<p>The C CGI library provides these functions.</p>
<ul>
<li><a href="#CGI_get_query">CGI_get_query()</a> decodes CGI
variables from the <i>QUERY_STRING</i> environment variable and
adds them to a CGI_varlist.</li>
<li><a href="#CGI_get_post">CGI_get_post()</a> decodes CGI
variables from standard input and adds them to a
CGI_varlist.</li>
<li><a href="#CGI_get_cookie">CGI_get_cookie()</a> parses
cookies from the <i>HTTP_COOKIE</i> environment variable and
adds them to a CGI_varlist.</li>
<li><a href="#CGI_get_all">CGI_get_all()</a> decodes all CGI
variables and cookies and returns them in a CGI_varlist.</li>
<li><a href="#CGI_decode_query">CGI_decode_query()</a> decodes
CGI variables in a null terminated <a href="#query">query
string</a> and adds them to a CGI_varlist.</li>
<li><a href="#CGI_add_var">CGI_add_var()</a> adds a variable
name and value to a CGI_varlist.</li>
<li><a href="#CGI_lookup_all">CGI_lookup_all()</a> looks up a
name in a CGI_varlist and returns all of its values in an
array.</li>
<li><a href="#CGI_lookup">CGI_lookup()</a> looks up a name in a
CGI_varlist and returns its first (or only) value.</li>
<li><a href="#CGI_first_name">CGI_first_name()</a> returns the
first name in a CGI_varlist.</li>
<li><a href="#CGI_next_name">CGI_next_name()</a> returns the
next name in a CGI_varlist.</li>
<li><a href="#CGI_free_varlist">CGI_free_varlist()</a> frees
memory used by a CGI_varlist.</li>
<li><a href="#CGI_encode_query">CGI_encode_query()</a> URL
encodes a list of strings into a <a href="#query">query
string</a>.</li>
<li><a href="#CGI_encode_varlist">CGI_encode_varlist()</a> URL
encodes a CGI_varlist into a <a href="#query">query
string</a>.</li>
<li><a href="#CGI_encrypt">CGI_encrypt()</a> encrypts data
bytes into a secure string.</li>
<li><a href="#CGI_decrypt">CGI_decrypt()</a> decrypts a secure
string and verifies the contents.</li>
<li><a href="#CGI_decode_url">CGI_decode_url()</a> decodes a
URL encoded string.</li>
<li><a href="#CGI_encode_url">CGI_encode_url()</a> URL encodes
a string.</li>
<li><a href="#CGI_encode_entity">CGI_encode_entity()</a> HTML
entity encodes a string.</li>
<li><a href="#CGI_encode_base64">CGI_encode_base64()</a> base64
encodes data bytes.</li>
<li><a href="#CGI_decode_base64">CGI_decode_base64()</a>
decodes a base64 encoded string.</li>
<li><a href="#CGI_encode_hex">CGI_encode_hex()</a> hexadecimal
encodes data bytes.</li>
<li><a href="#CGI_decode_hex">CGI_decode_hex()</a> decodes a
hexadecimal encoded string.</li>
<li><a href="#CGI_prefork_server">CGI_prefork_server()</a>
implements a pre forking SCGI server.</li>
</ul>
<p>Except for <i>CGI_prefork_server()</i>, the C CGI library
functions are reentrant because they do not modify any global
variables or use any static local variables, so you can use these
functions with threads.</p>
<p>Some functions accept null terminated string parameters of
type <i>const&nbsp;char&nbsp;*</i>. These functions make copies
of strings as necessary so that after the function returns you
can safely do anything you want with any string that you have
passed as a parameter. Some functions return null terminated
strings of type <i>const&nbsp;char&nbsp;*</i> and you should not
modify these strings.</p>
<dl>
<dt><a name="CGI_get_query" id="CGI_get_query"></a> CGI_varlist
*CGI_get_query (CGI_varlist *varlist);</dt>
<dd><i>CGI_get_query()</i> decodes CGI variables in the
<i>QUERY_STRING</i> environment variable and adds them to
variable list <i>varlist</i>. <i>QUERY_STRING</i> is presumed
to be in <a href="#query">application/x-www-form-urlencoded</a>
format. If <i>varlist</i> is null then a new variable list is
created and returned, otherwise <i>varlist</i> is returned.
Null is returned if <i>varlist</i> is null and
<i>QUERY_STRING</i> does not exist or contains no CGI
variables.</dd>
<dt><a name="CGI_get_post" id="CGI_get_post"></a> CGI_varlist
*CGI_get_post(CGI_varlist *varlist, const char *template);</dt>
<dd><i>CGI_get_post()</i> reads and decodes CGI variables from
standard input and adds them to variable list <i>varlist</i>.
If <i>varlist</i> is null then a new variable list is created
and returned, otherwise <i>varlist</i> is returned. Null is
returned if <i>varlist</i> is null and standard input is empty
or contains no CGI variables. The <i>template</i> parameter
(which may be null) is a file name template string that is
passed to the standard C library function <i>mkstemp()</i> when
uploading a file. (See the <a href="#upload">file upload</a>
section for more information.) <i>CGI_get_post()</i> checks the
<i>CONTENT_TYPE</i> environment variable to get the data
encoding, which is either <a href=
"#query">application/x-www-form-urlencoded</a> or
<i>multipart/form-data</i>.</dd>
<dt><a name="CGI_get_cookie" id="CGI_get_cookie"></a>
CGI_varlist *CGI_get_cookie(CGI_varlist *varlist);</dt>
<dd><i>CGI_get_cookie()</i> parses HTTP cookies from the
<i>HTTP_COOKIE</i> environment variable and adds them to
variable list <i>varlist</i>. If <i>varlist</i> is null then a
new variable list is created and returned, otherwise
<i>varlist</i> is returned. Returns null if <i>varlist</i> is
null and <i>HTTP_COOKIE</i> does not exist or contains no
cookies.</dd>
<dt><a name="CGI_get_all" id="CGI_get_all"></a> CGI_varlist
*CGI_get_all(const char *template);</dt>
<dd><i>CGI_get_all()</i> calls <a href=
"#CGI_get_cookie">CGI_get_cookie()</a>, <a href=
"#CGI_get_query">CGI_get_query()</a> and <a href=
"#CGI_get_post">CGI_get_post()</a>, returning all of the CGI
variables and cookies in one variable list. The <i>template</i>
parameter (which may be null) is passed on to
<i>CGI_get_post()</i>.</dd>
<dt><a name="CGI_decode_query" id="CGI_decode_query"></a>
CGI_varlist *CGI_decode_query(CGI_varlist *varlist, const char
*query);</dt>
<dd><i>CGI_decode_query()</i> decodes CGI variables in null
terminated <a href="#query">query string</a> <i>query</i>
(which is in <i>application/x-www-urlencoded</i> format) and
adds the CGI variables to <i>varlist</i>. If <i>varlist</i> is
null then a new variable list is created and returned,
otherwise <i>varlist</i> is returned. Returns null if
<i>varlist</i> is null and <i>query</i> is null or has no CGI
variables.</dd>
<dt><a name="CGI_add_var" id="CGI_add_var"></a> CGI_varlist
*CGI_add_var(CGI_varlist *varlist, const char *name, const char
*value);</dt>
<dd><i>CGI_add_var()</i> adds an entry named <i>name</i> with
value <i>value</i> to variable list <i>varlist</i>. If
<i>varlist</i> is null, then a new variable list is created and
returned, otherwise <i>varlist</i> is returned. If the variable
list already has an entry named <i>name</i>, then the value is
added to that entry. This function is provided so that you can
add data to a variable list by hand, or create a variable list
for other purposes.</dd>
<dt><a name="CGI_lookup_all" id="CGI_lookup_all"></a> CGI_value
*CGI_lookup_all(CGI_varlist *varlist, const char *name);</dt>
<dd><i>CGI_lookup_all()</i> searches <i>varlist</i> for an
entry whose name matches <i>name</i> case sensitively and
returns all the values of the entry, or returns null if no
entry is found. If <i>name</i> is null, then the values of the
entry most recently visited by <a href=
"#CGI_first_name">CGI_first_name()</a> or <a href=
"#CGI_next_name">CGI_next_name()</a> are returned. The return
value is a null terminated array of pointers to null terminated
strings. The array and strings are stored in memory allocated
to <i>varlist</i>, which you should not modify. The return type
<i>CGI_value&nbsp;*</i>
(<i>const&nbsp;char&nbsp;*&nbsp;const&nbsp;*</i>) declares the
array and strings to be read only to discourage
modification.</dd>
<dt><a name="CGI_lookup" id="CGI_lookup"></a> const char
*CGI_lookup(CGI_varlist *varlist, const char *name);</dt>
<dd><i>CGI_lookup()</i> searches <i>varlist</i> for an entry
whose name matches <i>name</i> case sensitively and returns the
first (or only) value of the entry, or returns null if no entry
is found. If <i>name</i> is null, then the first value of the
entry most recently visited by <a href=
"#CGI_first_name">CGI_first_name()</a> or <a href=
"#CGI_next_name">CGI_next_name()</a> is returned. If you expect
an entry to have a single value, then this function is easier
to use than <i>CGI_lookup_all()</i> and it is more efficient
because it doesn't construct an array to return multiple
values. You should not modify the string returned by this
function.</dd>
<dt><a name="CGI_first_name" id="CGI_first_name"></a> const
char *CGI_first_name(CGI_varlist *varlist);</dt>
<dd><i>CGI_first_name()</i> begins an iteration of
<i>varlist</i> and returns the name of the first entry, or
returns null if <i>varlist</i> is null. You can get all the
values of this entry with
<i>CGI_lookup_all(varlist,&nbsp;0);</i> You should not modify
the string returned by this function.</dd>
<dt><a name="CGI_next_name" id="CGI_next_name"></a> const char
*CGI_next_name(CGI_varlist *varlist);</dt>
<dd><i>CGI_next_name()</i> continues an iteration of
<i>varlist</i> and returns the name of the next entry. You can
get all the values of this entry with
<i>CGI_lookup_all(varlist,&nbsp;0);</i> Returns null if 1)
there are no more entries, or 2) <i>varlist</i> is null, or 3)
no iteration was started with <i>CGI_first_name()</i>, or 4)
new data was added to <i>varlist</i> during the iteration. You
should not modify the string returned by this function.</dd>
<dt><a name="CGI_free_varlist" id="CGI_free_varlist"></a> void
CGI_free_varlist(CGI_varlist *varlist);</dt>
<dd><i>CGI_free_varlist()</i> frees all memory used by variable
list <i>varlist</i>.</dd>
<dt><a name="CGI_encode_query" id="CGI_encode_query"></a> char
*CGI_encode_query(const char *keep, const char *name1, const
char *value1, ..., (char *)0);</dt>
<dd><i>CGI_encode_query()</i> returns a query string in
<a href="#query">application/x-www-form-urlencoded</a> format
that is built from a null terminated list of null terminated
string arguments. The first argument <i>keep</i> (which may be
null) is a null terminated string that specifies characters
that you do not want to <a href="#query">URL encode</a> with
<b>%xx</b>. You do not need to specify letters or digits
because they are never encoded. The first two arguments after
<i>keep</i> are a name and value pair, the next two are a
second name and value pair, etc. <b>Be sure to terminate the
argument list with (char *)0</b>. (When passing a variable
length argument list, the C compiler does not automatically
cast <b>0</b> to <b>(char *)0</b>, which is necessary
on 64 bit platforms.) In the result the names
and values are URL encoded and separated with literal
<b>&amp;</b> and <b>=</b> characters like this:
<b>name1=value1&amp;name2=value2</b>&nbsp;... Memory is
allocated with <i>malloc()</i> to hold the result, which you
should free with <i>free()</i>.</dd>
<dt><a name="CGI_encode_varlist" id="CGI_encode_varlist"></a>
char *CGI_encode_varlist(CGI_varlist *varlist, const char
*keep);</dt>
<dd><i>CGI_encode_varlist()</i> returns a query string in
<a href="#query">application/x-www-form-urlencoded</a> format
that is built from CGI_varlist <i>varlist</i>. The argument
<i>keep</i> (which may be null) is a null terminated string
that specifies characters that you do not want to <a href=
"#query">URL encode</a> with <b>%xx</b>. You do not need to
specify letters or digits because they are never encoded. The
names and values in <i>varlist</i> are URL encoded and
separated with literal <b>&amp;</b> and <b>=</b> characters
like this: <b>name1=value1&amp;name2=value2</b>&nbsp;... Memory
is allocated with <i>malloc()</i> to hold the result, which you
should free with <i>free()</i>. You can use <a href=
"#CGI_add_var">CGI_add_var()</a> to build <i>varlist</i>.</dd>
<dt><a name="CGI_encrypt" id="CGI_encrypt"></a> char
*CGI_encrypt(const void *p, int len, const char
*password);</dt>
<dd><i>CGI_encrypt()</i> encrypts input <i>p</i> of length
<i>len</i> bytes using <i>password</i> to generate the cipher
key. Also computes a <i>message digest</i> using the input
data. Returns the encrypted digest and encrypted data in a
base64 encoded string, which must be decrypted with <a href=
"#CGI_decrypt">CGI_decrypt()</a> and the same password. Returns
null if <i>p</i> is null or if <i>len</i> is less than one or
if <i>password</i> is null or zero length. Memory is allocated
with <i>malloc()</i> to hold the result, which you should free
with <i>free()</i>. (See the <a href="#crypto">cryptography</a>
section for more information.)</dd>
<dt><a name="CGI_decrypt" id="CGI_decrypt"></a> void
*CGI_decrypt(const char *p, int *len, const char
*password);</dt>
<dd><i>CGI_decrypt()</i> decrypts input <i>p</i>, which was
encrypted with <a href="#CGI_encrypt">CGI_encrypt()</a>, using
<i>password</i> to generate the cipher key. The output is a
<i>message digest</i> and decrypted data bytes. Verifies the
data using the message digest and returns the data. Returns the
length of the data in <i>*len</i>. Returns null if <i>p</i>
cannot be decrypted and verified. Also returns null if <i>p</i>
or <i>password</i> is null or zero length. Memory is allocated
with <i>malloc()</i> to hold the result, which you should free
with <i>free()</i>. (See the <a href="#crypto">cryptography</a>
section for more information.)</dd>
<dt><a name="CGI_decode_url" id="CGI_decode_url"></a> char
*CGI_decode_url(const char *p);</dt>
<dd><i>CGI_decode_url()</i> returns a <a href="#query">URL
decoded</a> copy of input string <i>p</i>. Memory is allocated
with <i>malloc()</i> to hold the result, which you should free
with <i>free()</i>.</dd>
<dt><a name="CGI_encode_url" id="CGI_encode_url"></a> char
*CGI_encode_url(const char *p, const char *keep);</dt>
<dd><i>CGI_encode_url()</i> returns a <a href="#query">URL
encoded</a> copy of input string <i>p</i>. Memory is allocated
with <i>malloc()</i> to hold the result, which you should free
with <i>free()</i>. The <i>keep</i> argument (which may be
null) is a null terminated string that specifies characters
that you do not want to URL encode with <b>%xx</b>. You do not
need to specify letters or digits because they are never
encoded.</dd>
<dt><a name="CGI_encode_entity" id="CGI_encode_entity"></a>
char *CGI_encode_entity(const char *p);</dt>
<dd><i>CGI_encode_entity()</i> returns a HTTP entity encoded
copy of input string <i>p</i>. Memory is allocated with
<i>malloc()</i> to hold the result, which you should free with
<i>free()</i>. <i>CGI_encode_entity()</i> makes the following
conversions: <b>&lt;</b> becomes <b>&amp;lt;</b>, <b>&gt;</b>
becomes <b>&amp;gt;</b>, <b>&amp;</b> becomes <b>&amp;amp;</b>,
<b>"</b> becomes <b>&amp;quot;</b>, <b>'</b> becomes
<b>&amp;#39;</b>, <i>newline</i> becomes <b>&amp;#10;</b>, and
<i>return</i> becomes <b>&amp;#13;</b>.</dd>
<dt><a name="CGI_encode_base64" id="CGI_encode_base64"></a>
char *CGI_encode_base64(const void *p, int len);</dt>
<dd><i>CGI_encode_base64()</i> encodes input <i>p</i> of length
<i>len</i> bytes, and returns the result, which is a null
terminated base64 encoded string. Memory is allocated for the
result with <i>malloc()</i>, which you should free with
<i>free()</i>. Base64 is a commonly used encoding that
represents arbitrary bytes of data using the following
printable characters: upper case, lower case, digits, <b>+</b>,
<b>/</b>, and <b>=</b>.</dd>
<dt><a name="CGI_decode_base64" id="CGI_decode_base64"></a>
void *CGI_decode_base64(const char *p, int *len);</dt>
<dd><i>CGI_decode_base64()</i> decodes <i>p</i>, which is a
null terminated base64 encoded string, and returns the result.
The length of the result is stored in <i>*len</i> and a null
byte is written just after the last byte of the result. Memory
is allocated with <i>malloc()</i> to hold the result, which you
should free with <i>free()</i>.</dd>
<dt><a name="CGI_encode_hex" id="CGI_encode_hex"></a> char
*CGI_encode_hex(const void *p, int len);</dt>
<dd><i>CGI_encode_hex()</i> encodes input <i>p</i>, of length
<i>len</i> bytes, and returns the result, which is a null
terminated hexadecimal encoded string. Memory is allocated for
the result with <i>malloc()</i>, which you should free with
<i>free()</i>. Hexadecimal is a commonly used encoding that
represents arbitrary bytes of data using two hexadecimal digits
for each byte.</dd>
<dt><a name="CGI_decode_hex" id="CGI_decode_hex"></a> void
*CGI_decode_hex(const char *p, int *len);</dt>
<dd><i>CGI_decode_hex()</i> decodes <i>p</i>, which is a null
terminated hexadecimal encoded string, and returns the result.
The length of the result is stored in <i>*len</i> and a null
byte is written just after the last byte of the result. Memory
is allocated with <i>malloc()</i> to hold the result, which you
should free with <i>free()</i>. Returns null if <i>p</i> is
null, or if the length of <i>p</i> is odd, or if <i>p</i>
contains characters other than hexadecimal digits.</dd>
<dt><a name="CGI_prefork_server" id="CGI_prefork_server"></a>
void CGI_prefork_server(const char *host, int port, const char
*pidfile, int maxproc, int minidle, int maxidle, int maxreq,
void (*callback)(void));</dt>
<dd><i>CGI_prefork_server()</i> implements a <a href=
"http://www.mems-exchange.org/software/scgi/">SCGI</a> (Simple
CGI) pre forking server. The <i>host</i> specifies a local
network address, either by hostname or dotted decimal IP
address, and <i>port</i> specifies a TCP port number. The SCGI
server listens for requests on the specified address and port.
If <i>host</i> is null, then the server listens on all local
addresses. The <i>pidfile</i> (which may be null) is an
optional file name where the server writes its process ID. The
SCGI server forks up to <i>maxproc</i> child processes to
handle requests. It forks and destroys processes to maintain
between <i>minidle</i> and <i>maxidle</i> idle processes. Each
process exits after handling <i>maxreq</i> requests. If
<i>maxreq</i> is less than one, then it is unlimited. You
provide the <i>callback</i> function, which the SCGI server
calls to process each web request. <i>CGI_prefork_server()</i>
does not return unless it fails. (See the <a href=
"#prefork">SCGI server</a> section for more information.)</dd>
</dl>
<h2><a name="using" id="using"></a>Using the C CGI Library</h2>
<p>Here is an example program that outputs all of its CGI data.
In your C source, include <i>ccgi.h</i> and link your program
with <i>libccgi.a</i>. (If you use <i>CGI_encrypt()</i> or
<i>CGI_decrypt()</i> then you must also link with the
<i>openssl</i> library <i>libcrypto</i>.) The simplest way to
obtain your CGI data is with <i>CGI_get_all()</i>. If you are not
uploading any files, then just pass it a null argument.</p>
<pre>
#include &lt;stdio.h&gt;
#include &lt;ccgi.h&gt;
int main(int argc, char **argv) {
CGI_varlist *varlist;
const char *name;
CGI_value *value;
int i;
fputs("Content-type: text/plain\r\n\r\n", stdout);
if ((varlist = CGI_get_all(0)) == 0) {
printf("No CGI data received\r\n");
return 0;
}
/* output all values of all variables and cookies */
for (name = CGI_first_name(varlist); name != 0;
name = CGI_next_name(varlist))
{
value = CGI_lookup_all(varlist, 0);
/* CGI_lookup_all(varlist, name) could also be used */
for (i = 0; value[i] != 0; i++) {
printf("%s [%d] = %s\r\n", name, i, value[i]);
}
}
CGI_free_varlist(varlist); /* free variable list */
return 0;
}
</pre>
<h2><a name="upload" id="upload"></a>File Uploads</h2>
<p>To upload files to your CGI program, your HTML form must use
the <i>post</i> method and must specify
<i>multipart/form-data</i> encoding, so the form tag looks like
this:</p>
<pre>
&lt;form method="POST" enctype="multipart/form-data"
action="url-for-your-CGI"&gt;
</pre>
<p>Within the HTML form a file upload tag looks like this:</p>
<pre>
&lt;input type="file" name="uploadfield" /&gt;
</pre>
<p>Most browsers render this tag with a file browse button and a
text field to enter and/or display the name of the file being
uploaded.</p>
<p>When the user submits the form, the browser sends the file
data together with any CGI form variables using
<i>multipart/form-data</i> encoding. To receive uploaded file
data you must call <a href="#CGI_get_post">CGI_get_post()</a> or
<a href="#CGI_get_all">CGI_get_all()</a>, and pass a file name
template string, a copy of which is passed on to standard C
function <i>mkstemp()</i>. The final six characters of the
template string must be <b>XXXXXX</b> and <i>mkstemp()</i>
replaces these with random characters to create a new file with a
unique name. If you pass a null or invalid template string, then
uploaded file data is silently discarded.</p>
<p><i>CGI_get_post()</i> or <i>CGI_get_all()</i> stores two names
for the uploaded file in the variable list, which you can
retrieve with</p>
<pre>
value = CGI_lookup_all(varlist, "uploadfield");
</pre>
<p>This returns an array of two strings (provided no other form
input tags are named <i>uploadfield</i>). In <i>value[0]</i> is
the name of the uploaded file on the web server, which is derived
from the template string. In <i>value[1]</i> is the name of the
file specified by the user in the browser. If the user has not
uploaded a file, then <i>varlist</i> has no entry named
<i>uploadfield</i> and <i>CGI_lookup_all()</i> returns null.</p>
<p>We use <i>mkstemp()</i> to guarantee unique file names because
a form may have multiple file upload fields, resulting in
multiple files. Furthermore, multiple users can upload files to
multiple instances of the CGI at the same time. Here is an
example CGI program that uploads a file.</p>
<pre>
#include &lt;ccgi.h&gt;
#include &lt;stdio.h&gt;
int main(int argc, char **argv) {
CGI_varlist *varlist;
CGI_value *value;
fputs("Content-type: text/plain\r\n\r\n", stdout);
varlist = CGI_get_all("/tmp/cgi-upload-XXXXXX");
value = CGI_lookup_all(varlist, "uploadfield");
if (value == 0 || value[1] == 0) {
fputs("No file was uploaded\r\n", stdout);
}
else {
printf("Your file \"%s\" was uploaded to my file \"%s\"\r\n",
value[1], value[0]);
/* Do something with the file here */
unlink(value[0]);
}
CGI_free_varlist(varlist);
return 0;
}
</pre>
<h2><a name="crypto" id="crypto"></a>Simple Cryptography
Support</h2>
<p>HTML and HTTP provide no native support for protecting and
verifying CGI data. Many web applications pass state data to the
browser in cookies or form variables. When the browser passes
this data back, the web application cannot tell if it has been
tampered with. An attacker can easily handcraft a web request
that includes forged cookies or forged form data.</p>
<p>The C CGI Library addresses this problem with <a href=
"#CGI_encrypt">CGI_encrypt()</a> and <a href=
"#CGI_decrypt">CGI_decrypt()</a>. <i>CGI_encrypt()</i> computes a
<i>SHA1 message digest</i> from the input data, encrypts the
digest and the input data, and returns the result in a base64
encoded string. (Raw encrypted output is binary.)
<i>CGI_decrypt()</i> reverses the process. It decrypts the digest
and the data, and recomputes the digest. If the two digests
match, then it returns the data. Otherwise it returns null to
indicate failure. <i>CGI_encrypt()</i> and <i>CGI_decrypt()</i>
use a password that you provide, which is a null terminated
string of arbitrary length (the longer the better). It is
essentially impossible to tamper with the data without knowing
the password. If the output of <i>CGI_encrypt()</i> is modified
in any way, then <i>CGI_decrypt()</i> computes a message digest
that does not match and returns null.</p>
<p>To protect state data, simply encrypt it with
<i>CGI_encrypt()</i> and a password before passing it to the
browser. When the browser passes the encrypted data back, decrypt
with <i>CGI_decrypt()</i> and the same password. If
<i>CGI_decrypt()</i> succeeds, then you know that the data has
not changed. Of course the security of the data depends on the
security of the password, which should be very difficult to guess
and very difficult to steal.</p>
<p><i>CGI_encrypt()</i> uses the <i>openssl</i> library
<i>libcrypto</i> and has these features:</p>
<ul>
<li>Uses the <i>AES-256-CBC</i> cipher.</li>
<li>Generates the cipher key by feeding the password and a
random <i>salt</i> to a hash function. One password results in
a huge number of different cipher keys.</li>
<li>Uses the <i>SHA1</i> message digest algorithm to verify the
input data. Feeds the salt, the password, and the input data to
<i>SHA1</i> to generate the message digest.</li>
<li>Encrypts the message digest and the input data. (Does not
encrypt the salt because decryption needs it.)</li>
<li>Returns a base64 encoded string consisting of the salt,
encrypted digest and encrypted input data.</li>
</ul>
<h2><a name="prefork" id="prefork"></a>Pre Forking SCGI
Server</h2>
<p>Usually when a web server receives a request for a CGI
resource, the web server executes a CGI program, which handles
the request and exits. This does not perform well under high
load. <a href=
"http://www.mems-exchange.org/software/scgi/">SCGI</a> (Simple
CGI) is a protocol for running a persistent CGI server. When a
web server receives a request for a SCGI resource, the web server
connects to a <i>SCGI Server</i> and forwards the request using
the SCGI protocol. The SCGI server responds to the web server,
which forwards the response back to the user's browser. This is
much more efficient than executing a CGI program for each
request. To configure the Apache httpd web server to use SCGI,
see the <a href=
"http://www.mems-exchange.org/software/scgi/">mod_scgi</a>
module.</p>
<p>Under high load a SCGI server must be able to handle multiple
requests concurrently. The SCGI server provided here <i>pre
forks</i> a specified number of child processes that all wait for
requests. The parent process monitors how many child processes
are busy and creates more if necessary. If too many processes are
idle, then the parent terminates some of them.</p>
<p>You provide the code that handles web requests in a
<i>callback</i> function, which is called once for each request.
The environment, standard input, and standard output are set up
so that your <i>callback</i> can be written very much like a
traditional CGI program. All the functions in the C CGI library
work as specified when using the SCGI server.</p>
<p>To start the SCGI server, call <a href=
"#CGI_prefork_server">CGI_prefork_server()</a> and pass it a
pointer to your <i>callback</i> function. The SCGI server puts
itself into the background, and forks child processes to handle
web requests. If <i>CGI_prefork_server()</i> returns, then it has
failed. The web server does not automatically start the SCGI
server program, so you must start it. You control which user
account runs the SCGI server and what privileges it has.</p>
<p>To terminate the SCGI server, send the <i>SIGTERM</i> signal
to the parent process and it sends the signal on to its child
processes and exits. The parent process writes its process ID in
a file if you pass the file name to <a href=
"#CGI_prefork_server">CGI_prefork_server()</a> in
<i>pidfile</i>.</p>
<p>The <i>callback</i> function operates very much like a
traditional CGI program, except that it gets called multiple
times. When writing your <i>callback</i> consider the
following.</p>
<ul>
<li>The <i>callback</i> function should not call <i>exit()</i>
(unless it encounters a serious error). You defeat the purpose
of a persistent process if you exit. The parent process
replaces exited children, so calling <i>exit()</i> does not
cause the SCGI server to fail.</li>
<li>Be careful to free memory that you have allocated and close
files that you have opened inside the <i>callback</i> function.
Otherwise the process will consume memory and/or file
descriptors with each call to <i>callback</i>, and eventually
fail. It may be too difficult to track down all memory leaks.
You can call <a href=
"#CGI_prefork_server">CGI_prefork_server()</a> with
<i>maxreq</i> greater than zero, which causes the child process
to exit after handling <i>maxreq</i> requests, which releases
all its resources.</li>
<li>You may need to handle initialization code differently.
Initializations made before you call
<i>CGI_prefork_server()</i> are inherited by all child
processes. If you open any files or sockets, then the
corresponding file descriptors are inherited and shared. You
may not get the behavior you want when multiple processes share
a file descriptor.</li>
<li>To initialize each child process individually, place
initialization code in <i>callback</i> inside an <i>if</i>
statement that executes the first time <i>callback</i> is
called. The <i>if</i> statement can test a <i>static</i>
variable and reset it to prevent executing the body of the
<i>if</i> statement again.</li>
<li>Each SCGI request includes a full set of environment
variables. The SCGI server replaces the entire environment with
these variables before each call to <i>callback</i>. If you
need anything from the original environment, then you should
save it before calling <i>CGI_prefork_server()</i>. If you need
to manipulate the environment, then the standard C function
<i>putenv()</i> allows you to add or modify environment
variables, and the global C variable
<i>extern&nbsp;char&nbsp;**environ;</i> is a pointer to the
current environment.</li>
<li>If you read standard input directly (rather than using
<i>CGI_get_all()</i> or <i>CGI_get_post()</i>) then use
<i>stdio</i> library functions. In particular you should not
use the <i>read()</i> system call because the SCGI server reads
environment data from standard input using <i>stdio</i> before
calling <i>callback</i>. Use <i>fread()</i> instead.</li>
<li>The SCGI server uses <i>syslog()</i> to log error messages.
If you do not want the default syslog parameters, then
initialize the logging system with <i>openlog()</i> before
calling <i>CGI_prefork_server()</i>. Use <i>syslog()</i> inside
<i>callback</i> to log any errors.</li>
</ul>
<p>Here is an example SCGI server.</p>
<pre>
#include &lt;ccgi.h&gt;
#include &lt;stdio.h&gt;
#include &lt;syslog.h&gt;
static void
cgi_callback() {
static int first_call = 1;
CGI_varlist *varlist;
if (first_call) {
first_call = 0;
/* initializations for each child process go here */
}
varlist = CGI_get_all(0);
fputs("Content-type: text/html\r\n\r\n", stdout);
/* write the rest of the web response to stdout */
/* free memory and close open files */
CGI_free_varlist(varlist);
}
int
main(int argc, char **argv) {
/* initializations before forking child processes go here */
openlog("my-scgi-server", 0, LOG_DAEMON);
CGI_prefork_server("localhost", 4000, "/var/run/my-scgi-server.pid",
/* maxproc */ 100, /* minidle */ 8, /* maxidle */ 16,
/* maxreq */ 1000, cgi_callback);
/* if CGI_prefork_server() returns, then it failed */
return 0;
}
</pre>
</body>
</html>
+6
View File
@@ -0,0 +1,6 @@
CFLAGS = -I ..
dump.cgi: dump.cgi.o
$(CC) -o dump.cgi dump.cgi.o -L .. -lccgi
dump.cgi.o: dump.cgi.c
+9
View File
@@ -0,0 +1,9 @@
dump.html is a sample web form that POSTs to dump.cgi.
dump.cgi displays all the form data and uploads
files to /tmp/cgi-upload-*
To build dump.cgi: make
Install dump.cgi and dump.html on a web server and
browse dump.html.
+34
View File
@@ -0,0 +1,34 @@
#include <stdio.h>
#include <ccgi.h>
int
main(int argc, char **argv, char **env) {
CGI_varlist *vl;
const char *name;
CGI_value *value;
int i;
fputs("Content-type: text/plain\r\n\r\n", stdout);
fputs("Environment:\r\n", stdout);
for (i = 0; env[i] != 0; i++) {
fputs(env[i], stdout);
fputs("\r\n", stdout);
}
if ((vl = CGI_get_all("/tmp/cgi-upload-XXXXXX")) == 0) {
fputs("CGI_get_all() failed\r\n", stdout);
return 0;
}
fputs("\r\nCGI Variables:\r\n", stdout);
for (name = CGI_first_name(vl); name != 0;
name = CGI_next_name(vl))
{
value = CGI_lookup_all(vl, 0);
for (i = 0; value[i] != 0; i++) {
printf("%s [%d] >>%s<<\r\n", name, i, value[i]);
}
}
CGI_free_varlist(vl);
return 0;
}
+73
View File
@@ -0,0 +1,73 @@
<html>
<head>
<title>C CGI Test Form</title>
<style>
.middle {
vertical-align: middle;
}
</style>
</head>
<body>
<h2>C CGI Library Test Form</h2>
<form method="POST"
action="dump.cgi?qvar1=q+val+1&amp;qvar2=q+val+2&amp;qvar3=q+val+3"
enctype="multipart/form-data">
<p>
Text Input: <input type="text" name="textvar1" />
</p>
<p>
File Uploads:
</p>
<p>
File 1:<input type="file" name="upload1" /><br />
File 2:<input type="file" name="upload2" />
</p>
<p>
Check Boxes:
cb1<input type="checkbox" name="cbvar1" value="cb #1" checked="checked" />
cb2<input type="checkbox" name="cbvar1" value="cb #2" />
cb3<input type="checkbox" name="cbvar1" value="cb #3" checked="checked" />
cb4<input type="checkbox" name="cbvar1" value="cb #4" />
</p>
<p>
Radio Buttons:
rb1<input type="radio" name="rbvar1" value="rb #1" />
rb2<input type="radio" name="rbvar1" value="rb #2" />
rb3<input type="radio" name="rbvar1" value="rb #3" checked="checked" />
rb4<input type="radio" name="rbvar1" value="rb #4" />
NONE: <input type="radio" name="rbvar1" value="none" />
</p>
<p>
Selection:
<select class="middle" multiple name="selvar1">
<option value="sel val #1" selected="selected">Select Value 1</option>
<option value="sel val #2">Select Value 2</option>
<option value="sel val #3" selected="selected">Select Value 3</option>
<option value="sel val #4" selected="selected">Select Value 4</option>
<option value="sel val #5">Select Value 5</option>
<option value="sel val #6">Select Value 6</option>
<option value="sel val #7" selected="selected">Select Value 7</option>
</select>
</p>
<p>
Text Area:
<textarea class="middle" name="textarea1" rows="10" cols="40">Initial Line 1
Initial Line 2</textarea>
</p>
<p>
<input type="submit" name="submitbutton" value="Submit Form" />
<input type="hidden" name="hidden"
value="This is three&#13;&#10;lines of hidden&#13;&#10;form data" />
</p>
</form>
</body>
</html>
+468
View File
@@ -0,0 +1,468 @@
/*
* C CGI Library version 1.2
*
* Author: Stephen C. Losen, University of Virginia
*
* Copyright 2015 Stephen C. Losen. Distributed under the terms
* of the GNU Lesser General Public License (LGPL 2.1)
*
* CGI_prefork_server() is a pre forking SCGI server that handles web
* requests by calling a callback function provided by the user.
* The parent process forks a configurable number of child processes
* to accept web requests.
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <syslog.h>
#include <stdio.h>
#define SCORE_MAX_PROC 10000
#define SCORE_IDLE 1
#define SCORE_BUSY 2
/* scoreboard structs to keep track of child process state */
struct score_state {
pid_t pid;
int state;
};
struct score_board {
int numproc;
int numidle;
int minidle;
int maxidle;
int maxproc;
struct score_state proc[1];
};
/* score_new() creates new scoreboard */
static struct score_board *
score_new(int maxproc, int minidle, int maxidle) {
struct score_board *scb;
int size = sizeof(*scb) + maxproc * sizeof(scb->proc);
scb = (struct score_board *) malloc(size);
memset(scb, 0, size);
scb->maxproc = maxproc;
scb->minidle = minidle;
scb->maxidle = maxidle;
return scb;
}
/* score_find() finds process entry and returns its index */
static int
score_find(struct score_board *scb, pid_t pid) {
int i;
for (i = 0; i < scb->maxproc; i++) {
if (scb->proc[i].pid == pid) {
return i;
}
}
return -1;
}
/* score_add() adds a new pid to the scoreboard */
static void
score_add(struct score_board *scb, pid_t pid) {
int i = score_find(scb, 0);
if (i >= 0) {
scb->proc[i].pid = pid;
scb->proc[i].state = SCORE_IDLE;
scb->numproc++;
scb->numidle++;
}
}
/* score_remove() removes a pid from the scoreboard */
static void
score_remove(struct score_board *scb, pid_t pid) {
int i = score_find(scb, pid);
if (i >= 0) {
scb->proc[i].pid = 0;
scb->numproc--;
if (scb->proc[i].state == SCORE_IDLE) {
scb->numidle--;
}
}
}
/* score_update() changes pid state to busy or idle */
static void
score_update(struct score_board *scb, struct score_state *st) {
int i = score_find(scb, st->pid);
if (i >= 0 && scb->proc[i].state != st->state) {
scb->proc[i].state = st->state;
if (st->state == SCORE_IDLE) {
scb->numidle++;
}
else {
scb->numidle--;
}
}
}
/* score_kill() finds an idle process and kills it */
static void
score_kill(struct score_board *scb) {
int i;
for (i = 0; i < scb->maxproc; i++) {
if (scb->proc[i].pid != 0 && scb->proc[i].state == SCORE_IDLE) {
kill(scb->proc[i].pid, SIGTERM);
return;
}
}
}
/*
* setup_sock() opens a TCP socket, binds it to a local address
* specified by host and port, calls listen(), and returns the
* socket.
*/
static int
setup_sock(const char *host, int port) {
struct sockaddr_in local;
struct hostent *h;
int sock;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
if (host == 0 || *host == 0) {
local.sin_addr.s_addr = INADDR_ANY;
}
else if (!isdigit(host[0]) ||
inet_aton(host, &local.sin_addr) == 0)
{
if ((h = gethostbyname(host)) != 0) {
memcpy(&local.sin_addr, h->h_addr_list[0], h->h_length);
}
else {
local.sin_addr.s_addr = INADDR_ANY;
}
}
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
return sock;
}
if (bind(sock, (struct sockaddr *) &local, sizeof(local)) < 0 ||
listen(sock, 5) < 0)
{
close(sock);
return -1;
}
return sock;
}
/*
* read_env() reads the environment from the SCGI client (httpd).
* We read the length of the environment, which is an ASCII decimal
* string terminated by ':'. We read the environment, which
* is a sequence of null terminated strings where each pair of strings
* is an environment variable name and value. After the environment
* is a comma. (Any remaining input is POST data, which we leave
* unread.) We replace the null between each variable name and value
* with '=' and we return an array of char * pointers to the
* environment variable strings.
*/
static char **
read_env(void) {
char **env;
char *buf, *p;
int i, k, len;
/* read length of environment data, eg, "175:" */
for (len = 0; isdigit(k = getc(stdin)); ) {
len = len * 10 + k - '0';
}
if (k != ':' || len < 4) {
return 0;
}
/* read the environment strings */
buf = (char *) malloc(len);
if (fread(buf, 1, len, stdin) != len || getc(stdin) != ',') {
free(buf);
return 0;
}
/* replace null between each name and value with '=' */
for (i = k = 0; i < len; i++) {
if (buf[i] == 0 && (++k & 1)) {
buf[i] = '=';
}
}
/* build environment array */
k = k / 2 + 1;
env = (char **) malloc(k * sizeof(*env));
p = buf;
for (i = k = 0; i < len; i++) {
if (buf[i] == 0) {
env[k++] = p;
p = buf + i + 1;
}
}
env[k] = 0;
return env;
}
/* terminate() sets a flag when we get SIGTERM */
static int terminate_flag = 0;
static void
terminate(int sig) {
terminate_flag = 1;
}
/* child_handler() sets a flag when a child exits */
static int child_exited = 0;
static void
child_handler(int sig) {
child_exited = 1;
}
/*
* set_handler() sets a signal handler. We use this instead of
* signal() because it is more portable and system calls tend to
* be restarted when signal() is used. In particular we need
* read() to return if we catch a signal.
*/
static int
set_handler(int sig, void (*handler)(int sig)) {
struct sigaction action;
action.sa_handler = handler;
action.sa_flags = 0;
sigemptyset(&action.sa_mask);
return sigaction(sig, &action, 0);
}
/*
* CGI_prefork_server() pre forks child processes to handle SCGI requests.
* The parent process opens a TCP socket that listens on a local
* address specified by "host" and "port". Each child handles "maxreq"
* requests and exits. If maxreq <= 0 then maxreq is unlimited. The
* parent process creates or destroys child processes to maintain between
* "minidle" and "maxidle" idle children, and no more than "maxproc"
* total children. To handle a request, a child 1) accepts a connection
* on the listen socket from the SCGI client, 2) redirects stdin and
* stdout to the socket, 3) reads the environment from the SCGI client,
* 4) calls the "callback" function, 5) closes the socket and restores
* the environment. Child processes notify the parent when they
* become busy or idle by writing messages to a pipe.
*/
void
CGI_prefork_server(const char *host, int port, const char *pidfile,
int maxproc, int minidle, int maxidle, int maxreq,
void (*callback)(void))
{
int i, sock, fd;
struct score_state message;
struct score_board *scb;
pid_t pid;
FILE *fp;
int pfd[2];
char **realenv, **tmpenv;
char *tmpbuf;
extern char **environ;
/* sanity check arguments */
if (callback == 0) {
syslog(LOG_ERR, "CGI_prefork_server(): null callback "
"function pointer");
return;
}
if (minidle <= 0) {
minidle = 2;
}
if (maxidle <= minidle) {
maxidle = minidle + 2;
}
if (maxproc <= 0) {
maxproc = maxidle;
}
if (maxproc > SCORE_MAX_PROC) {
maxproc = SCORE_MAX_PROC;
}
syslog(LOG_INFO, "CGI_prefork_server(): maxproc = %d, minidle = %d, "
"maxidle = %d, maxreq = %d", maxproc, minidle, maxidle, maxreq);
/* parent puts self into the background */
if (fork() != 0) {
_exit(0);
}
setsid();
set_handler(SIGTERM, terminate);
set_handler(SIGCHLD, child_handler);
freopen("/dev/null", "r", stdin);
freopen("/dev/null", "w", stdout);
/* write our pid to pidfile */
if (pidfile != 0 && *pidfile != 0 &&
(fp = fopen(pidfile, "w")) != 0)
{
fprintf(fp, "%d\n", getpid());
fclose(fp);
}
/* create child process scoreboard */
scb = score_new(maxproc, minidle, maxidle);
/* parent opens the listen socket, children accept() connections */
if ((sock = setup_sock(host, port)) < 0) {
syslog(LOG_ERR, "CGI_prefork_server(): setup_sock() failed: %m");
return;
}
/* open pipe to receive messages from child processes */
pipe(pfd);
/* parent manages child processes */
for (;;) {
/* fork child if necessary */
if (scb->numidle < scb->minidle && scb->numproc < scb->maxproc) {
if ((pid = fork()) == 0) {
break;
}
else if (pid > 0) {
score_add(scb, pid);
continue;
}
else {
syslog(LOG_ERR, "CGI_prefork_server(): fork() failed: %m");
if (scb->numproc == 0) {
return;
}
}
}
/*
* read status message from child. The read() call returns with
* an error if we catch SIGCHLD or SIGTERM.
*/
if (child_exited == 0 && terminate_flag == 0 &&
read(pfd[0], &message, sizeof(message)) == sizeof(message))
{
score_update(scb, &message);
}
/* kill everything and exit if we got SIGTERM */
if (terminate_flag != 0) {
set_handler(SIGTERM, SIG_IGN);
kill(0, SIGTERM); /* kill process group */
while(wait(0) >= 0)
;
exit(0);
}
/* kill idle child if necessary */
if (scb->numidle > scb->maxidle) {
score_kill(scb);
}
/* wait for exited children */
child_exited = 0;
while ((pid = waitpid(-1, 0, WNOHANG)) > 0) {
score_remove(scb, pid);
}
}
/* child handles maxreq requests and exits */
set_handler(SIGTERM, SIG_DFL);
set_handler(SIGCHLD, SIG_DFL);
close(pfd[0]);
message.pid = getpid();
realenv = environ;
for (i = 0; i < maxreq || maxreq <= 0; i++) {
/* accept connection from SCGI client (httpd) */
if ((fd = accept(sock, 0, 0)) < 0) {
syslog(LOG_ERR, "CGI_prefork_server(): accept() failed: %m");
break;
}
/* notify parent we are busy */
message.state = SCORE_BUSY;
write(pfd[1], &message, sizeof(message));
/* redirect stdin and stdout to socket */
dup2(fd, 0);
dup2(fd, 1);
close(fd);
/* read environment and call callback */
if ((tmpenv = read_env()) != 0) {
tmpbuf = tmpenv[0];
environ = tmpenv;
callback();
}
else {
fputs("Content-type: text/plain\r\n\r\n"
"CGI_prefork_server() could not read environment.\r\n",
stdout);
syslog(LOG_ERR, "CGI_prefork_server(): could not read "
"environment");
}
/* close socket and restore environment */
freopen("/dev/null", "r", stdin); /* closes socket */
freopen("/dev/null", "w", stdout);
environ = realenv;
if (tmpenv != 0) {
free(tmpbuf);
free(tmpenv);
}
/* notify parent we are idle */
message.state = SCORE_IDLE;
write(pfd[1], &message, sizeof(message));
}
_exit(0);
}
+14
View File
@@ -0,0 +1,14 @@
# set OPENSSL_LIB to the directory containing openssl libcrypto
OPENSSL_LIB = /usr/lib
CFLAGS = -I ..
LDFLAGS = -L .. -L $(OPENSSL_LIB)
all: test.sh test
./test.sh
test: test.o ../libccgi.a
$(CC) -o test $(LDFLAGS) test.o -lccgi -lcrypto
test.o: test.c ../ccgi.h
+141
View File
@@ -0,0 +1,141 @@
#include <string.h>
#include <stdio.h>
#include <ccgi.h>
static void
dumplist(CGI_varlist *vl) {
const char *name;
CGI_value *value;
int i;
for (name = CGI_first_name(vl); name != 0;
name = CGI_next_name(vl))
{
value = CGI_lookup_all(vl, 0);
for (i = 0; value[i] != 0; i++) {
printf("%s [%d] >>%s<<\n", name, i, value[i]);
}
}
}
int
main(int argc, char **argv) {
CGI_varlist *vl;
char *p;
CGI_value *value;
int i;
if (strcmp(argv[1], "CGI_get_cookie") == 0) {
vl = CGI_get_cookie(0);
dumplist(vl);
return 0;
}
if (strcmp(argv[1], "CGI_get_query") == 0) {
vl = CGI_get_query(0);
dumplist(vl);
return 0;
}
if (strcmp(argv[1], "CGI_decode_query") == 0) {
vl = CGI_decode_query(0, argv[2]);
dumplist(vl);
return 0;
}
if (strcmp(argv[1], "CGI_get_post") == 0) {
vl = CGI_get_post(0, 0);
dumplist(vl);
return 0;
}
if (strcmp(argv[1], "upload") == 0) {
vl = CGI_get_post(0, "./cgi-upload-XXXXXX");
value = CGI_lookup_all(vl, "upload");
dumplist(vl);
return 0;
}
if (strcmp(argv[1], "CGI_add_var") == 0) {
vl = 0;
for (i = 2; i + 1 < argc; i += 2) {
vl = CGI_add_var(vl, argv[i], argv[i + 1]);
}
dumplist(vl);
return 0;
}
if (strcmp(argv[1], "CGI_encode_varlist") == 0) {
vl = 0;
for (i = 2; i + 1 < argc; i += 2) {
vl = CGI_add_var(vl, argv[i], argv[i + 1]);
}
dumplist(vl);
p = CGI_encode_varlist(vl, ".-_");
fputs(p, stdout);
fputc('\n', stdout);
return 0;
}
if (strcmp(argv[1], "CGI_encode_url") == 0) {
p = CGI_encode_url(argv[2], ".-_");
fputs(p, stdout);
fputc('\n', stdout);
p = CGI_decode_url(p);
fputs(p, stdout);
fputc('\n', stdout);
return 0;
}
if (strcmp(argv[1], "CGI_encode_query") == 0) {
p = CGI_encode_query(".-_", argv[2], argv[3], argv[4], argv[5],
argv[6], argv[7], argv[8], argv[9], argv[10], argv[11],
argv[12], argv[13], argv[14], argv[15], argv[16], argv[17],
argv[18], argv[19], argv[20], argv[21], (char *)0);
fputs(p, stdout);
fputc('\n', stdout);
vl = CGI_decode_query(0, p);
dumplist(vl);
return 0;
}
if (strcmp(argv[1], "CGI_encode_base64") == 0) {
p = argv[2];
p = CGI_encode_base64(p, strlen(p));
fputs(p, stdout);
fputc('\n', stdout);
p = (char *) CGI_decode_base64(p, &i);
fwrite(p, i, 1, stdout);
fputc('\n', stdout);
return 0;
}
if (strcmp(argv[1], "CGI_encode_hex") == 0) {
p = argv[2];
p = CGI_encode_hex(p, strlen(p));
fputs(p, stdout);
fputc('\n', stdout);
p = (char *) CGI_decode_hex(p, &i);
fwrite(p, i, 1, stdout);
fputc('\n', stdout);
return 0;
}
if (strcmp(argv[1], "CGI_encode_entity") == 0) {
p = CGI_encode_entity(argv[2]);
fputs(p, stdout);
fputc('\n', stdout);
return 0;
}
if (strcmp(argv[1], "CGI_encrypt") == 0) {
const char *pw = "This is my C CGI test password";
p = argv[2];
p = CGI_encrypt(p, strlen(p), pw);
printf("enc len = %d\n", strlen(p));
p = CGI_decrypt(p , &i, pw);
printf("dec len = %d %s\n", i, p);
return 0;
}
return 0;
}
+337
View File
@@ -0,0 +1,337 @@
#! /bin/sh
PATH=/bin:/usr/bin
# Regression tests for the CGI library
# Usage: ./test.sh [ -r ]
# The -r option causes the script to edit itself and renumber
# the tests in case any are added or removed.
# renumber the tests in this script
renumber() {
cp $0 $0.old &&
awk '/^TEST=[0-9].* ######/ {
printf("TEST=%d ########################################\n", ++i);
next }
{ print }' $0.old > $0 &&
/bin/rm $0.old
exit
}
check () {
echo
if diff -c expected result; then
echo "Test $TEST success"
else
echo "Test $TEST failure"
fi
}
test "X$1" = X-r && renumber
TEST=1 ########################################
# Testing CGI_get_query
Q='one=This+is+a+test&two=%22Bob+Smith%22+%3Cbob%40gmail.com%3E&'
Q=$Q'varnovalue&eq=un=encoded=eq==&one=second+value&the+end'
QUERY_STRING=$Q ./test CGI_get_query > result 2>&1
cat > expected <<'E-O-F'
one [0] >>This is a test<<
one [1] >>second value<<
two [0] >>"Bob Smith" <bob@gmail.com><<
varnovalue [0] >><<
eq [0] >>un=encoded=eq==<<
the end [0] >><<
E-O-F
check
TEST=2 ########################################
# Testing CGI_get_post
CONTENT_TYPE=application/x-www-form-urlencoded \
CONTENT_LENGTH=60 \
./test CGI_get_post > result 2>&1 <<'E-O-F'
one=This+is+a+test&two=%22Bob+Smith%22+%3Cbob%40gmail.com%3E
E-O-F
cat > expected <<'E-O-F'
one [0] >>This is a test<<
two [0] >>"Bob Smith" <bob@gmail.com><<
E-O-F
check
TEST=3 ########################################
# Testing CGI_get_post multipart/form-data
CONTENT_TYPE='multipart/form-data; boundary=----12345' \
./test CGI_get_post > result 2>&1 <<'E-O-F'
------12345
Content-Disposition: form-data; name="textvar1"
------12345
Content-Disposition: form-data; name="doublevar"
Text data here
------12345
Content-Disposition: form-data; name="cbvar1"
cb #1
------12345
Content-Disposition: form-data; name="cbvar1"
cb #3
------12345
Content-Disposition: form-data; name="rbvar1"
rb #3
------12345
Content-Disposition: form-data; name="selvar1"
selval1
------12345
Content-Disposition: form-data; name="selvar1"
selval3
------12345
Content-Disposition: form-data; name="selvar1"
selval4
------12345
Content-Disposition: form-data; name="selvar1"
selval7
------12345
Content-Disposition: form-data; name="textarea1"
Initial Line 1
Initial Line 2
Added Line
Another Line
------12345
Content-Disposition: form-data; name="doublevar"
OK
------12345
Content-Disposition: form-data; name="hidden"
This is three
lines of hidden
form data
------12345--
E-O-F
cat > expected <<'E-O-F'
textvar1 [0] >><<
doublevar [0] >>Text data here<<
doublevar [1] >>OK<<
cbvar1 [0] >>cb #1<<
cbvar1 [1] >>cb #3<<
rbvar1 [0] >>rb #3<<
selvar1 [0] >>selval1<<
selvar1 [1] >>selval3<<
selvar1 [2] >>selval4<<
selvar1 [3] >>selval7<<
textarea1 [0] >>Initial Line 1
Initial Line 2
Added Line
Another Line<<
hidden [0] >>This is three
lines of hidden
form data<<
E-O-F
check
TEST=4 ########################################
# Testing CGI_get_cookie
HTTP_COOKIE='cookie1=cookie1data; cookie2=cookie2data; cookie3=more-stuff;' \
./test CGI_get_cookie > result 2>&1
cat > expected <<'E-O-F'
cookie1 [0] >>cookie1data<<
cookie2 [0] >>cookie2data<<
cookie3 [0] >>more-stuff<<
E-O-F
check
TEST=5 ########################################
# Testing upload
/bin/rm -f cgi-upload-??????
CONTENT_TYPE='multipart/form-data; boundary=----12345' \
./test upload > result 2> result <<'E-O-F'
------12345
Content-Disposition: form-data; name="var1"
Value # 1
------12345
Content-Disposition: form-data; name="upload"; filename="input"
This is a test uploaded file
Here is the second line.
The quick brown fox jumped over the lazy dog
(or was that a hog?)
Now is the time for
all good men
to come to the aid of
their party!
------12345--
E-O-F
F=` echo ./cgi-upload-?????? `
cat $F >> result
/bin/rm -f $F
cat > expected << E-O-F
var1 [0] >>Value # 1<<
upload [0] >>$F<<
upload [1] >>input<<
This is a test uploaded file
Here is the second line.
The quick brown fox jumped over the lazy dog
(or was that a hog?)
Now is the time for
all good men
to come to the aid of
their party!
E-O-F
check
TEST=6 ########################################
# Testing CGI_add_var
./test CGI_add_var one 'first value' two 'value # 2' \
three '3rd-var' two 200% > result 2>&1
cat > expected <<'E-O-F'
one [0] >>first value<<
two [0] >>value # 2<<
two [1] >>200%<<
three [0] >>3rd-var<<
E-O-F
check
TEST=7 ########################################
# Testing CGI_encode_varlist
./test CGI_encode_varlist one 'first value' two 'value # 2' \
three '3rd-var' two 200% > result 2>&1
cat > expected <<'E-O-F'
one [0] >>first value<<
two [0] >>value # 2<<
two [1] >>200%<<
three [0] >>3rd-var<<
one=first+value&two=value+%23+2&two=200%25&three=3rd-var
E-O-F
check
TEST=8 ########################################
# Testing CGI_encode_query
./test CGI_encode_query one 'first value' two 'value # 2' \
three '3rd-var' two 200% > result 2>&1
cat > expected <<'E-O-F'
one=first+value&two=value+%23+2&three=3rd-var&two=200%25
one [0] >>first value<<
two [0] >>value # 2<<
two [1] >>200%<<
three [0] >>3rd-var<<
E-O-F
check
TEST=9 ########################################
# Testing CGI_encode_url
./test CGI_encode_url '/etc/passwd holds Unix(TM) accounts.' > result 2>&1
cat > expected <<'E-O-F'
%2Fetc%2Fpasswd+holds+Unix%28TM%29+accounts.
/etc/passwd holds Unix(TM) accounts.
E-O-F
check
TEST=10 ########################################
# Testing CGI_encode_entity
./test CGI_encode_entity \
'if (i > 20 && i < 200) {
fputs("OK\n", stdout);
}' > result 2>&1
cat > expected <<'E-O-F'
if (i &gt; 20 &amp;&amp; i &lt; 200) {&#10; fputs(&quot;OK\n&quot;, stdout);&#10;}
E-O-F
check
TEST=11 ########################################
# Testing CGI_encode_base64
./test CGI_encode_base64 'Now is the time for the quick brown fox, ...' \
> result 2>&1
cat > expected <<'E-O-F'
Tm93IGlzIHRoZSB0aW1lIGZvciB0aGUgcXVpY2sgYnJvd24gZm94LCAuLi4=
Now is the time for the quick brown fox, ...
E-O-F
check
TEST=12 ########################################
# Testing CGI_encode_hex
./test CGI_encode_hex 'Now is the time for the quick brown fox, ...' \
> result 2>&1
cat > expected <<'E-O-F'
4E6F77206973207468652074696D6520666F722074686520717569636B2062726F776E20666F782C202E2E2E
Now is the time for the quick brown fox, ...
E-O-F
check
TEST=13 ########################################
# Testing CGI_encrypt
./test CGI_encrypt 'Now is the time for the quick brown fox, ...' \
> result 2>&1
cat > expected <<'E-O-F'
enc len = 120
dec len = 44 Now is the time for the quick brown fox, ...
E-O-F
check
# clean up
/bin/rm -f result expected cgi-upload-??????
+45
View File
@@ -0,0 +1,45 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAwV5xaK7ez2/TX7vSgJ0a3YbZj2l1VQ2rMzqO1Id01xlWbF/U
rebwhAdVtWcT9R6RaBTPDGaILkV38u77M2BxIHX4MSnR6WezoA2bGMgvt3+tq2N6
q+xkj57vwBEqedBjscVtFkoWtsX8pKwtNlMB1NvTa8p5+BNsvpvzaDX+51+FotId
wvieQfQYgFg36HpOtOyyIV31rZ/5+qtoce8gU6wApHxmovTnQPoduNM6fOUJCHDd
Lz90EeBREtoTVgoWcKvQoCEwJQSBmeDZgkA8Q1OYmbYoS12tIyi8rTkseRj5BvPH
iXfNmHFKliAjvlsml5qI44I9DoagPubTf6qR5wIDAQABAoIBACZ6VZTgH0Ql22jU
ZhnjqUHloIsyEAABvUxvXZaa8bwPtavREfAc4UVUdFCpl0YSdBrC8URlbrnOZwT3
WxMpILm139JgoP2R/iNeMbunsh8QkA1nuTRW0NfnZ4vPnqUou33XbFKgIY7zLMfT
3xdNQzMJHzP20Xh03RG81J2rCPMfLScTRo2XxcSxmhhS/p2WLk6pnmMHiNgYGGwX
gcdK5lIVjMMNxgcltC30x90v0o0GDRM8/+wua+/vfn8rr3iudv9IHzL8xIzpi6NY
CXJ8Kxd6Jtgsr3Boj5i6Mqi3Q/Trxt+rIA4bKAFXxwcp4+GmRIJtQFFiTWXpLCPC
tLT4CHECgYEA7iCbrGjWHJ4QsUWUGrGlw1/sQ0SIv9BdZm8RydHzpRVtQOi+YOuU
i6raVaXWzUBKgKcs/htVjAMTiePs/yhlU/MGXivz6uTX/nrD7ISJImmK2K50hgUe
+UBnFKmBMVaNxD9RFWPJkfmNXfW7nBkqSa9CxlBcYPuOcPtZDqRl+gkCgYEAz+HX
8wh3SHKb1cAI+o4caclpUTpGa9/zW4k+7gOh72WCKaqxTNvBvNyZGdXc9t5ToDSf
xxsDXWG10lcHBIGLj4QBEoSWp9I43lid5swY3mCo7CjTl+1l03IfDNaC6CYQFp5p
ZnKlsQUwR38t/uiyZpnnicCAZjqIfJbeQ5jD6G8CgYB8ufmwQa08ihJmN/KOVNRl
VF31EfWquqHhYHXpxx2eL23tXLszGtHQoioASIANPAqJ/oaTho+1aXsXc5oUP/1r
DlUciFsXgswb0APFY9pMewmt2xrPg+koVvJnIS25QQO6cguvb3gKDLNeLrMY3RmI
RNNt+nOYnMqMJSsNf1CmuQKBgQCiCZxWaCbyZcNqncFh7BvhqYlaM15o/6ulkhln
VZWIEUugRtjk2/bry9fa94TBORNeMSbKABhjVaJwTj2+GWw7dd2QHaGBNq/1QIX0
POq1jAqf6kLkjbttUes6CosHgYPQ3bGylXLpxO2ZDV1A8Qj+SMDd8xsilEWHN+IQ
NqeeKQKBgQDe4c7VVG+WvRRKshTh8+tjzc9nXKE2AWgwnw729SMFZO/WqX2FPp2C
7C99XJTVBsCBy8VzuyaojeTKkag0YL3v6UTZYUeyu0YTHGQ33WVPaqdCAo840nmG
ttwHVqshB9c67HHiYOOFt1VmT3xW6x6yympUyRqR0L+BZ1wOS3h2vQ==
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIC6DCCAdACBRQJQlZlMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNVBAMTAm5zMQsw
CQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA4
MzAxOTA3NDRaFw0yNDA4MjcxOTA3NDRaMDgxCzAJBgNVBAMTAm5zMQswCQYDVQQK
EwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkdhbHdheTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAMFecWiu3s9v01+70oCdGt2G2Y9pdVUNqzM6jtSH
dNcZVmxf1K3m8IQHVbVnE/UekWgUzwxmiC5Fd/Lu+zNgcSB1+DEp0elns6ANmxjI
L7d/ratjeqvsZI+e78ARKnnQY7HFbRZKFrbF/KSsLTZTAdTb02vKefgTbL6b82g1
/udfhaLSHcL4nkH0GIBYN+h6TrTssiFd9a2f+fqraHHvIFOsAKR8ZqL050D6HbjT
OnzlCQhw3S8/dBHgURLaE1YKFnCr0KAhMCUEgZng2YJAPENTmJm2KEtdrSMovK05
LHkY+Qbzx4l3zZhxSpYgI75bJpeaiOOCPQ6GoD7m03+qkecCAwEAATANBgkqhkiG
9w0BAQUFAAOCAQEAJ+wZ/IgAF5LIu0yOfJlaFRJLunKHZENigiVjYvkTdM7NI3O2
1AZGY4O8H5Fs3YT5ZY3vas/n6IwWTk3o/JSPXojMFo82XkbI1k2cm3oLtwgEGN3p
s5yFsjZE3H7fQJ9wHIzESBPHFY6dwwgMsNENuAM2zkwFpbAkisKhjK+EyUCXauok
7zJY6RVPMaNojsje4iE/SBtSOnK/9WDBAgpCznHrSChJmKs4FsU7ZTO+Dg+0vQln
l8/yBcEGAFe0GA2D9NvZKH5IoNmitvtU9zdNDK4dzC3Q+C28IjW5jE8peDFtdGs1
P0u4kRxmb4UH1DchgoWlZjL2lSFScJ7L4xY2aQ==
-----END CERTIFICATE-----
+1
View File
@@ -0,0 +1 @@
hi
+1
View File
@@ -0,0 +1 @@
joe:foo.com:77bbd68c7bc5cd7b6bb33a19e2fc7007
+1
View File
@@ -0,0 +1 @@
hi
View File
Whitespace-only changes.
View File
Whitespace-only changes.
+1
View File
@@ -0,0 +1 @@
foo
+1
View File
@@ -0,0 +1 @@
:-)
+12
View File
@@ -0,0 +1,12 @@
Http = {
onrcv: function(c, req) {
if (req.uri === '/data/js') {
// Send reply. We need to construct everything, headers and body
mg_send(c, 'HTTP/1.0 200 OK\r\n\r\n');
mg_send(c, JSON.stringify(req));
// Let Mongoose know we've handled the connection
return true;
}
}
}
+13
View File
@@ -0,0 +1,13 @@
------WebKitFormBoundaryh3pBS3C5nni6XxR4
Content-Disposition: form-data; name="file1"; filename="keyword.c"
Content-Type: application/octet-stream
file1 data
------WebKitFormBoundaryh3pBS3C5nni6XxR4
Content-Disposition: form-data; name="file2"; filename="stdarg.c"
Content-Type: application/octet-stream
file2 data
------WebKitFormBoundaryh3pBS3C5nni6XxR4--
+9
View File
@@ -0,0 +1,9 @@
Faith of consciousness is freedom
Faith of feeling is weakness
Faith of body is stupidity.
Love of consciousness evokes the same in response
Love of feeling evokes the opposite
Love of body depends only on type and polarity.
Hope of consciousness is strength
Hope of feelings is slavery
Hope of body is disease.
+1
View File
@@ -0,0 +1 @@
foo_root
+1
View File
@@ -0,0 +1 @@
works
+1
View File
@@ -0,0 +1 @@
a
+1
View File
@@ -0,0 +1 @@
<!--#include file="nested.shtml" -->
+3
View File
@@ -0,0 +1,3 @@
<!--#include file="f1.txt"-->
<b><!--#call foo(bar) --></b>
<!--#exec "echo b"-->
+85
View File
@@ -0,0 +1,85 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ccgi.h>
extern char **environ;
#ifdef _WIN32
#define EOL "\n"
#else
#define EOL "\r\n"
#endif
int alpha_cmp(const void *a, const void *b) {
return strcmp(*(const char **) a, *(const char **) b);
}
int main(void) {
puts("Content-Type: text/html" EOL "Status: 201 Created" EOL EOL);
puts("<pre>" EOL "<h1>Environment</h1>" EOL);
{
const char *sorted_env[500];
size_t i, num_env;
for (num_env = 0; environ[num_env] != 0; num_env++) {
sorted_env[num_env] = environ[num_env];
}
qsort(sorted_env, num_env, sizeof(const char *), alpha_cmp);
for (i = 0; i < num_env; i++) {
printf("E: %s" EOL, sorted_env[i]);
}
}
puts(EOL "<h1>Query string</h1>" EOL);
{
const char *k;
CGI_varlist *vl = CGI_get_query(NULL);
for (k = CGI_first_name(vl); k != NULL; k = CGI_next_name(vl)) {
printf("Q: %s=%s" EOL, k, CGI_lookup(vl, k));
}
CGI_free_varlist(vl);
}
puts(EOL "<h1>Form variables</h1>" EOL);
{
const char *k;
CGI_varlist *vl = CGI_get_post(NULL, NULL);
for (k = CGI_first_name(vl); k != NULL; k = CGI_next_name(vl)) {
printf("P: %s=%s" EOL, k, CGI_lookup(vl, k));
}
CGI_free_varlist(vl);
}
puts(EOL "</pre>" EOL);
return 0;
}
/* Some functions for libccgi that are missing on Windows (VC6). */
#ifdef _WIN32
static int lowercase(const char *s) {
return tolower(*(const unsigned char *) s);
}
int strncasecmp(const char *s1, const char *s2, size_t len) {
int diff = 0;
if (len > 0) do {
diff = lowercase(s1++) - lowercase(s2++);
} while (diff == 0 && s1[-1] != '\0' && --len > 0);
return diff;
}
int strcasecmp(const char *s1, const char *s2) {
return strncasecmp(s1, s2, (size_t) ~0);
}
int mkstemp(char *template) {
return -1; /* Not used by us. */
}
#endif
+45
View File
@@ -0,0 +1,45 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1mONQ0hAXOL9lb15Pz4fqXXNHREsF3a7/NoMJdQDclx0+a32
MhuHcO6R7Fhsc0mZMuzbmAFLMmIIgXPMKQBZLoA12yCBlZPyKFoWUhFrLa3gUjO6
CZlBKqkUVEACpVrQ41ihapeeUHa0uryt3tXwMn2/853yzi1uciGYi4ULTy3yTE/n
qRIVJLiBDSC9WNFLg26f/W4YRW7tANOk2b/W/Ws9B/n7vNDgHG7Lpd38YTpFhhXT
n3xlt/VcczkQhW79Moh6/lY6sLg6H15EjHKHeTn8t9BRm+qYi/CvC258YF/Qz/qK
agSsLT/3FrQ6+aQgg/Eyao0IWAql49PQNxuwPQIDAQABAoIBAQC5y3S1BnyhAyb5
Ckd1g4U0+x5TPnqTqxanvuAgOGj0RyQo7ZYbPrhWKqrTxJ3YG8Rk2dhFF3nvo/3z
EkOwlNi07++8g6NJ2flW9xu469eSsslg8+saPnK3Yeh4SzD/1ICLRlg9ZECTQwzF
eJbGM2oCl/AuVIgEHmNFDdCBuT9f0b7j3/Z3aK3lKzqzBYQgZ5fd8UxT+Kn4oAuS
cLr3lQT1s6xZOAYn7O2GvXEC+yMMbvm0a97MdwSpQez1WcE9YxtCgAWwn5EmSXlh
296iLtbaM1wgYOykJUOUoSgijf8pUfotk4Zj/y1KPHXePgAlyGCtE1zasiYb5K+5
LuajD++BAoGBAPpKWLNzQBwQLiFaJgt6bLOxlEUR+EnjdFePDPJtyCCCiKJiKO5c
Z5s/FT1JDQawouhjQwYqT48hbGBPjWRHkSkzB7+cg6FVSKkQRYX2TsSFvN+KCu32
oSgDV9cFo68v1csoZIQ41TtHC82db4OTv9MPUe3Glujnep1TOTwspAM1AoGBANtH
i+HWKOxOm7f/R2VX1ys9UjkAK+msac512XWSLAzBs7NFnB7iJ7m3Bh3ydb1ZiTgW
l6bIdoT8TLPYNIXJ6uohhxPU5h3v81PHqIuJMBtmHCQjq3nxeH9mOsfjOFvS1cQa
At45F9pK/5sQpOkkaBGSv8jXUFIKBEDBErourVHpAoGAK0gSAK4sZu3xXDkfnRqF
k6lgr3UFD5nys3V8UqvjUKPiBtqco2N9Ux5ciOWKCB8hfLg1jephKaoo+JqpI68w
jgRSEbN6G7sIvpueuiS2yEssNyfC7hWZFrdFSFykSpYmDWSlxSuizAZkJyFTeFhj
cpcSnuCZlhr5XB1ZJ2u8zQUCgYEAke5QgpCDFZjO+ynR+vj1gppBwRuDHfUXSUaW
3S7VT/wNOq6F0uvRYkASuxVkFAqlToWCkYVxktlRtpKZibwyMXT0r1cNejj5Z/VF
Du/S6zkOW2K9uN7hwW9oiSSHmlx61RI2fGvkmus0pp7yERKgi6ltJx1cH+z4nZug
efWcdRkCgYBy+XdmsxgNZOunlSC6VZiD0Ve/VFrCtKPWUivKDAZZPKl0T/1tbTwb
I/N4zTF82jx88rDz+6jN5nOy9qbSR5TeCy6WlBesTvXm49awr5jSK3WkcLgmO+JI
Zr2ozCBhUG6RvVsUPp2kXEsmwZMV/e9faFAlIXeJhKum6hZmfOgodg==
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIC6DCCAdACBRQJQlZkMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNVBAMTAm5zMQsw
CQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA4
MzAxOTA3NDNaFw0yNDA4MjcxOTA3NDNaMDgxCzAJBgNVBAMTAm5zMQswCQYDVQQK
EwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkdhbHdheTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBANZjjUNIQFzi/ZW9eT8+H6l1zR0RLBd2u/zaDCXU
A3JcdPmt9jIbh3DukexYbHNJmTLs25gBSzJiCIFzzCkAWS6ANdsggZWT8ihaFlIR
ay2t4FIzugmZQSqpFFRAAqVa0ONYoWqXnlB2tLq8rd7V8DJ9v/Od8s4tbnIhmIuF
C08t8kxP56kSFSS4gQ0gvVjRS4Nun/1uGEVu7QDTpNm/1v1rPQf5+7zQ4Bxuy6Xd
/GE6RYYV0598Zbf1XHM5EIVu/TKIev5WOrC4Oh9eRIxyh3k5/LfQUZvqmIvwrwtu
fGBf0M/6imoErC0/9xa0OvmkIIPxMmqNCFgKpePT0DcbsD0CAwEAATANBgkqhkiG
9w0BAQUFAAOCAQEAoVXK97WA24tp3JyPBJKr28gFSUtOBNDPdY8atWaqw7PwUIIM
qhs3BTag96tgSoaISRwRphz2LM1Cl+QlItYXySAnxPKrUsA0S6DlxnA6Hq3s2wTR
6yIT7oDUDKcWkVQcQmuNGdfxCvZXkCih9lnQn++xHcuVn9mZmjXW2xk42ljDTZCp
CM29betpcmuho6sFXsBhY7WjQWg7UpRZat0bOwleS4fsePebMKrnr/6cq4bVw59U
XvhSFBlLoGMYteJ82fOYH6pUO1hiPr6ww5d819LPcJEcRpcxCdQZqIq680Kp7+GY
0wkyOYr0gkNwWVP7IUZ0FExaQ/s54g71Kd0OgA==
-----END CERTIFICATE-----
+129
View File
@@ -0,0 +1,129 @@
# Copyright (c) 2014 Cesanta Software Limited
# All rights reserved
#
# = Requires:
#
# - SRC_DIR = path to directory with source files
# - AMALGAMATED_SOURCES = path to amalgamated C file(s)
# - PROG = name of main unit test source file
#
# - CFLAGS = default compiler flags
# - LDFLAGS = default linker flags
#
# = Parameters:
#
# - V=1 -> show commandlines of executed commands
# - TEST_FILTER -> test name (substring match)
# - CMD -> run wrapped in cmd (e.g. make test_cxx CMD=lldb)
#
# = Useful targets
#
# - compile: perform a very fast syntax check for all dialects
# - presubmit: suggested presubmit tests
# - cpplint: run the linter
# - lcov: generate coverage HTML in test/lcov/index.html
# - test_asan: run with AddressSanitizer
# - test_valgrind: run with valgrind
# OSX clang doesn't build ASAN. Use brew:
# $ brew tap homebrew/versions
# $ brew install llvm36 --with-clang --with-asan
CLANG:=clang-3.6
PEDANTIC=$(shell gcc --version 2>/dev/null | grep -q clang && echo -pedantic)
###
DIALECTS=cxx ansi c99 c11
SPECIALS=asan gcov valgrind
# Each test target might require either a different compiler name
# a compiler flag, or a wrapper to be invoked before executing the test
# they can be overriden here with <VAR>_<target>
#
# Vars are:
# - CC: compiler
# - CFLAGS: flags passed to the compiler, useful to set dialect and to disable incompatible tests
# - LDFLAGS: flags passed to the compiler only when linking (e.g. not in syntax only)
# - SOURCES: non-test source files. To be overriden if needs to build on non amalgamated files
# - CMD: command wrapper or env variables required to run the test binary
CMD=MallocLogFile=/dev/null
CC_cxx=$(CXX)
CFLAGS_cxx=-x c++
CFLAGS_ansi=-ansi
CFLAGS_c99=$(PEDANTIC) -std=c99
# Path to `gcov` binary
GCOV?=gcov
CFLAGS_gcov=$(PEDANTIC) -std=c99 -fprofile-arcs -ftest-coverage
#SOURCES_gcov=$(addprefix $(SRC_DIR)/, $(SOURCES))
SOURCES_gcov=$(AMALGAMATED_SOURCES)
CC_asan=$(CLANG)
CFLAGS_asan=-fsanitize=address -fcolor-diagnostics -std=c99 -DNO_DNS_TEST -UMG_ENABLE_SSL
CMD_asan=ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer ASAN_OPTIONS=allocator_may_return_null=1,symbolize=1 $(CMD)
CMD_valgrind=valgrind
###
SHELL := /bin/bash
SUFFIXES=$(DIALECTS) $(SPECIALS)
ALL_PROGS=$(foreach p,$(SUFFIXES),$(PROG)-$(p))
ALL_TESTS=$(foreach p,$(SUFFIXES),test_$(p))
SHORT_TESTS=$(foreach p,$(DIALECTS),test_$(p))
all: clean compile $(SHORT_TESTS) coverage
alltests: $(ALL_TESTS) lcov cpplint
# currently both valgrind and asan tests are failing for some test cases
# it's still useful to be able to run asan/valgrind on some specific test cases
# but we don't enforce them for presubmit until they are stable again.
presubmit: $(SHORT_TESTS) cpplint
.PHONY: clean clean_coverage lcov valgrind docker cpplint
ifneq ($(V), 1)
.SILENT: $(ALL_PROGS) $(ALL_TESTS)
endif
compile:
@make $(foreach p,$(DIALECTS),$(PROG)-$(p)) CFLAGS_EXTRA="$(CFLAGS_EXTRA) -fsyntax-only" LDFLAGS=
# HACK: cannot have two underscores
$(PROG)-%: Makefile $(TEST_SOURCES) $(or $(SOURCES_$*), $(AMALGAMATED_SOURCES)) data/cgi/index.cgi
@echo -e "CC\t$(PROG)_$*"
$(or $(CC_$*), $(CC)) $(CFLAGS_$*) $(TEST_SOURCES) $(or $(SOURCES_$*), $(AMALGAMATED_SOURCES)) -o $(PROG)_$* $(CFLAGS) $(LDFLAGS)
$(ALL_TESTS): test_%: Makefile $(PROG)-%
@echo -e "RUN\t$(PROG)_$* $(TEST_FILTER)"
@$(or $(CMD_$*), $(CMD)) ./$(PROG)_$* $(TEST_FILTER)
coverage: Makefile clean_coverage test_gcov
@echo -e "RUN\tGCOV"
@$(GCOV) -p $(notdir $(TEST_SOURCES)) $(notdir $(GCOV_SOURCES)) >/dev/null
test_leaks: Makefile
$(MAKE) test_valgrind CMD_valgrind="$(CMD_valgrind) --leak-check=full"
lcov: clean coverage
@echo -e "RUN\tlcov"
@lcov -q -o lcov.info -c -d . 2>/dev/null
@genhtml -q -o lcov lcov.info
cpplint:
@echo -e "RUN\tcpplint"
@cpplint.py --verbose=0 --extensions=c,h $(SRC_DIR)/*.[ch] 2>&1 >/dev/null| grep -v "Done processing" | grep -v "file excluded by"
clean: clean_coverage
@echo -e "CLEAN\tall"
@rm -rf $(PROG) $(PROG)_* lcov.info *.txt *.exe *.obj *.o a.out *.pdb *.opt $(EXTRA_CLEAN_TARGETS)
clean_coverage:
@echo -e "CLEAN\tcoverage"
@rm -rf *.gc* *.dSYM index.html
+5617
View File
File diff suppressed because it is too large. Load diff
+28
View File
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
* This software is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http://www.gnu.org/licenses/>.
*
* You are free to use this software under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* Alternatively, you can license this software under a commercial
* license, as set out in <https://www.cesanta.com/license>.
*/
#ifndef CS_MONGOOSE_TEST_UNIT_TEST_H_
#define CS_MONGOOSE_TEST_UNIT_TEST_H_
#ifndef _WIN32
#include "common/platform.h"
#endif
extern void *(*test_malloc)(size_t);
extern void *(*test_calloc)(size_t, size_t);
#endif /* CS_MONGOOSE_TEST_UNIT_TEST_H_ */
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash -e
cd $(dirname $0)
./mkmgtest.cmd $PWD/mgtest.vcxproj
cd ..
cp win/Debug/mgtest.exe .
./mgtest.exe || {
#hack to flush the logs
echo failed
exit 1
}
+28
View File
@@ -0,0 +1,28 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mgtest", "mgtest.vcxproj", "{699B1D38-AFCE-4B16-B030-A54833909F8B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{699B1D38-AFCE-4B16-B030-A54833909F8B}.Debug|x64.ActiveCfg = Debug|x64
{699B1D38-AFCE-4B16-B030-A54833909F8B}.Debug|x64.Build.0 = Debug|x64
{699B1D38-AFCE-4B16-B030-A54833909F8B}.Debug|x86.ActiveCfg = Debug|Win32
{699B1D38-AFCE-4B16-B030-A54833909F8B}.Debug|x86.Build.0 = Debug|Win32
{699B1D38-AFCE-4B16-B030-A54833909F8B}.Release|x64.ActiveCfg = Release|x64
{699B1D38-AFCE-4B16-B030-A54833909F8B}.Release|x64.Build.0 = Release|x64
{699B1D38-AFCE-4B16-B030-A54833909F8B}.Release|x86.ActiveCfg = Release|Win32
{699B1D38-AFCE-4B16-B030-A54833909F8B}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
+161
View File
@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{699B1D38-AFCE-4B16-B030-A54833909F8B}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>mgtest</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;MG_ENABLE_POLL_UNTIL;MG_ENABLE_COAP;MG_ENABLE_DNS_SERVER;MG_ENABLE_MQTT_BROKER;MG_ENABLE_THREADS;MG_DISABLE_DAV_AUTH;MG_INTERNAL=;_DEBUG;_CONSOLE;MG_ENABLE_COAP;MG_ENABLE_DNS_SERVER;MG_ENABLE_MQTT_BROKER;MG_ENABLE_THREADS;MG_ENABLE_HTTP_SSI_EXEC;MG_ENABLE_HTTP_WEBDAV;MG_DISABLE_DAV_AUTH;MG_ENABLE_SYNC_RESOLVER;NO_CGI_TEST;NO_RESOLVE_HOSTS_TEST;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../../..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;MG_ENABLE_COAP;MG_ENABLE_DNS_SERVER;MG_ENABLE_MQTT_BROKER;MG_ENABLE_THREADS;MG_ENABLE_HTTP_WEBDAV;MG_ENABLE_HTTP_SSI_EXEC;MG_ENABLE_SYNC_RESOLVER;MG_DISABLE_DAV_AUTH;MG_INTERNAL=;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\common\test_util.c" />
<ClCompile Include="..\..\..\mongoose\mongoose.c" />
<ClCompile Include="..\..\..\mongoose\test\unit_test.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\common\test_util.h" />
<ClInclude Include="..\..\..\mongoose\mongoose.h" />
<ClInclude Include="..\..\..\mongoose\test\unit_test.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
+42
View File
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\mongoose\mongoose.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\mongoose\test\unit_test.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\common\test_util.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\mongoose\mongoose.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\mongoose\test\unit_test.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\common\test_util.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
+1
View File
@@ -0,0 +1 @@
"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" /p:Configuration=Debug /t:Clean;Build %1