The following C code can be found in the cgi-src/rover/irolo.c file.
/**********************************************************************
*
*
* ROVER object execution engine
* Anthony D. Joseph
* Laboratory for Computer Science
* Massachusetts Institute of Technology
*
* Module: irolo.c
*
* Description: Server C module for the Irolo graphical Rolodex application.
*
*
* Global Functions:
* void irolo_import(char *object, char *objclass);
* void irolo_export(char *object, char *objclass);
*
* Global Variables: None
*
*
*
***********************************************************************
* Copyright 1996
* Anthony D. Joseph
* Massachusetts Institute of Technology
*
* Permission to use, copy, modify, and distribute this program
* for any purpose and without fee is hereby granted, provided
* that this copyright and permission notice appear on all copies
* and supporting documentation, the name of M.I.T. not be used
* in advertising or publicity pertaining to distribution of the
* program without specific prior permission, and notice be given
* in supporting documentation that copying and distribution is
* by permission of M.I.T. M.I.T. makes no representations about
* the suitability of this software for any purpose. It is pro-
* vided "as is" without express or implied warranty.
***********************************************************************/
#include "rover.h"
#include "database.h"
#define IROLO_DBNAME ".irolo-db"
#define IROLO_ENTRY "Rover__Irolo__%s__Entry__%s"
#define IROLO_IDX "Rover__Irolo__%s__Index"
#define IROLO_VERSION 1
struct _Index {
time_t time;
int version;
char names[0]; /* Irolo name entries */
};
typedef struct _Index Index;
struct _Entry {
time_t time;
char record[0]; /* Marshalled entry */
};
typedef struct _Entry Entry;
PRIVATE Database currdb;
PRIVATE char line[2050], tmp[2048];
/*
* Create a new irolo database.
* Initializes the database by creating a null Index entry.
*/
PRIVATE void irolo_builder(Database *db)
{
Index *idx;
datum info, rq;
int rc, write = db->write;
/* Here we make an Irolo Index object. Allocate an extra byte to hold
the null terminator for the names field (which is zero-bytes long
by default
*/
if ((idx = (Index *)malloc(sizeof(Index)+1)) == NULL) {
status = 500;
errorExit("Out of memory");
}
time(&(idx->time)); /* Set the creation time */
idx->version = IROLO_VERSION; /* Set the db version */
idx->names[0] = '\0'; /* Empty name field */
if (!write) db_reopenWrite(db); /* If necessary, Reopen the db */
sprintf(line, IROLO_IDX, ClientUser); /* Construct the Index db entry name */
rq.dptr = line;
rq.dsize = strlen(line)+1;
info.dptr = (char *) idx;
info.dsize = sizeof(Index)+1;
rc = gdbm_store(db->db, rq, info, GDBM_REPLACE);
if (rc != 0) {
status = 500;
errorExit("Error while creating Irolo index");
}
if (!write) db_reopenRead(db);
free((char *) idx);
}
PUBLIC void irolo_import(char *object, char *objclass)
{
char *name, *value, user[32];
datum info, rq;
Index *idx;
Entry *entry;
if (Nobody) { /* Extra paranoia check */
status = 500;
errorExit("Server reached protected procedure");
}
fprintf(stderr, "Irolo import\n");
name = index(objclass, '_');
name += 2;
value = index(name, '_');
*value = '\0';
strcpy(user, name);
*value = '_';
value += 2;
if (strcmp(ClientUser, user)) {
sprintf(line, "Username mismatch! %s vs. %s", user, ClientUser);
status = 401;
errorExit(line);
}
ObjType = ROVER_TCL_TK;
sprintf(tmp, "%s/%s", Home, IROLO_DBNAME);
db_openRead(tmp, &currdb, irolo_builder);
if (!strcmp("Index", value)) {
/* Request is for the irolo index */
/* IROLO INDEX */
fprintf(stderr, "Producing irolo index object\n");
/* Here we make an Irolo Index object */
rq.dptr = Object;
rq.dsize = strlen(Object)+1;
info = gdbm_fetch(currdb.db, rq);
if (info.dptr == NULL) {
db_close(&currdb);
status = 500;
errorExit("Unable to find index in Irolo database");
}
idx = (Index *)(info.dptr);
dvtime = idx->time;
if (dvtime <= UserTime) {
/* We've verified that the object hasn't changed at the server */
fprintf(stderr, "Verified object `%s', returning header\n", Object);
Cacheable = ROVER_VERIFIED;
db_close(&currdb);
free(idx);
return;
} else if (UserTime) {
fprintf(stderr, "Updating client's copy of object `%s'\n", object);
ObjType = ROVER_TCL_TK_UPDATE;
}
fprintf(stderr, "Object `%s' time is %lu vs. user time %lu\n", Object,
dvtime, UserTime);
db_setAccessTime(&currdb, Object, dvtime); /* Note the access time */
/* The code section is simply the unmarshall command */
outstrVC("return \"Rolo-with-name %0\"\n", 1, Object);
#if 0
The Irolo Index object contains the following fields:
struct {
char *name; /* Name of the entry */
char *status; /* Status of the entry (Loading, Loaded, etc.) */
};
The marshalled format is:
: :
Irolo Index objects are stored in an already marshalled format.
#endif
/* Now, we emit the marshalled irolo index data */
emitData();
name = idx->names;
outstrD(name);
} else if (!strncmp("Entry__", value, strlen("Entry__"))) {
/* Request for specific irolo entry */
/* IROLO ENTRY FETCH */
name = index(value, '_');
name += 2;
name = escape_url(name);
fprintf(stderr, "Producing irolo entry\n");
/* Here we make an Irolo entry object */
sprintf(tmp, IROLO_ENTRY, user, name);
free(name);
rq.dptr = tmp;
rq.dsize = strlen(tmp)+1;
info = gdbm_fetch(currdb.db, rq);
if (info.dptr == NULL) {
db_close(&currdb);
status = 500;
sprintf(line, "Unable to find irolo entry `%s' in database", tmp);
errorExit(line);
}
entry = (Entry *)info.dptr;
dvtime = entry->time;
if (dvtime <= UserTime) {
/* We've verified that the object hasn't changed at the server */
fprintf(stderr, "Verified object `%s', returning header\n", object);
Cacheable = ROVER_VERIFIED;
db_close(&currdb);
free(entry);
return;
} else if (UserTime) {
fprintf(stderr, "Updating client's copy of object `%s'\n", object);
ObjType = ROVER_TCL_TK_UPDATE;
}
fprintf(stderr, "Object `%s' time is %lu vs. user time %lu\n", object,
dvtime, UserTime);
db_setAccessTime(&currdb, tmp, dvtime); /* Note the access time */
/* The code section is simply the unmarshall command */
outstrVC("return \"Entry-with-name %0\"\n", 1, tmp);
#if 0
An Irolo entry object contains the following fields:
struct {
char *text; /* The text of the entry */
};
The marshalled format is:
: :
#endif
/* Now, we emit the marshalled irolo entry data */
emitData();
outstrD(entry->record);
free(entry);
}
db_close(&currdb);
}
PUBLIC void irolo_export(char *object, char *objclass)
{
int rc, new, size;
char *record, *value, *name, *names, user[32], *code = NULL;
datum info, rq;
Index *idx = NULL;
Entry *entry = NULL;
time_t userlast, last, current;
if (Nobody) {
status = 500;
errorExit("Server reached protected procedure");
}
fprintf(stderr, "Irolo export\n");
name = index(objclass, '_');
name += 2;
value = index(name, '_');
*value = '\0';
strcpy(user, name);
*value = '_';
value += 2;
if (strcmp(ClientUser, user)) {
sprintf(line, "Username mismatch! %s vs. %s", user, ClientUser);
status = 401;
errorExit(line);
}
ObjType = ROVER_LOG_SUFFIX;
Cacheable = ROVER_NO_CACHE;
sprintf(tmp, "%s/%s", Home, IROLO_DBNAME);
db_openWrite(tmp, &currdb, irolo_builder);
hentry = Tcl_FindHashEntry(&rqpairs, "CODE");
if (!hentry || !strlen(code = Tcl_GetHashValue(hentry))) {
status = 400;
errorExit("No code (CODE) string found");
}
if (createTclInterp() < 0) {
status = 500;
errorExit("Server unable to create tcl interpreter");
}
Tcl_LoadLibrary("irolo.lib");
if (!strcmp("Index", value)) {
/* IROLO INDEX */
/* Export operation is for the irolo index:
1. Load the object.
2. Execute the specified code.
3. Save the object, if modified.
4. Generate a log suffix to reflect the results.
*/
fprintf(stderr, "Manipulating an irolo index object\n");
/* 1. Load an Irolo Index object */
sprintf(line, IROLO_IDX, user);
rq.dptr = line;
rq.dsize = strlen(line)+1;
info = gdbm_fetch(currdb.db, rq);
idx = (Index *)(info.dptr);
if (idx == NULL) {
new = 1;
names = "";
last = 0;
} else {
new = 0;
names = idx->names;
last = idx->time;
}
fprintf(stderr, "%s object `%s' time is %lu vs. user time %lu\n",
(new ? "New" : "Existing"), object, last, UserTime);
/* Now, create the object */
time(¤t);
Tcl_SetVar(globalTclInterp, "args", names, TCL_GLOBAL_ONLY);
sprintf(tmp, "Rolo-with-name %s $args %lu\n",line,(unsigned long) current);
if ((rc = Tcl_GlobalEval(globalTclInterp, tmp)) != TCL_OK) {
sprintf(line, "TCL EVAL ERR: %s [irolo_export:cmd]\n",
globalTclInterp->result);
status = 400;
errorExit(line);
}
userlast = db_getAccessTime(&currdb, line); /* Get the last access time */
if (userlast < UserTime) userlast = UserTime;
if (info.dptr) free(info.dptr);
idx = NULL;
/* 2. Execute the specified code */
fprintf(stderr, "Executing: |%s|\n", code);
if ((rc = Tcl_GlobalEval(globalTclInterp, code)) != TCL_OK) {
sprintf(line, "TCL EVAL ERR: %s [irolo_export:code]\n",
globalTclInterp->result);
status = 400;
errorExit(line);
}
fprintf(stderr, "Result is `%s'\n", globalTclInterp->result);
/* 3. Save the object, if modified. If the object is modified, the
code returns the marshalled version of the object */
if ((size = strlen(globalTclInterp->result)) != 0) {
if ((idx = (Index *)malloc(sizeof(Index)+size+1)) == NULL) {
status = 500;
errorExit("Out of memory");
}
last = idx->time = current;
strcpy((char *) (&(idx->names)), globalTclInterp->result);
rq.dptr = line;
rq.dsize = strlen(line)+1;
info.dptr = (char *) idx;
info.dsize = sizeof(Index)+size+1;
rc = gdbm_store(currdb.db, rq, info, GDBM_REPLACE);
free((char *) idx);
if (rc != 0) {
status = 500;
errorExit("Error while updating Irolo index");
}
}
/* 4. Generate a log suffix to reflect the results. */
emitLog();
log_suffix(&currdb, line, userlast, log_outstrSuffix);
dvtime = last;
db_setAccessTime(&currdb, line, last); /* Note the access time */
} else if (!strncmp("Entry__", value, strlen("Entry__"))) {
/* IROLO ENTRY EXPORT */
/* Export operation is for a specific irolo entry:
1. Load the object.
2. Execute the specified code.
3. Save the object, if modified.
4. Generate a log suffix to reflect the results.
*/
name = index(value, '_');
name += 2;
name = escape_url(name);
fprintf(stderr, "Manipulating an irolo entry object\n");
/* 1. Load an Irolo Entry object */
sprintf(line, IROLO_ENTRY, user, name);
free(name);
rq.dptr = line;
rq.dsize = strlen(line)+1;
info = gdbm_fetch(currdb.db, rq);
entry = (Entry *)(info.dptr);
if (entry == NULL) {
new = 1;
record = "";
last = 0;
} else {
new = 0;
record = entry->record;
last = entry->time;
}
fprintf(stderr, "%s object `%s' time is %lu vs. user time %lu\n",
(new ? "New" : "Existing"), object, last, UserTime);
/* Now, create the object */
time(¤t);
Tcl_SetVar(globalTclInterp, "args", record, TCL_GLOBAL_ONLY);
sprintf(tmp,"Entry-with-name %s $args %lu\n",line,(unsigned long) current);
fprintf(stderr, "Command is `%s'\n", tmp);
if ((rc = Tcl_GlobalEval(globalTclInterp, tmp)) != TCL_OK) {
sprintf(line, "TCL EVAL ERR: %s [irolo_export:cmd]\n",
globalTclInterp->result);
status = 400;
errorExit(line);
}
userlast = db_getAccessTime(&currdb, line); /* Get the last access time */
if (userlast < UserTime) userlast = UserTime;
if (info.dptr) free(info.dptr);
/* 2. Execute the specified code */
fprintf(stderr, "Executing: |%s|\n", code);
if ((rc = Tcl_GlobalEval(globalTclInterp, code)) != TCL_OK) {
sprintf(line, "TCL EVAL ERR: %s [irolo_export:code]\n",
globalTclInterp->result);
status = 400;
errorExit(line);
}
/* 3. Save the object, if modified. If the object is modified, the
code returns the marshalled version of the object */
if ((size = strlen(globalTclInterp->result)) != 0) {
if ((entry = (Entry *)malloc(sizeof(Entry)+size+1)) == NULL) {
status = 500;
errorExit("Out of memory");
}
last = entry->time = current;
strcpy((char *) (&(entry->record)), globalTclInterp->result);
rq.dptr = line;
rq.dsize = strlen(line)+1;
info.dptr = (char *) entry;
info.dsize = sizeof(Entry)+size+1;
rc = gdbm_store(currdb.db, rq, info, GDBM_REPLACE);
free((char *) entry);
if (rc != 0) {
status = 500;
errorExit("Error while updating Irolo entry");
}
}
/* 4. Generate a log suffix to reflect the results. */
emitLog();
log_suffix(&currdb, line, userlast, log_outstrSuffix);
dvtime = last;
db_setAccessTime(&currdb, line, last); /* Note the access time */
}
deleteTclInterp();
db_close(&currdb);
}