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); }