Example Rover Server Plug-In Module: Irolo.c

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


Last updated by $Author: adj $ on $Date: 1997/12/01 23:41:38 $.
Copyright © 1995-1998 Anthony D. Joseph and Massachusetts Institute of Technology