This tutorial describes the creation of CGI scripts in the C++ programming language. You should have prior knowledge of ANSI C++ before continuing. A number of excellent resources can be found in our CGI Links section.
That said, let’s get down to the nitty-gritty CGI internals! Making CGI scripts is actually quite simple if you know your way around C++. The server automatically takes whatever your program outputs to stdout and redirects it to the browser. So, for instance, if you wanted to send the text “Hello, World!” to the browser, you would simply state:
Code: Select all
cout << "Hello, World!";
The content-type header
Every CGI script must first output a content-type header that specifies the MIME type of the data that is being output by the script - for instance, text/plain if it is outputting plaintext, or text/html if it is outputting an HTML webpage.
The actual code for this header looks like this:
Code: Select all
cout << "Content-type: text/plain" << endl << endl;
Your first CGI script
Armed with this knowledge, we can create our first functional CGI script:
Code: Select all
#include <iostream.h>
void main()
{
cout << "Content-type: text/plain" << endl << endl
<< "Hello, World!";
}
Now, let's spruce it up. Say we want to output not just "Hello, World!" in text, but why not make it pretty with some HTML formatting?
Code: Select all
#include <iostream.h>
void main()
{
cout << "Content-type: text/html" << endl << endl
<< "<html>" << endl
<< "<head>" << endl
<< "<title>CGI Test</title>" << endl
<< "</head>" << endl
<< "<body>" << endl
<< "<h1><em>" << endl
<< "Hello, World!" << endl
<< "</em></h1>" << endl
<< "</body>" << endl
<< "</html>";
}
Using environment variables
Now, outputting an HTML page is all well and good - but why would you want to do so when you could just put the HTML page on the server? Well, here's where the fact that you're using C++ comes in handy.
When the server executes a CGI script, it first sets a number of environment variables that you can retrieve using the getenv() function. These will tell you a number of useful things. Have you ever seen one of those nifty pages that tells you your IP address?
Code: Select all
#include <iostream.h>
#include <stdlib.h>
void main()
{
char* lpszRemoteHost = getenv("REMOTE_HOST");
cout << "Content-type: text/html" << endl << endl
<< "<html>" << endl
<< "<body>" << endl
<< "<p>" < endl
<< "Hello, "
<< lpszRemoteHost
<< "!" << endl
<< "</p>" << endl
<< "</body>" << endl
<< "</html>";
}
Receiving data from a form
Okay, here's where we get into what makes a CGI script really useful; receiving the data sent by a form on a webpage.
Using the POST method, a C++ CGI script retrieves the form data from stdin, the input buffer. The easiest way to do so is with the fread() function. There are a few things that have to be done first, however. You must retrieve the CONTENT_LENGTH environment variable, which tells you how much data is waiting. You must then allocate a buffer of this or greater length to hold the data.
Now, the data comes in a specific format. Each HTML <input> field and its value is sent along. Let's say the following is the form in the webpage:
Code: Select all
<form method="post" action="http://path.to/my/cgi/program">
<input type="hidden" name="value1" value="test1">
<input type="hidden" name="value2" value="test2">
<input type="hidden" name="value3" value="test3">
<input type="submit">
</form>
Anyway, when the user submits this, this is what gets put into stdin for the script:
Code: Select all
value1=test1&value2=test2&value3=test3
Knowing all this, here's a script that sends back what was sent to it:
Code: Select all
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
void main()
{
char* lpszContentLength = getenv("CONTENT_LENGTH");
char* lpszBuffer;
int nContentLength = atoi(lpszContentLength);
lpszBuffer = malloc(lpszContentLength+1); // allocate a buffer
memset(lpszBuffer, 0, lpszContentLength+1); // zero it out
fread(lpszBuffer,1,lpszContentLength,stdin); // get data
cout << "Content-type: text/html" << endl << endl
<< "<html>" << endl
<< "<body>" << endl
<< "<p>" << endl
<< "Hello! You sent " << lpszContentLength << " bytes of data which read: <br>" << endl
<< lpszBuffer << endl
<< "</p>" << endl
<< "</body>" << endl
<< "</html>";
free(lpszBuffer);
}
You probably noticed that the server doesn't do much to parse the form data for you; you have to do that yourself. It's a very repetitive task, so I wrote a couple classes to do so. It's too much code to reproduce here, but you can go ahead and download the source files: These two classes, CCGI and CCGIItem, make it easy to parse the data.
Here's how they work: you create a CCGI and a CCGIItem. Then you call CCGI::Load(), which reads in the data. You can then call CCGI::GetItemCount() to retrieve the number of items that were sent. You then pass CCGI::GetItem for however many items you want, passing a pointer to the CCGIItem which is filled with that item's name and value. It's quite easy to use, once you get used to it. Read through the source and you should get a better understanding of how it works (it's fairly well commented).
There are many other libraries that provide much more complete CGI functionality; cgic and libcgi++ are two examples. You'll find them in the CGI links section.
And here's my final example, using my CGI classes:
Code: Select all
#include <iostream.h>
#include <malloc.h>
#include "CGI.h"
void main()
{
CCGI cgi;
CCGIItem item;
char* lpszOut;
char* lpszContentLength = getenv("CONTENT_LENGTH");
int i,nLength;
cout << "Content-type: text/html" << endl << endl
<< "<html>" << endl
<< "<body>" << endl
<< "<p>You sent " << lpszContentLength
<< " bytes of data, which contained the following values: </p>" << endl
<< "<ul>" << endl;
cgi.Load();
for(i=0; i<cgi.GetItemCount(); i++)
{
cgi.GetItem(i,&item);
nLength = item.GetNameLength();
lpszOut = (char*) malloc(nLength);
item.GetName(lpszOut,nLength);
cout << "<li>" << lpszOut << " - ";
free(lpszOut);
nLength = item.GetLength();
lpszOut = (char*) malloc(nLength);
item.GetValue(lpszOut,nLength);
cout << lpszOut << endl;
free(lpszOut);
}
cout << "</ul>" << endl
<< "</body>" << endl
<< "</html>" << endl;
}