1. Introduction
1.1 Things you have to know
1.2 What you need
1.2.1 Server
1.2.2 Compiler
2. Go!
2.1 Hello World!
2.2 Installing/running CGI's
3. Output
3.1 The first counter
3.2 Files and paths
3.3 Environment variables
4. Input
4.1 Encoded string
4.2 Cgilib
4.3 GET/POST
5. One step down
5.1 Request a file
5.2 GET
5.3 POST
However, just ask your provider first if they support CGI, and if they say 'no' then just ask again, or ask why not, or send them a bottle of wine and ask again, and finally don't forget to tell me if you were succesfull :)
I know, there is also UNIX out there. I concentrate more on the windows-stuff here, but the basics are the same on every operating systems. Perhaps your directories will have different names or you'll have to set the appropriate rights under UNIX.
Of course you don't have to write your own web-server or ugly things like that. It's much easier: If someone requests a file that is a CGI, the CGI is run by the server and the output of the program will be sent back to the client instead of sending the content of the file.
But how does the server know what files are CGI's? This is usually indicated by a special directory that contains only executable CGI's and no .html, .gif, etc...-files. This will then look something like this: '/scripts/hello.exe'
[ -- CODE 'hello.c' -- ]
#include <stdio.h>
void main()
{
printf("Content-Type: text/html\n\n");
printf("<html>\n");
printf("<head>\n");
printf(" <title>Hello World</title>\n");
printf("</head>\n");
printf("<body>\n");
printf(" <h1>Hello World!</h1>\n");
printf(" cgi rulez!\n");
printf("</body>\n");
printf("</html>\n");
}
[ -- CODE ENDS -- ]
As you can see this program produces this output:
[ -- TERMINAL -- ] Content-Type: text/html <html> <head> <title>Hello World</title> </head> <body> <h1>Hello World!</h1> cgi rulez! </body> </html> [ -- TERMINAL ENDS -- ]this can be split-up in two parts: the header and the data. The header consist of the line 'Content-Type: text/html', the data is a simple HTML-file. As you can see the header and the data are separated by a single empty line. This is in fact what every CGI-programm looks like. We will discuss the header (it's a part of the HTTP-Header of the server response) later. Let's first figure out how to install and run the program as a CGI.
http://www.provid.er/scripts/arthur/hello.exe
in your web-browser. If everything works fine you should see the output of the CGI in your browser. If the browser wants do download the .exe-file something went wrong and the file has not been recognised as a CGI (If you are using GetRight you may get some general problems with .exe-files, because it treats them as normal .exe files you can download! Try renaming the .exe- file to .cgi).
If everything works fine so far I congratulate you, and you better clean your desk and prepare for a huge phone-bill, because you will be coding CGI's for the rest of your life from now on :)
hello.exe >output.htm
if the exe produces an HTML-file as output (hello.exe does), you can cut of the 'Content-Type'-header with a text-editor and open the file with a web-browser. This is a simple and useful way to test your CGI's if you aren't running your own CGI-server.
[ -- PSEUDOCODE -- ] - read the number of visitors from a file - increase the number of visitors by 1 - write number of visitors back to file - print 'Content-Type'-header - print HTML (containing the count, some blabla-stuff, the date and time) [ -- PSEUDOCODE ENDS -- ]Ok. In C, this would look something like this:
[ -- CODE 'count.c' -- ]
#include <stdio.h>
#include <dos.h>
#define COUNTFILE "count.txt"
void WriteN(int n)
{
FILE *file = fopen(COUNTFILE, "w+b");
fprintf(file, "%i", n);
fclose(file);
}
int GetN()
{
int n;
FILE *file = fopen(COUNTFILE, "rb");
if(file == NULL)
{ //File doesn't exist
return 0;
}
fscanf(file, "%i", &n);
fclose(file);
return n;
}
void main()
{
int n_visitors;
struct date d;
struct time t;
n_visitors = GetN()+1; //Get number of visitors
WriteN(n_visitors); //Increment number of visitors
getdate(&d); //Get date & time
gettime(&t);
printf("Content-Type: text/html\n\n");
printf("<html>\n");
printf("<head>\n");
printf(" <title>an example</title>\n");
printf("</head>\n");
printf("<body>\n");
printf(" <h1>welcome to my homepage!</h1>\n");
printf(" Hi! You are visitor number %i.<p>\n", n_visitors);
printf(" The current year is: %d<br>\n", d.da_year);
printf(" The current day is: %d<br>\n", d.da_day);
printf(" The current month is: %d<br>\n", d.da_mon);
printf(" The current time is: %2d:%02d:%02d.%02d<br>\n", t.ti_hour, t.ti_min,
t.ti_sec, t.ti_hund);
printf("</body>\n");
printf("</html>\n");
}
[ -- CODE ENDS -- ]
The output may look like this:
[ -- TERMINAL -- ] Content-Type: text/html <html> <head> <title>an example</title> </head> <body> <h1>welcome to my homepage!</h1> Hi! You are visitor number 5.<p> The current year is: 1998<br> The current day is: 18<br> The current month is: 8<br> The current time is: 18:42:42.39<br> </body> </html> [ -- TERMINAL ENDS -- ]
If you don't have a directory with writing permission, there is still a very bad and ugly way to write/read data. You could implement an FTP-client in your CGI's that connects to itself (the CGI-server) and does all the file-operations via FTP. Ugly, but it works :)
If you've however got such a directory, you should check if you can read from it via HTTP, e.g. with
http://www.provid.er/scripts/data/thesecretdata.dat
from your web-browser. This has some advantages, but also some very big disadvantages. The advantages are that you can write a .html-file there and request it directly from the client, without running a CGI first. This is sometimes useful, for example in a guestbook. You'll have a file like /guestbook.htm in the data-directory that will be updated by a CGI and requested directly via HTTP. The disadvantages are that you shouldn't store secret data there. If you're planning to code a CGI-chat with user accounts, you shouldn't store the usernames and passwords there because everyone can download them :) The most convenient method would be to have two different directories, 'pubdata' and 'privdata'.
[ -- CODE 'envdemo.c' -- ]
#include <stdlib.h>
#include <stdio.h>
void main()
{
char *name;
name = getenv("NAME");
printf("%s\n", name);
}
[ -- CODE ENDS -- ]
The clue is: Before the server runs a CGI-program, it sets some environment-
variables to nice values. The most interesing ones are:
GATEWAY_INTERFACE Version of CGI protocol (usually CGI/1.1) SERVER_PROTOCOL Version of HTTP protocol (usually HTTP/1.0) REQUEST_METHOD Is 'GET' or 'POST' (See chapter 'Input') PATH_TRANSLATED Path on the server QUERY_STRING Input-data if REQUEST_METHOD = GET (See 'Input') CONTENT_LENGTH Length of input for REQUEST_METHOD = POST (See 'Input') SERVER_SOFTWARE Name and version of server software SERVER_NAME Name of the server (DNS-name) SERVER_ADMIN E-mail address of system-administrator SERVER_PORT HTTP-port of server (usually 80) SCRIPT_NAME Path and name of CGI-program REMOTE_HOST Name of the client (or IP if no name) REMOTE_ADDR IP of the client REMOTE_USER Name of the user REMOTE_GROUP Group of the user HTTP_ACCEPT List of MIME-types the client accepts HTTP_USER_AGENT Name, version and OS of the client's browser HTTP_REFERER URL the client visited before it run the CGI HTTP_ACCEPT_LANGUAGE Supported language HTTP_COOKIE Cookie-valuesAs you see you can find out quite a lot about the guy who starts your CGI-program with his browser. But the environment-variables are also essential for the CGI-input. See next chapter for more info. Here's a little program that sends the values of all environment-vars back to the client. 'text/plain' is used here as content-type, so the respons is in plain text.
[ -- CODE 'getenv.c' -- ]
#include <stdio.h>
#include <dos.h>
#include <string.h>
#include <stdlib.h>
void Get(char *s)
{
printf("'%s' = '%s'\n", s, getenv(s));
}
void main()
{
printf("Content-Type: text/plain\n\n");
Get("GATEWAY_INTERFACE");
Get("SERVER_PROTOCOL");
Get("REQUEST_METHOD");
Get("PATH_INFO");
Get("PATH_TRANSLATED");
Get("QUERY_STRING");
Get("CONTENT_TYPE");
Get("CONTENT_LENGTH");
Get("SERVER_SOFTWARE");
Get("SERVER_NAME");
Get("SERVER_ADMIN");
Get("SERVER_PORT");
Get("SCRIPT_NAME");
Get("DOCUMENT_ROOT");
Get("REMOTE_HOST");
Get("REMOTE_ADDR");
Get("REMOTE_USER");
Get("REMOTE_GROUP");
Get("AUTH_TYPE");
Get("REMOTE_IDENT");
Get("HTTP_ACCEPT");
Get("HTTP_USER_AGENT");
Get("HTTP_REFERER");
Get("HTTP_ACCEPT_LANGUAGE");
Get("HTTP_COOKIE");
}
[ -- CODE ENDS -- ]
... and here a possible output:
[ -- TERMINAL -- ] Content-Type: text/plain 'GATEWAY_INTERFACE' = 'CGI/1.1' 'SERVER_PROTOCOL' = 'HTTP/1.0' 'REQUEST_METHOD' = 'GET' 'PATH_INFO' = '(null)' 'PATH_TRANSLATED' = 'C:\internet\InetPub\wwwroot' 'QUERY_STRING' = 'name=Arthur&var2=blabla&var3=hellou' 'CONTENT_TYPE' = '(null)' 'CONTENT_LENGTH' = '0' 'SERVER_SOFTWARE' = 'Microsoft-IIS/3.0' 'SERVER_NAME' = 'www2.agri.ch' 'SERVER_ADMIN' = '(null)' 'SERVER_PORT' = '80' 'SCRIPT_NAME' = '/scripts/arthur/getenv.exe' 'DOCUMENT_ROOT' = '(null)' 'REMOTE_HOST' = '194.6.166.201' 'REMOTE_ADDR' = '194.6.166.201' 'REMOTE_USER' = '(null)' 'REMOTE_GROUP' = '(null)' 'AUTH_TYPE' = '(null)' 'REMOTE_IDENT' = '(null)' 'HTTP_ACCEPT' = 'image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, */*' 'HTTP_USER_AGENT' = 'Mozilla/2.0 (compatible; MSIE 3.0; SK; Windows 95)' 'HTTP_REFERER' = '(null)' 'HTTP_ACCEPT_LANGUAGE' = 'de' 'HTTP_COOKIE' = '(null)' [ -- TERMINAL ENDS -- ]
varname1=value1&varname2=value2&varname3=value3...
special-characters are translated into their ASCII-code in hex, so '(' becomes '%28' for example. Space (' ') can be translated into '%20' or '+', so the string
'hello world(20)'
encodes into:
'hello+world%2820%29'