|
|
This example can be found in the examples/c/counter directory.
/*-
*
* This code implements an odometer-like page counter extension to the
* AOLserver. The code is based on the popular "odometer" CGI
* by Chris Stephens, stephenc@pcmail.cbil.vcu.edu, and
* Fred Christiansen, fredch@fc.hp.com (see below).
*
* Why use this module instead of the CGI? By loading the code directly
* into the AOLserver, you avoid all the painful overhead of CGI.
* The result is this module is more than 10 times as fast as the CGI!
* Plus, it compiles and run on all platforms, including Windows NT!
*
* To use the counter, load the module and insert an image
* link of the form <IMG SRC="/NS/Counter/my/page.htm">.
* The NsCounter() function will return a small odometer image of the
* number of hits to the corresponding page (i.e., /my/page.htm). The
* page count is kept in a file prefixed by .odo (i.e., /my/.odo.page.htm).
*
* To load this module, add the following to the [ns\server\<vs>\modules]
* section of your nsd.ini file where <vs> is the name of the virtual
* server you want to add the counter too:
*
* counter=counter.dll ; or counter.so on Unix platforms
*
* This prefix(s) of the count URL can be set with the "UrlPrefix"
* key in the module's nsd.ini configuration file section (i.e.,
* [ns\server\<vs>\module\counter]). The value is a comma separated
* list of URL prefixes. For example, if the list includes the
* "/cnt,/count" prefixes, then any URL which begins with /cnt or
* /count will be processed by the NsCounter() function.
*
* Note: the setup interface in AOLserver 2.0 is open-ended, so you can
* use it to view/edit these parameters by using the "List AOLserver
* Configuration Sections" available from the "Setup Home" page in expert
* mode
*
*/
#include "ns.h"
#include <errno.h>
#include <assert.h>
#include <string.h>
#ifdef WIN32
#include <io.h>
#include <fcntl.h>
#else
#include <unistd.h>
#endif
#define CONFIG_PREFIX "UrlPrefix"
#define DEFAULT_PREFIX "/cgi-bin/counter,/NS/Counter,/ns/counter"
#define COUNT_PREFIX ".odo"
static Ns_OpProc NsCounter;
DllExport int Ns_ModuleVersion = 1;
DllExport int
Ns_ModuleInit(char *hServer, char *hModule)
{
char *configPath;
char *urlPrefix;
char *next;
Ns_DString dsBuf;
Ns_DString dsPath;
/*
* Fetch the UrlPrefix from the config file.
*/
configPath = Ns_ConfigGetPath(hServer, hModule, NULL);
urlPrefix = Ns_ConfigGetValue(configPath, CONFIG_PREFIX);
if (urlPrefix == NULL) {
urlPrefix = DEFAULT_PREFIX;
}
/*
* Register NsCounter at each of the URL's specified
* in the comma seperated UrlPrefix list.
*/
Ns_DStringInit(&dsBuf);
Ns_DStringInit(&dsPath);
Ns_DStringAppend(&dsBuf, urlPrefix);
next = dsBuf.string;
do {
urlPrefix = next;
next = strchr(urlPrefix, `,');
if (next != NULL) {
*next++ = `\0';
}
if (*urlPrefix != `/') {
Ns_Log(Warning, "%s(%s): Invalid URL prefix skipped: %s",
hModule, hServer, urlPrefix);
} else {
Ns_NormalizePath(&dsPath, urlPrefix);
urlPrefix = Ns_DStringExport(&dsPath);
Ns_RegisterRequest(hServer, "GET", urlPrefix, NsCounter, ns_free,
urlPrefix, 0);
}
} while (next != NULL);
Ns_DStringFree(&dsBuf);
Ns_DStringFree(&dsPath);
return NS_OK;
}
/*-
** Originally count.c, by Chris Stephens, stephenc@pcmail.cbil.vcu.edu, (c)1995.
** Code cleaned up and enhanced by Fred Christiansen, fredch@fc.hp.com,
** as odometer.c; supports:
** - 8 digits instead of 7 (and by changing ODO_DIGITS, up to 10 (number
** of digits in an unsigned long))
*/
#define NL `\n' /* new line char */
#define ODO_DIGITS 8 /* # digits displayed */
#define BUFSIZE (ODO_DIGITS+2) /* 8 digits + NL + nul */
#define BM_HT 16 /* bitmap height */
#define BM_WD 8 /* bitmap width */
static char *bitmap[] = {
"0xff", "0xff", "0xff", "0xc3", "0x99", "0x99", "0x99", "0x99", /* rows 1-8 of 0 */
"0x99", "0x99", "0x99", "0x99", "0xc3", "0xff", "0xff", "0xff", /* rows 9-16 of 0 */
"0xff", "0xff", "0xff", "0xcf", "0xc7", "0xcf", "0xcf", "0xcf", /* rows 1-8 of 1 */
"0xcf", "0xcf", "0xcf", "0xcf", "0xcf", "0xff", "0xff", "0xff", /* rows 9-16 of 1 */
"0xff", "0xff", "0xff", "0xc3", "0x99", "0x9f", "0x9f", "0xcf", /* rows 1-8 of 2 */
"0xe7", "0xf3", "0xf9", "0xf9", "0x81", "0xff", "0xff", "0xff", /* rows 9-16 of 2 */
"0xff", "0xff", "0xff", "0xc3", "0x99", "0x9f", "0x9f", "0xc7", /* rows 1-8 of 3 */
"0x9f", "0x9f", "0x9f", "0x99", "0xc3", "0xff", "0xff", "0xff", /* rows 9-16 of 3 */
"0xff", "0xff", "0xff", "0xcf", "0xcf", "0xc7", "0xc7", "0xcb", /* rows 1-8 of 4 */
"0xcb", "0xcd", "0x81", "0xcf", "0x87", "0xff", "0xff", "0xff", /* rows 9-16 of 4 */
"0xff", "0xff", "0xff", "0x81", "0xf9", "0xf9", "0xf9", "0xc1", /* rows 1-8 of 5 */
"0x9f", "0x9f", "0x9f", "0x99", "0xc3", "0xff", "0xff", "0xff", /* rows 9-16 of 5 */
"0xff", "0xff", "0xff", "0xc7", "0xf3", "0xf9", "0xf9", "0xc1", /* rows 1-8 of 6 */
"0x99", "0x99", "0x99", "0x99", "0xc3", "0xff", "0xff", "0xff", /* rows 9-16 of 6 */
"0xff", "0xff", "0xff", "0x81", "0x99", "0x9f", "0x9f", "0xcf", /* rows 1-8 of 7 */
"0xcf", "0xe7", "0xe7", "0xf3", "0xf3", "0xff", "0xff", "0xff", /* rows 9-16 of 7 */
"0xff", "0xff", "0xff", "0xc3", "0x99", "0x99", "0x99", "0xc3", /* rows 1-8 of 8 */
"0x99", "0x99", "0x99", "0x99", "0xc3", "0xff", "0xff", "0xff", /* rows 9-16 of 8 */
"0xff", "0xff", "0xff", "0xc3", "0x99", "0x99", "0x99", "0x99", /* rows 1-8 of 9 */
"0x83", "0x9f", "0x9f", "0xcf", "0xe3", "0xff", "0xff", "0xff" /* rows 9-16 of 9 */
};
/*
* NsCounter -
*
* Handle a request of the form "/NS/Counter/my/page.htm" to create an
* odometer-like count of visits to the /my/page.htm page.
*
*/
static int
NsCounter(void *context, Ns_Conn *conn)
{
char buf[BUFSIZE], dial[ODO_DIGITS];
unsigned long cnt;
int status, i, l;
char *p;
Ns_DString ds;
int fd;
char *uri;
char *hServer;
char *urlPrefix;
int nPrefix;
hServer = Ns_ConnServer(conn);
urlPrefix = (char *) context;
/*
* Figure out how many parts are in the
* prefix, i.e., /NS/Counter has 2 parts.
*/
nPrefix = 0;
p = urlPrefix;
while ((p = strchr(p, `/')) != NULL) {
++p;
++nPrefix;
}
if (conn->request->urlc > nPrefix) {
uri = Ns_SkipUrl(conn->request, nPrefix);
} else {
return Ns_ConnReturnBadRequest(conn, "Missing URI");
}
/*
* Convert the URI to the file, sneaking in counter prefix before the last
* path element.
*/
Ns_DStringInit(&ds);
p = strrchr(uri, `/');
*p = `\0';
Ns_UrlToFile(&ds, hServer, uri);
Ns_DStringAppend(&ds, "/");
Ns_DStringAppend(&ds, COUNT_PREFIX);
*p++ = `/';
Ns_DStringAppend(&ds, ".");
Ns_DStringAppend(&ds, p);
/*
* Open (or create) the counter file and increment the page count.
*/
fd = open(ds.string, O_RDWR | O_CREAT, 0660);
if (fd < 0) {
Ns_Log(Error, "Could not open counter file `%s' for URI `%s': %s",
ds.string, uri, strerror(errno));
Ns_DStringFree(&ds);
return Ns_ConnReturnInternalError(conn);
}
Ns_DStringFree(&ds);
i = read(fd, buf, BUFSIZE);
if (i > 0) {
buf[i] = `\0';
cnt = strtoul(buf, (char **) NULL, 10);
} else {
cnt = 0;
}
lseek(fd, 0, SEEK_SET);
sprintf(buf, "%u\n", ++cnt);
write(fd, buf, strlen(buf));
close(fd);
/* Calc length (ignoring NL) and copy right-to-left into dial */
for (l = 0, p = buf; *p != `\0' && *p != NL; l++, p++);
for (i = 0, p = buf; i < l; i++, p++)
dial[ODO_DIGITS - l + i] = *p;
for (i = 0; i < (ODO_DIGITS - l); i++) /* backfill with zeros */
dial[i] = `0';
/*
* Build up the odometer xbm.
*/
Ns_DStringPrintf(&ds, "#define odo_width %d\n", (BM_WD * ODO_DIGITS));
Ns_DStringPrintf(&ds, "#define odo_height %d\n", BM_HT);
Ns_DStringAppend(&ds, "static char odo_bits[] = {\n");
for (i = 0; i < BM_HT; i++) {
for (l = 0; l < ODO_DIGITS; l++) {
Ns_DStringAppend(&ds, bitmap[(((dial[l] - `0') * BM_HT) + i)]);
Ns_DStringAppend(&ds, ", ");
if (l == 7)
Ns_DStringAppend(&ds, "\n");
}
if (i == 15) {
Ns_DStringAppend(&ds, "};");
}
}
Ns_DStringAppend(&ds, "\n");
/*
* Setup the HTTP headers.
*/
Ns_ConnSetRequiredHeaders(conn, "image/x-xbitmap", ds.length);
status = Ns_ConnFlushHeaders(conn, 200);
if (status == NS_OK) {
/*
* Send the bitmap if the headers were sent o.k.
*
* Note that the underlying communications driver may send
* less than the number of bytes requested so we need to
* call Ns_ConnWrite() in a while loop to ensure all the
* bytes are sent. If the connection is shut down while
* attempting to send, the communication driver returns
* -1 and we set status to NS_ERROR and immediately break
* out of the loop. Whenever a status other than NS_OK
* is returned from a request function, the AOLserver does
* not run the registered trace functions (trace functions
* are normally used for access logging and you shouldn't
* normally log aborted connections).
*
* Actually, in this trivial case, you could simply use:
*
* status = Ns_ConnPuts(conn, ds.string)
*
* which pretty much implements the loop below.
*/
p = ds.string;
i = ds.length;
while (i > 0) {
cnt = Ns_ConnWrite(conn, p, i);
if (cnt < 0) {
status = NS_ERROR;
break;
}
i -= cnt;
p += cnt;
}
}
Ns_DStringFree(&ds);
return status;
}