C-Scene Issue #06
CGI Bits & Pieces #1
Jürgen Hermann

CGI Bits & Pieces #1

CGI Bits & Pieces is a new column in which I will present bits and pieces on different CGI-related issues. If you are a CGI novice, see the references at the end of this article. Having read Brent's article is the bare minimum to understand anything that follows.

All examples are implemented in C++ and usually compiled and tested on both Windows NT4 using Visual C++ 5.0 and FreeBSD using gcc.

Sending image files via CGI

In this installment, we want to send an image file via CGI to the browser. You might ask, and rightly so, what is the purpose of this since any web server can send images by itself, without the need for a cgi-bin program.

Well, there are several valid reasons. For one, you might store your images in a modern RDBMS (relational database management system) that supports BLOBs (binary large objects). To get your images to the user's browser, you obviously have to retrieve them from the database server first, which no current httpd will do for you.

Another reason is that the image file does not have to be in your public WWW area, so you can send any file in the filesystem the web server (or actually, your cgi-bin) has access to. This of course can also be a security hole, and in the example source presented here this is not checked. If you use it for anything real, add some code to replace all slashes with underscores or something similar that prevents opening up your file system.

Other typical applications are images that are generated on the fly, for example by using ImageMagick. Actual examples on the web are statistical graphs of web server usage, or the infamous GIF counters. Further, banner type programs contain an algorithm that decides to send one of several possible images; this might serve as a exercise for the reader, extend the program to send the file bgndX.gif, where X is a random number between 0 and 9. Then use that modified program in the background image URL of a web page

    <BODY BACKGROUND="/cgi-bin/randomimage">
and you get a nice effect when you hit "Reload".

Finally, sending binary data instead of the usual text/plain or text/html responses gives us the chance to see what is necessary to do that on a Windows system. If you approach the task in a naive way, you will get broken image data because of the CP/M and MSDOS heritage still lurking in the dusty corners of your Windows OS. Namely, by default any line feed (LF, '\n') you send to standard output is automatically converted to a CRLF pair, which is the standard Windows line break marker. At the very start of main(), you can see how we can prevent the runtime library from doing that.

An example implementation

Let's get to the details of sending an image file via HTTP. The task at hand can be broken down into three major pieces: getting the image name from the CGI environment, sending the necessary MIME headers, and finally streaming the image data to the browser. You can find that structure reflected in the main of the sample program.

To send the image file, we use a helper function named dumpFile that opens a file and sends its contents to the standard output stream.

    // Send an image file via CGI
    //
    // Copyright (c) 1998 by Jürgen Hermann, All Rights Reserved.

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <iostream.h>
    #include <fstream.h>

    #if _WIN32
    #include <fcntl.h>
    #include <io.h>

    #define strcasecmp stricmp
    #endif


    void cgiError()
    {
        // Most simple error handling
        cout << endl;
        exit(0);
    }


    void dumpFile(const char* filename)
    {
        ifstream file(filename, ios::in | ios::binary);

        if (file) {
            while (!file.eof()) {
                char buf[1024];

                file.read(buf, sizeof(buf));
                int count = file.gcount();
                if (count) {
                    cout.write(buf, count);
                } else {
                    break;
                }
            }

            cout.flush();
        }
    }


    int main()
    {
    #if _WIN32
        // Standard I/O is in text mode by default; since we intend
        // to send binary image data to standard output, we have to
        // set it to binary mode.
        // Error handling is tricky to say the least, so we have none.
        _fmode = _O_BINARY;
        if (_setmode(_fileno(stdin), _fmode) == -1) {}
        if (_setmode(_fileno(stdout), _fmode) == -1) {}
    #endif

        // First, we get the filename of the image to send
        char* querystr = getenv("QUERY_STRING");
        char* imgparm  = querystr ? strstr(querystr, "image=") : 0;
        char imagename[81];
        if (!imgparm || 1 != sscanf(imgparm, "image=%80[^&]", imagename)) {
            cgiError();
        }

        // Get the image type and send the headers
        const char* imgtype = "gif"; // assume gif by default
        char* ptr = strrchr(imagename, '.');
        if (ptr && (strcasecmp(ptr, ".jpg") == 0 || strcasecmp(ptr, ".jpeg") == 0)) {
            imgtype = "jpeg";
        }

        cout << "Content-Type: " << "image/" << imgtype << endl;
        cout << endl;

        // Send the image
        dumpFile(imagename);

        return 0;
    }

How to install

To compile the program with Visual C++ on Win32, get the ZIP file containing the source, a VC5 project and the compiled executable (i.e. you do not need a compiler to test it on Windows). To compile on UNIX with gcc, simply call g++ image.cpp.

After you have successfully compiled the executable, copy the result and the example image (what else than cscene.jpg could be appropriate) to your cgi-bin directory. To test it on a UNIX server, call the URL

    http://localhost/cgi-bin/image?image=cscene.jpg
or for Windows
    http://localhost/cgi-bin/image.exe?image=cscene.jpg

References

Gundavaram, Shishir, «CGI Programming on the World Wide Web».
O'Reilly, 1996, 433 p., covers CGI basics as well as hot topics like data base interfacing, details of the HTTP protocol and their use, forms, server side includes, hyper-media, client-pull and server-push (examples in Perl), ISBN 1-56592-168-2.

York, Brent, «CGI in C a starters tutorial».
C Scene Issue #02, 1997.


This page is Copyright © 1998 by Jürgen Hermann and Copyright © 1998 by C Scene. All Rights Reserved.