IMPORTANT NOTICES:If you have CGIC 1.05 or earlier, you should upgrade to CGIC 1.07, or to CGIC 2.02 or better, in order to obtain important security fixes.
If you have CGIC 2.0 or CGIC 2.01 and you use the cgiCookie routines, you should upgrade to CGIC 2.02 or better, in order to obtain important security fixes.
cgic is now distributed under the MIT license:
Copyright (c) 1996-2019 Thomas Boutell
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Thanks are due to Robert Gustavsson, Ken Holervich, Bob Nestor, Jon Ribbens, Thomas Strangert, Wu Yongwei, and other CGIC users who have corresponded over the years. Although the implementation of multipart/form-data file upload support in CGIC 2.x is my own, I particularly wish to thank those who submitted their own implementations of this feature.
cgicTempDir
macro in cgic.c before compiling).
The cgiFormFileName
,
cgiFormFileContentType
,
cgiFormFileSize
,
cgiFormFileOpen
,
cgiFormFileRead
, and
cgiFormFileClose
functions
provide a complete interface to this new functionality. Remember,
the enctype
attribute of the form
tag
must be set to multipart/form-data
when
<input type="file">
tags are used.
2. CGIC 2.0 provides support for setting and examining cookies
(persistent data storage on the browser side).
The cgiCookieString
,
and cgiCookieInteger
and cgiCookies
functions retrieve cookies. The
cgiHeaderCookieSetString
and cgiHeaderCookieSetInteger
functions set cookies.
3. CGIC 2.0 offers a convenient way to retrieve a list of all form fields.
The new cgiFormEntries
function performs this operation.
4. CGIC 2.0 provides convenience functions to correctly escape
text before outputting it as part of HTML, or as part of the
value of a tag attribute, such as the HREF
or
VALUE
attribute. See
cgiHtmlEscape
,
cgiHtmlEscapeData
,
cgiValueEscape
and
cgiValueEscapeData
.
5. Users have often asked the correct way to determine which submit button was clicked. This could always be accomplished in previous versions, but CGIC 2.0 also provides cgiFormSubmitClicked, a convenient alternate label for the cgiFormCheckboxSingle function.
2. The function cgiSaferSystem()
has been
removed entirely. This function escaped only a few metacharacters,
while most shells have many, and there was no way to account for
the many different operating system shells that might be in use
on different operating systems. Since this led to a false sense
of security, the function has been removed. It is our recommendation
that user input should never be passed directly on the command line
unless it has been carefully shown to contain only characters
regarded as safe and appropriate by the programmer. Even then, it is
better to design your utilities to accept their input from standard
input rather than the command line.
9
are
now properly decoded.
cgic performs the following tasks:
cgic is compatible with any CGI-compliant server environment, and compiles without modification in Posix/Unix/Linux and Windows environments.
Important: to use cgic, you will need an ANSI-standard C compiler. Under Unix, just obtain and use gcc. Most Unix systems have standardiszed on gcc. Users of Windows operating systems should not have ANSI C-related problems as all of the popular compilers follow the ANSI standard.
Note for Windows Programmers: you must use a modern 32-bit compiler. Visual C++ 2.0 or higher, Borland C++ and the mingw32 gcc compiler are all appropriate, as is cygwin. Do NOT use an ancient 16-bit DOS executable compiler, please.
What Operating System Does Your WEB SERVER Run?
Remember, the computer on your desk is usually NOT your web server. Compiling a Windows console executable will not give you a CGI program that can be installed on a Linux-based server.
On a Unix system, you can build cgictest simply by typing 'make cgictest.cgi'. cgic.c and cgictest.c will be compiled and linked together to produce the cgictest application. Under non-Unix operating systems, you will need to create and compile an appropriate project containing the files cgic.c and cgictest.c.
IMPORTANT: after compiling cgictest.cgi, you will need to place it in a location on your server system which is designated by your server administrator as an appropriate location for CGI scripts. Some servers are configured to recognize any file ending in .cgi as a CGI program when it is found in any subdirectory of the server's web space, but this is not always the case! The right locations for CGI programs vary greatly from one server to another. Resolving this issue is between you, your web server administrator, and your web server documentation. Before submitting a bug report for cgic, make certain that the CGI example programs which came with your server do work for you. Otherwise it is very likely that you have a server configuration problem.
Once you have moved cgictest.cgi (or cgictest.exe, under Windows)
to an appropriate cgi directory,
use the web browser of your choice to access the URL at which
you have installed it
(for instance, www.mysite.com/cgi-bin/cgictest.cgi
).
Fill out the various fields in any manner you wish, then
select the SUBMIT button.
If all goes well, cgictest.cgi will respond with a page which indicates the various settings you submitted. If not, please reread the section above regarding the correct location in which to install your CGI program on your web server.
Since all CGI applications must perform certain initial tasks, such as parsing form data and examining environment variables, the cgic library provides its own main() function. When you write applications that use cgic, you will begin your own programs by writing a cgiMain() function, which cgic will invoke when the initial cgi work has been successfully completed. Your program must also be sure to #include the file cgic.h.
Important: if you write your own main() function, your program will not link properly. Your own code should begin with cgiMain(). The library provides main() for you. (Those who prefer different behavior can easily modify cgic.c.)
Consider the cgiMain function of cgictest.c:
int cgiMain() { #ifdef DEBUG LoadEnvironment(); #endif /* DEBUG */ /* Load a previously saved CGI scenario if that button has been pressed. */ if (cgiFormSubmitClicked("loadenvironment") == cgiFormSuccess) { LoadEnvironment(); } /* Set any new cookie requested. Must be done *before* outputting the content type. */ CookieSet(); /* Send the content type, letting the browser know this is HTML */ cgiHeaderContentType("text/html"); /* Top of the page */ fprintf(cgiOut, "<HTML><HEAD>\n"); fprintf(cgiOut, "<TITLE>cgic test</TITLE></HEAD>\n"); fprintf(cgiOut, "<BODY><H1>cgic test</H1>\n"); /* If a submit button has already been clicked, act on the submission of the form. */ if ((cgiFormSubmitClicked("testcgic") == cgiFormSuccess) || cgiFormSubmitClicked("saveenvironment") == cgiFormSuccess) { HandleSubmit(); fprintf(cgiOut, "<hr>\n"); } /* Now show the form */ ShowForm(); /* Finish up the page */ fprintf(cgiOut, "</BODY></HTML>\n"); return 0; }Note the DEBUG #ifdef. If DEBUG is defined at compile time, either by inserting the line "#define DEBUG 1" into the program or by setting it in the Makefile or other development environment, then the LoadEnvironment function is invoked. This function calls cgiReadEnvironment() to restore a captured CGI environment for debugging purposes. See also the discussion of the capture program, which is provided for use in CGI debugging. Because this is a test program, the cgiFormSubmitClicked function is also called to check for the use of a button that requests the reloading of a saved CGI environment. A completed CGI program typically would never allow the end user to make that decision.
void CookieSet() { char cname[1024]; char cvalue[1024]; /* Must set cookies BEFORE calling cgiHeaderContentType */ cgiFormString("cname", cname, sizeof(cname)); cgiFormString("cvalue", cvalue, sizeof(cvalue)); if (strlen(cname)) { /* Cookie lives for one day (or until browser chooses to get rid of it, which may be immediately), and applies only to this script on this site. */ cgiHeaderCookieSet(cname, cvalue, 86400, cgiScriptName, cgiServerName, cgiCookieHttpOnly | cgiCookieSameSiteStrict); } }Since this is a test program, the cgiFormString function is used to fetch the name and value from the form previously filled in by the user. Normally, cookie names and values are chosen to meet the needs of the programmer and provide a means of identifying the same user again later.
The cgiHeaderCookieSet function sets the cookie by requesting that the web browser store it. There is never any guarantee that this will happen! Many browsers reject cookies completely; others do not necessarily keep them as long as requested or return them with their values intact. Always code defensively when using cookies.
The cname and cvalue parameters are of course the namd and value for the cookie. The third argument is the time, in seconds, that the cookie should "live" on the browser side before it expires; in this case it has been set to 86,400 seconds, which is exactly one day. The browser may or may not respect this setting, as with everything else about cookies.
The fourth argument identifies the "path" within the web site for which
the cookie is considered valid. A cookie that should be sent back
for every access to the site should be set with a path of /
.
In this case the cookie is relevant only to the CGI program itself, so
cgiScriptName
(the URL of the CGI program, not including the
domain name) is sent. Similarly, a cookie can be considered relevant
to a single web site or to an entire domain, such as
www.boutell.dev
or the entire .boutell.dev
domain. In this case, the current site on which the program is running
is the only relevant site, so cgiServerName
is used
as the domain.
The sixth argument sets extra security options, for example HttpOnly or SameSite=Strict to prevent cross-site-scripting attacks.
Note that cgiHeaderStatus() or cgiHeaderLocation() could have been invoked instead to output an error code or redirect the request to a different URL. Only one of the cgiHeader functions should be called in a single execution of the program.
Important: one of the cgiHeader functions, usually cgiHeaderContentType(), must be invoked before outputting any other response to the user. Otherwise, the result will not be a valid document and the browser's behavior will be unpredictable. You may, of course, output your own ContentType and other header information to cgiOut if you prefer. The cgiHeader functions are provided as a convenience.
void HandleSubmit() { Name(); Address(); Hungry(); Temperature(); Frogs(); Color(); Flavors(); NonExButtons(); RadioButtons(); File(); Entries(); Cookies(); /* The saveenvironment button, in addition to submitting the form, also saves the resulting CGI scenario to disk for later replay with the 'load saved environment' button. */ if (cgiFormSubmitClicked("saveenvironment") == cgiFormSuccess) { SaveEnvironment(); } }
void Name() { char name[81]; cgiFormStringNoNewlines("name", name, 81); fprintf(cgiOut, "Name: "); cgicHtmlEscape(name); fprintf(cgiOut, "The purpose of this function is to retrieve and display the name that was input by the user. Since the programmer has decided that names should be permitted to have up to 80 characters, a buffer of 81 characters has been declared (allowing for the final null character). The cgiFormStringNoNewlines() function is then invoked to retrieve the name and ensure that carriage returns are not present in the name (despite the incorrect behavior of some web browsers). The first argument is the name of the input field in the form, the second argument is the buffer to which the data should be copied, and the third argument is the size of the buffer. cgic will never write beyond the size of the buffer, and will always provide a null-terminated string in response; if the buffer is too small, the string will be shortened. If this is not acceptable, the cgiFormStringSpaceNeeded() function can be used to check the amount of space needed; the return value of cgiFormStringNoNewlines() can also be checked to determine whether truncation occurred. See the full description of cgiFormStringNoNewlines().
\n"); }
The actual name submitted by the user may or may not contain
characters that have special meaning in HTML, specifically the
the <
, >
, and &
characters.
The cgiHtmlEscape function is used to output
the user-entered name with any occurrences of these characters
correctly escaped as <
, >
,
and &
.
Important: cgiOut is normally equivalent to stdout, and there is no performance penalty for using it. It is recommended that you write output to cgiOut to ensure compatibility with modified versions of the cgic library for special environments that do not provide stdin and stdout for each cgi connection.
Note that, for text input areas in which carriage returns are desired, the function cgiFormString should be used instead. cgiFormString ensures that line breaks are always represented by a single carriage return (ascii decimal 13), making life easier for the programmer. See the source code to the Address() function of cgictest.c for an example.
void Hungry() { if (cgiFormCheckboxSingle("hungry") == cgiFormSuccess) { fprintf(cgiOut, "I'm Hungry!<BR>\n"); } else { fprintf(cgiOut, "I'm Not Hungry!<BR>\n"); } }This function takes advantage of the cgiFormCheckboxSingle() function, which determines whether a single checkbox has been selected. cgiFormCheckboxSingle() accepts the name attribute of the checkbox as its sole argument and returns cgiFormSuccess if the checkbox is selected, or cgiFormNotFound if it is not. If multiple checkboxes with the same name are in use, consider the cgiFormCheckboxMultiple() and cgiFormStringMultiple() functions.
void Temperature() { double temperature; cgiFormDoubleBounded("temperature", &temperature, 80.0, 120.0, 98.6); fprintf(cgiOut, "My temperature is %f.<BR>\n", temperature); }The temperature is retrieved by the function cgiFormDoubleBounded(). The first argument is the name of the temperature input field in the form; the second argument points to the address of the variable that will contain the result. The next two arguments are the lower and upper bounds, respectively. The final argument is the default value to be returned if the user did not submit a value.
This function always retrieves a reasonable value within the specified bounds; values above or below bounds are constrained to fit the bounds. However, the return value of cgiFormDoubleBounded can be checked to make sure the actual user entry was in bounds, not blank, and so forth; see the description of cgiFormDoubleBounded() for more details. If bounds checking is not desired, consider using cgiFormDouble() instead.
Note that, for integer input, the functions cgiFormInteger and cgiFormIntegerBounded are available. The behavior of these functions is similar to that of their floating-point counterparts above.
char *colors[] = { "Red", "Green", "Blue" }; void Color() { int colorChoice; cgiFormSelectSingle("colors", colors, 3, &colorChoice, 0); fprintf(cgiOut, "I am: %s<BR>\n", colors[colorChoice]); }This function determines which of several colors the user chose from a <SELECT> list in the form. An array of colors is declared; the cgiFormSelectSingle() function is then invoked to determine which, if any, of those choices was selected. The first argument indicates the name of the input field in the form. The second argument points to the list of acceptable colors. The third argument indicates the number of entries in the color array. The fourth argument points to the variable which will accept the chosen color, and the last argument indicates the index of the default value to be set if no selection was submitted by the browser.
cgiFormSelectSingle() will always indicate a reasonable selection value. However, if the programmer wishes to know for certain that a value was actually submitted, that the value submitted was a legal response, and so on, the return value of cgiFormSelectSingle() can be consulted. See the full description of cgiFormSelectSingle() for more information.
Note that radio button groups and <SELECT> lists can both be handled by this function. If you are processing radio button groups, you may prefer to invoke cgiFormRadio(), which functions identically.
"What if I won't know the acceptable choices at runtime?"
If the acceptable choices aren't known until runtime, one can simply load the choices from disk. But if the acceptable choices aren't fixed at all (consider a list of country names; new names may be added to the form at any time and it is inconvenient to also update program code or a separate list of countries), simply invoke cgiFormStringNoNewlines() instead to retrieve the string directly. Keep in mind that, if you do so, validating the response to make sure it is safe and legitimate becomes a problem for your own program to solve. The advantage of cgiFormSelectSingle() is that invalid responses are never returned.
To handle multiple-selection <SELECT> lists and groups of checkboxes with the same name, see the discussion of the NonExButtons() function of cgictest.c, immediately below.
char *votes[] = { "A", "B", "C", "D" }; void NonExButtons() { int voteChoices[4]; int i; int result; int invalid; char **responses; /* Method #1: check for valid votes. This is a good idea, since votes for nonexistent candidates should probably be discounted... */ fprintf(cgiOut, "Votes (method 1):<BR>\n"); result = cgiFormCheckboxMultiple("vote", votes, 4, voteChoices, &invalid); if (result == cgiFormNotFound) { fprintf(cgiOut, "I hate them all!<p>\n"); } else { fprintf(cgiOut, "My preferred candidates are:\n"); fprintf(cgiOut, "<ul>\n"); for (i=0; (i < 4); i++) { if (voteChoices[i]) { fprintf(cgiOut, "<li>%s\n", votes[i]); } } fprintf(cgiOut, "</ul>\n"); }This function takes advantage of cgiFormCheckboxMultiple(), which is used to identify one or more selected checkboxes with the same name. This function performs identically to cgiFormSelectMultiple(). That is, <SELECT> tags with the MULTIPLE attribute are handled just like a group of several checkboxes with the same name.
The first argument to cgiFormCheckboxMultiple() is the name given to all checkbox input fields in the group. The second argument points to an array of legitimate values; these should correspond to the VALUE attributes of the checkboxes (or OPTION tags in a <SELECT> list). The third argument indicates the number of entries in the array of legitimate values. The fourth argument points to an array of integers with the same number of entries as the array of legitimate values; each entry will be set true if that checkbox or option was selected, false otherwise.
The last argument points to an integer which will be set to the number of invalid responses (responses not in the array of valid responses) that were submitted. If this value is not of interest, the last argument may be a null pointer (0).
Note that the return value of cgiFormCheckboxMultiple is inspected to determine whether any choices at all were set. See the full description of cgiFormCheckboxMultiple for other possible return values.
"What if I won't know the acceptable choices at runtime?"
If the acceptable choices aren't known until runtime, one can simply load the choices from disk. But if the acceptable choices aren't fixed at all (consider a list of ice cream flavors; new names may be added to the form at any time and it is inconvenient to also update program code or a separate list of countries), a more dynamic approach is needed. Consider the second half of the NonExButtons() function of cgictest.c:
/* Method #2: get all the names voted for and trust them. This is good if the form will change more often than the code and invented responses are not a danger or can be checked in some other way. */ fprintf(cgiOut, "Votes (method 2):<BR>\n"); result = cgiFormStringMultiple("vote", &responses); if (result == cgiFormNotFound) { fprintf(cgiOut, "I hate them all!<p>\n"); } else { int i = 0; fprintf(cgiOut, "My preferred candidates are:\n"); fprintf(cgiOut, "<ul>\n"); while (responses[i]) { fprintf(cgiOut, "<li>%s\n", responses[i]); i++; } fprintf(cgiOut, "</ul>\n"); } /* We must be sure to free the string array or a memory leak will occur. Simply calling free() would free the array but not the individual strings. The function cgiStringArrayFree() does the job completely. */ cgiStringArrayFree(responses); }This code excerpt demonstrates an alternate means of retrieving a list of choices. The function cgiFormStringMultiple() is used to retrieve an array consisting of all the strings submitted for with a particular input field name. This works both for <SELECT> tags with the MULTIPLE attribute and for groups of checkboxes with the same name.
The first argument to cgiFormStringMultiple() is the name of the input field or group of input fields in question. The second argument should be the address of a pointer to a pointer to a string, which isn't as bad as it sounds. Consider the following simple call of the function:
/* An array of strings; each C string is an array of characters */ char **responses; cgiFormStringMultiple("vote", &responses);"How do I know how many responses there are?"
After the call, the last entry in the string array will be a null pointer. Thus the simple loop:
int i = 0; while (responses[i]) { /* Do something with the string responses[i] */ i++; }can be used to walk through the array until the last entry is encountered.
Important: the cgiFormStringMultiple function returns a pointer to allocated memory. Your code should not modify the strings in the responses array or the responses array itself; if modification is needed, the strings should be copied. When your code is done examining the responses array, you MUST call cgiStringArrayFree() with the array as an argument to free the memory associated with the array. Otherwise, the memory will not be available again until the program exists. Don't just call the free() function; if you do, the individual strings will not be freed.
enctype
attribute of your form
tag
to multipart/form-data
for this feature to work! For an
example, see the ShowForm function of
cgictest.c, examined below.
The File
function of cgictest.c takes care of
receiving uploaded files:
void File() { cgiFilePtr file; char name[1024]; char contentType[1024]; char buffer[1024]; int size; int got; if (cgiFormFileName("file", name, sizeof(name)) != cgiFormSuccess) { printf("<p>No file was uploaded.<p>\n"); return; } fprintf(cgiOut, "The filename submitted was: "); cgiHtmlEscape(name); fprintf(cgiOut, "<p>\n"); cgiFormFileSize("file", &size); fprintf(cgiOut, "The file size was: %d bytes<p>\n", size); cgiFormFileContentType("file", contentType, sizeof(contentType)); fprintf(cgiOut, "The alleged content type of the file was: "); cgiHtmlEscape(contentType); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "Of course, this is only the claim the browser " "made when uploading the file. Much like the filename, " "it cannot be trusted.<p>\n"); fprintf(cgiOut, "The file's contents are shown here:<p>\n"); if (cgiFormFileOpen("file", &file) != cgiFormSuccess) { fprintf(cgiOut, "Could not open the file.<p>\n"); return; } fprintf(cgiOut, "<pre>\n"); while (cgiFormFileRead(file, buffer, sizeof(buffer), &got) == cgiFormSuccess) { cgiHtmlEscapeData(buffer, got); } fprintf(cgiOut, "</pre>\n"); cgiFormFileClose(file); }First, the File function checks to determine the filename that was submitted by the user. VERY IMPORTANT: this filename may or may not bear any relation to the real name of the file on the user's computer, may be deliberately manipulated with malicious intent, and should not be used for any purpose unless you have determined that its content is safe for your intended use and will not, at the very least, overwrite another file of importance to you, especially if you intend to use it as a file name on the server side. The cgic library itself does not use this file name for temporary storage.
If the cgiFormFileName function does not succeed, no file was uploaded.
Next, the cgiFormFileSize function is called to determine the size of the uploaded file, in bytes.
The File function then proceeds to query the content type of the uploaded
file. Files uploaded by the user have their own content type information,
which may be useful in determining whether the file is an image, HTML document,
word processing document, or other type of file. However,
as with the filename and any other claim made by the browser,
this information should not be blindly trusted. The browser
may upload a file with the name picture.jpg
and the
content type image/jpeg
, but this does not guarantee that the
actual file will contain a valid JPEG image suitable for display.
The content type submitted by the browser can be queried using the cgiFormFileContentType function.
Of course, CGIC also provides access to the actual uploded file.
First, the programmer calls cgiFormFileOpen,
passing the address of a cgiFilePtr
object. If this function
succeeds, the cgiFilePtr
object becomes valid, and can be
used in subsequent calls to cgiFormFileRead.
Notice that the number of bytes read may be less than the number requested,
in particular on the last successful call before cgiFormFileRead begins
to return cgiFormEOF
. When cgiFormFileRead no longer returns
cgiFormSuccess,
the programmer calls cgiFormFileClose to
release the cgiFilePtr
object.
The uploaded file data may contain anything, including binary data, null characters, and so on. The example program uses the cgiHtmlEscapeData function to output the data with any special characters that have meaning in HTML escaped. Most programs will save the uploaded information to a server-side file or database.
void Entries() { char **array, **arrayStep; fprintf(cgiOut, "List of All Submitted Form Field Names:<p>\n"); if (cgiFormEntries(&array) != cgiFormSuccess) { return; } arrayStep = array; fprintf(cgiOut, "<ul>\n"); while (*arrayStep) { fprintf(cgiOut, "<li>"); cgiHtmlEscape(*arrayStep); fprintf(cgiOut, "\n"); arrayStep++; } fprintf(cgiOut, "</ul>\n"); cgiStringArrayFree(array); }The cgiFormEntries function retrieves an array of form field names. This array consists of pointers to strings, with a final null pointer to mark the end of the list. The above code illustrates one way of looping through the returned strings. Note the final call to cgiStringArrayFree, which is essential in order to return the memory used to store the strings and the string array.
void Cookies() { char **array, **arrayStep; char cname[1024], cvalue[1024]; fprintf(cgiOut, "Cookies Submitted On This Call, With Values " "(Many Browsers NEVER Submit Cookies):<p>\n"); if (cgiCookies(&array) != cgiFormSuccess) { return; } arrayStep = array; fprintf(cgiOut, "<table border=1>\n"); fprintf(cgiOut, "<tr><th>Cookie<th>Value</tr>\n"); while (*arrayStep) { char value[1024]; fprintf(cgiOut, "<tr>"); fprintf(cgiOut, "<td>"); cgiHtmlEscape(*arrayStep); fprintf(cgiOut, "<td>"); cgiCookieString(*arrayStep, value, sizeof(value)); cgiHtmlEscape(value); fprintf(cgiOut, "\n"); arrayStep++; } fprintf(cgiOut, "</table>\n"); cgiFormString("cname", cname, sizeof(cname)); cgiFormString("cvalue", cvalue, sizeof(cvalue)); if (strlen(cname)) { fprintf(cgiOut, "New Cookie Set On This Call:<p>\n"); fprintf(cgiOut, "Name: "); cgiHtmlEscape(cname); fprintf(cgiOut, "Value: "); cgiHtmlEscape(cvalue); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "If your browser accepts cookies " "(many do not), this new cookie should appear " "in the above list the next time the form is " "submitted.<p>\n"); } cgiStringArrayFree(array); }VERY IMPORTANT: YOUR BROWSER MIGHT NOT SUBMIT COOKIES, EVER, REGARDLESS OF WHAT VALUES YOU ENTER INTO THE TEST FORM. Many, many browsers are configured not to accept or send cookies; others are configured to send them as little as possible to meet the bare minimum requirements for entry into popular sites. Users will often refuse your cookies; make sure your code still works in that situation!
The above code uses the cgiCookies function to retrieve a list of all currently set cookies as a null-terminated array of strings. The cgiCookieString function is then used to fetch the value associated with each cookie; this function works much like cgiFormString, discussed earlier. Note that a cookie set as a part of the current form submission process does not appear on this list immediately, as it has not yet been sent back by the browser. It should appear on future submissions, provided that the browser chooses to accept and resend the cookie at all.
void ShowForm() { fprintf(cgiOut, "<!-- 2.0: multipart/form-data is required "for file uploads. -->"); fprintf(cgiOut, "<form method=\"POST\" " "enctype=\"multipart/form-data\" "); fprintf(cgiOut, " action=\""); cgiValueEscape(cgiScriptName); fprintf(cgiOut, "\">\n"); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "Text Field containing Plaintext\n"); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "<input type=\"text\" name=\"name\">Your Name\n"); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "Multiple-Line Text Field\n"); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "<textarea NAME=\"address\" ROWS=4 COLS=40>\n"); fprintf(cgiOut, "Default contents go here. \n"); fprintf(cgiOut, "</textarea>\n"); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "Checkbox\n"); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "<input type=\"checkbox\" name=\"hungry\" checked>Hungry\n"); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "Text Field containing a Numeric Value\n"); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "<input type=\"text\" name=\"temperature\" value=\"98.6\">\n"); fprintf(cgiOut, "Blood Temperature (80.0-120.0)\n"); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "Text Field containing an Integer Value\n"); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "<input type=\"text\" name=\"frogs\" value=\"1\">\n"); fprintf(cgiOut, "Frogs Eaten\n"); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "Single-SELECT\n"); fprintf(cgiOut, "<br>\n"); fprintf(cgiOut, "<select name=\"colors\">\n"); fprintf(cgiOut, "<option value=\"Red\">Red\n"); fprintf(cgiOut, "<option value=\"Green\">Green\n"); fprintf(cgiOut, "<option value=\"Blue\">Blue\n"); fprintf(cgiOut, "</select>\n"); fprintf(cgiOut, "<br>\n"); fprintf(cgiOut, "Multiple-SELECT\n"); fprintf(cgiOut, "<br>\n"); fprintf(cgiOut, "<select name=\"flavors\" multiple>\n"); fprintf(cgiOut, "<option value=\"pistachio\">Pistachio\n"); fprintf(cgiOut, "<option value=\"walnut\">Walnut\n"); fprintf(cgiOut, "<option value=\"creme\">Creme\n"); fprintf(cgiOut, "</select>\n"); fprintf(cgiOut, "<p>Exclusive Radio Button Group: Age of " "Truck in Years\n"); fprintf(cgiOut, "<input type=\"radio\" name=\"age\" " "value=\"1\">1\n"); fprintf(cgiOut, "<input type=\"radio\" name=\"age\" " "value=\"2\">2\n"); fprintf(cgiOut, "<input type=\"radio\" name=\"age\" " "value=\"3\" checked>3\n"); fprintf(cgiOut, "<input type=\"radio\" name=\"age\" " "value=\"4\">4\n"); fprintf(cgiOut, "<p>Nonexclusive Checkbox Group: " "Voting for Zero through Four Candidates\n"); fprintf(cgiOut, "<input type=\"checkbox\" name=\"vote\" " "value=\"A\">A\n"); fprintf(cgiOut, "<input type=\"checkbox\" name=\"vote\" " "value=\"B\">B\n"); fprintf(cgiOut, "<input type=\"checkbox\" name=\"vote\" " "value=\"C\">C\n"); fprintf(cgiOut, "<input type=\"checkbox\" name=\"vote\" " "value=\"D\">D\n"); fprintf(cgiOut, "<p>File Upload:\n"); fprintf(cgiOut, "<input type=\"file\" name=\"file\" " "value=\"\"> (Select A Local File)\n"); fprintf(cgiOut, "<p>\n"); fprintf(cgiOut, "<p>Set a Cookie<p>\n"); fprintf(cgiOut, "<input name=\"cname\" " "value=\"\"> Cookie Name\n"); fprintf(cgiOut, "<input name=\"cvalue\" " "value=\"\"> Cookie Value<p>\n"); fprintf(cgiOut, "<input type=\"submit\" " "name=\"testcgic\" value=\"Submit Request\">\n"); fprintf(cgiOut, "<input type=\"reset\" " "value=\"Reset Request\">\n"); fprintf(cgiOut, "<p>Save the CGI Environment<p>\n"); fprintf(cgiOut, "Pressing this button will submit the form, then " "save the CGI environment so that it can be replayed later " "by calling cgiReadEnvironment (in a debugger, for " "instance).<p>\n"); fprintf(cgiOut, "<input type=\"submit\" name=\"saveenvironment\" " "value=\"Save Environment\">\n"); fprintf(cgiOut, "</form>\n"); }Note the use of
enctype="multipart/form-data"
in the
FORM
tag. This is absolutely required if the form
will contain file upload fields, as in the above example. Most
browsers will not even attempt file uploads without the
presence of this attribute.
Instead of calling getenv() to determine the value of a variable such as HTTP_USER_AGENT (the browser software being used), always use the cgic copies of the environment variables, which are always valid C strings (they are never null, although they may point to an empty string). For instance, the cgic variable containing the name of the browser software is cgiUserAgent. The referring URL appears in the variable cgiReferrer.
The following short sample program hints at the possibilities:
#include "cgic.h" #include "gd.h" char *colors[] = { "red", "green", "blue" }; #define colorsTotal 3 int cgiMain() { int colorChosen; gdImagePtr im; int r, g, b; /* Use gd to create an image */ im = gdImageCreate(64, 64); r = gdImageColorAllocate(im, 255, 0, 0); g = gdImageColorAllocate(im, 0, 255, 0); b = gdImageColorAllocate(im, 0, 0, 255); /* Now use cgic to find out what color the user requested */ cgiFormSelectSingle("color", 3, &colorChosen, 0); /* Now fill with the desired color */ switch(colorChosen) { case 0: gdImageFill(im, 32, 32, r); break; case 1: gdImageFill(im, 32, 32, g); break; case 2: gdImageFill(im, 32, 32, b); break; } /* Now output the image. Note the content type! */ cgiHeaderContentType("image/gif"); /* Send the image to cgiOut */ gdImageGif(im, cgiOut); /* Free the gd image */ gdImageDestroy(im); return 0; }Note that this program would need to be linked with both cgic.o and libgd.a. Often programs of this type respond to one cgiPathInfo value or set of form fields by returning an HTML page with an inline image reference that, in turn, generates a GIF image.
The provided program 'capture.c' can be used to capture CGI environments. Just change the first line of the cgiMain() function of capture.c to save the CGI environment to a filename appropriate on your system and type 'make capture'. Then place capture in your cgi directory and set the form action or other link you want to test to point to it. When the form submission or other link takes place, capture will write the CGI environment active at that time to the filename you specified in the source. The cgiReadEnvironment() function can then be invoked on the same filename at the beginning of the cgiMain() function of the application you want to test in order to restore the captured environment. You can then execute your program in the debugger of your choice, and it should perform exactly as it would have performed had it been launched by the actual web server, including file uploads, cookies and all other phenomena within the purview of cgic.
Important: Make sure you specify the full path, as the current working directory of a CGI script may not be what you think it is!
Even More Important: If you call getenv() yourself in your code, instead of using the provided cgic copies of the CGI environment variables, you will not get the values you expect when running with a saved CGI environment. Always use the cgic variables instead of calling getenv().
file
.
NEVER, EVER TRUST THIS FILENAME TO BE REASONABLE AND
SAFE FOR DIRECT USE ON THE SERVER SIDE.
The text will be copied into
the buffer specified by fileName, up to but not
exceeding max-1 bytes; a terminating null is then
added to complete the string. cgiFormFileName returns
cgiFormSuccess if the string was
successfully retrieved and was not empty,
cgiFormNoFileName if the string was
successfully retrieved but empty indicating that no file was uploaded,
cgiFormTruncated if the string was
retrieved but was truncated to fit the buffer,
and cgiFormNotFound if no
such input field was submitted. In the last case,
an empty string is copied to result.
file
specified by the
name
parameter. On success, the size is stored
to *sizeP, and this function returns
cgiFormSuccess. If the form
field does not exist, this function returns
cgiFormNotFound.
If the form field exists but no file was uploaded, this function
returns cgiFormNotAFile.
file
.
THERE IS NO GUARANTEE THAT THE CONTENT TYPE WILL BE
ACCURATE.
The content type string will be copied into
the buffer specified by contentType, up to but not
exceeding max-1 bytes; a terminating null is then
added to complete the string. cgiFormFileContentType returns
cgiFormSuccess if the string was
successfully retrieved and was not empty,
cgiFormNoContentType if the string was
successfully retrieved but empty indicating that no file was uploaded
or the browser did not know the content type,
cgiFormTruncated if the string was
retrieved but was truncated to fit the buffer,
and cgiFormNotFound if no
such input field was submitted. In the last case,
an empty string is copied to result.
file
. Upon success,
this function returns retrieve the content name claimed by the
user for the specified form input field of type file
.
On success, this function sets *cfpp to a valid cgiFilePtr
object for use with cgiFormFileRead
and returns cgiFormSuccess.
On failure, this function sets *cfpp to a null pointer, and
returns cgiFormNotFound,
cgiFormNotAFile,
cgiFormMemory or
cgiFormIO as appropriate.
See also cgiFormFileRead
and cgiFormFileClose.
bufferSize
bytes from a cgiFilePtr object previously opened with
cgiFormFileOpen. If any data
is successfully read, it is copied to buffer
,
and the number of bytes successfully read is stored
to *gotP
. This function returns
cgiFormSuccess if any data
is successfully read. At end of file, this function
returns cgiFormEOF. In the event
of an I/O error, this function returns
cgiFormIO. If cfp is a null pointer,
this function returns cgiFormOpenFailed.
See also cgiFormFileOpen
and cgiFormFileClose.
See also cgiFormFileOpen
and cgiFormFileRead.
If you wish to set cookies,
you must make your calls to
cgiHeaderCookieSet
and
cgiHeaderCookieSetInteger
BEFORE invoking cgiHeaderLocation.
If you wish to set cookies,
you must make your calls to
cgiHeaderCookieSet
and
cgiHeaderCookieSetInteger
BEFORE invoking cgiHeaderStatus.
If you wish to set cookies,
you must make your calls to
cgiHeaderCookieSet
and
cgiHeaderCookieSetInteger
BEFORE invoking cgiHeaderContentType.
/
. The final argument is the
web site name or entire domain for which this cookie should be
submitted; if you choose to have the cookie sent back for an
entire domain, this argument must begin with a dot, such as
.boutell.dev
. The cgic variables cgiScriptName
and cgiServerName are convenient
values for the fourth and fifth arguments.
The sixth argument is a bitmask for specifying cookie security options.
It can be zero (no options) or a bitwise-OR of the following
enum cgiCookieOption
values:
cgiCookieSecure
cgiCookieHttpOnly
cgiCookieSameSiteStrict
len
bytes. See cgiHtmlEscape
for more information.
len
bytes. See cgiValueEscape
for more information.
Most of these variables are equivalent to various CGI environment variables. The most important difference is that the cgic environment string variables are never null pointers. They will always point to valid C strings of zero or more characters.
application/x-www-form-urlencoded
or
multipart/form-data
, the cgic
library will automatically examine the form data submitted.
If this string has any other non-empty value, a different
type of data has been submitted. This is currently very rare,
as most browsers can only submit forms and file uploads which
cgic parses directly.
In most cases, cgic functions are designed to produce reasonable results even when browsers and users do unreasonable things. However, it is sometimes important to know precisely which unreasonable things took place, especially when assigning a default value or bounding a value is an inadequate solution. The following result codes are useful in making this determination.