/*
 * Copyright 1990 by Baylor College of Medicine ALL RIGHTS RESERVED. 
 *
 * This program is subject to a license agreement between 
 * Baylor College of Medicine and MIT. Any use inconsistent with
 * said license and any use by persons other than the faculty, 
 * students and staff at MIT or any use on a computer not operated 
 * as part of the Athena Computing Environment (ACE) is expressly 
 * prohibited.
 */
/****************************************************************
 * File: srvlib.c 
 * Date: 02/15/91
 * Description:
 *   This file contains the entire application programming 
 *   interface for the srvlib. All communication with the
 *   database server (dbsrv) is accomplished using this 
 *   application programming interface.
 *
 * Definitions used in this file:
 *   aid - Author Id
 *   nid - Notebook Id
 *   pid - Page Id
 *   oid - Object Id
 *
 * Revisions:
 * 01/12/91 (BDM): added srv_add_event_handler and srv_dispatch_event.
 *   These functions were added to allow different groups within
 *   a particular application to register event handlers 
 *   independently of each other and that these handlers would
 *   be called using srv_dispatch_event.
 * 01/17/91 (BDM): added the functions srv_host, srv_port, and
 *   srv_database. The function srv_init was modified to store
 *   the calling parameters into the connection record so that
 *   this info could later be retrieved via the functions added.
 * 01/22/91 (BDM): added error handling to the srvlib. The main
 *   function added was srv_error which routes error message to a
 *   logfile (default stderr). Several support functions were also
 *   added. The srv_error function is now used in all public
 *   functions so that wach may be traced. An environment variable
 *   SRVTRACE can be set to allow different levels of printing to
 *   the logfile. Those levels are:
 *     SRVTRACE = 3 - Fatal errors only will be logged (the default)
 *              = 2 - Errors and fatal errors will be logged.
 *              = 1 - Warnings, errors, and fatal errors will be logged.
 *              = 0 - Everything is logged
 * 02/15/91 (BDM): reworked the entire library so that the connection
 *   record is now dynamically allocated and returned as a pointer
 *   to a structure. As a result of this change, the calling 
 *   interface of all the functions was modified to handle the 
 *   new connection pointer. Also, checking for a NULL connection
 *   pointer was added (there was no checking in the previous version).
 *   Also, srv_validate_page was modified to return different flags
 *   (positive) to avoid conflict with the standard error return 
 *   flags. The standard return flag was adopted where the following
 *   is returned by most library functions unless otherwise specified:
 *      Error Flag =  0 - no error
 *                 = -1 - warning
 *                 = -2 - error
 *                 = -3 - fatal error
 * 03/21/91 (BDM): added documentation to functions that allocate
 *   memory that must be freed by the front-end application. This
 *   information has been added under the heading "Notes:" for 
 *   functions that allocate memory.
 * 05/02/91 (BDM): changed getuid to geteuid. This allows programs
 *   using this library to be run using setuid.
 ****************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/uio.h>
#include <malloc.h>

/* inlcude the VNS protocol definitions (stored with dbsrv) */
#include <vns_proto.h>

#include "srvlib.h"

/* major and minor protocol version is set the same as dbsrv */
static int srv_proto_major = VNS_PROTO_MAJOR;
static int srv_proto_minor = VNS_PROTO_MINOR;

/* define Public and Private */
#define PUBLIC 
#define PRIVATE static

#define ERR_SIZE 11  /* size of the error table */

/* error lookup table */
static ErrorRec error_table[ERR_SIZE] = 
{ /*  level        error id              error message */
	{ SRV_OK,      SRV_OK,               "no error"},
	{ SRV_WARNING, SRV_NOT_AUTHORIZED,   "not authorized to execute function"},
	{ SRV_ERROR,   SRV_READ,             "read on socket failed"},
	{ SRV_ERROR,   SRV_WRITE,            "write on socket failed"},
	{ SRV_ERROR,   SRV_WAIT,             "wait for reply failed"},
	{ SRV_ERROR,   SRV_CONTEXT,          "srvlib context is NULL"},
	{ SRV_FATAL,   SRV_MALLOC,           "malloc failed"},
	{ SRV_FATAL,   SRV_GETHOST,          "gethostbyname failed"},
	{ SRV_FATAL,   SRV_SOCKET,           "socket failed"},
	{ SRV_FATAL,   SRV_CONNECT,          "connect failed"},
	{ SRV_FATAL,   SRV_VERSION,          "version checking"},
};

/****************************************************************
 *                     PRIVATE FUNCTIONS
 ****************************************************************/

/****************************************************************
 * Function: find_error_level 
 * Date: 01/18/91
 *
 * Description:
 *   Find the error level associated with a given error flag.
 * 
 * Linkage: int find_error_level(error_id)
 *   int err - error id
 *
 * Returns: return the error level or -1 otherwise
 *
 * Revisions:
 ****************************************************************/
PRIVATE
int
find_error_level(id)
	int id;
{
	int i;
	for (i = 0; i < ERR_SIZE; ++i)
	{
		if (error_table[i].id == id)
		{
			return error_table[i].level;
		}
	}
	return -1;
}

/****************************************************************
 * Function: find_error_name 
 * Date: 01/18/91
 *
 * Description:
 *   Find the error name associated with a given error flag.
 * 
 * Linkage: char *find_error_name(error_id)
 *   int err - error id
 *
 * Returns: the error name or NULL if error id not found in table.
 *
 * Revisions:
 ****************************************************************/
PRIVATE
char *
find_error_name(id)
	int id;
{
	int i;
	for (i = 0; i < ERR_SIZE; ++i)
	{
		if (error_table[i].id == id)
		{
			return error_table[i].name;
		}
	}
	return NULL;
}

/****************************************************************
 * Function: srv_error  
 * Date: 01/18/91
 *
 * Description:
 *   Dispatch an error to the stderr file pointer. The function
 *   returns the error flag spcified when calling srv_error.
 * 
 * Linkage: int srv_error(sc,module,type,msg)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   char *module   - name of the function
 *   int type       - error flag or type of error
 *   char *msg      - error message
 * 
 * Returns: the error level assocated with the error flag:
 *    0 - no error
 *   -1 - warning
 *   -2 - error
 *   -3 - fatal error
 *
 * Revisions:
 * 2/13/91 (BDM) - now returns 0,-1,-2,-3 for each error level
 ****************************************************************/
PRIVATE
int
srv_error(sc,module,type,msg)
	SrvContext *sc;
	char *module;
	int type;
	char *msg;
{
	int trace;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		fprintf(stderr,"Error(srv_error): srvlib context is NULL\n");
		return -1;
	}

	trace = sc->trace;
	if ((type == SRV_OK) && (trace > 0))
	{
		return 0;
	}
	else
	{
		int level = find_error_level(type);
		char *name = find_error_name(type);
		FILE *logfile = sc->logfile;
		
		switch (level)
		{
			case SRV_OK:
				if (trace == 0)
				{
					fprintf(logfile,"Ok(%s): %s : %s\n",module,name,msg);
					return 0;
				}
				break;
			case SRV_WARNING:
				if (trace <= 1)
				{
					fprintf(logfile,"Warning(%s): %s : %s\n",module,name,msg);
					return -1;
				}
				break;
			case SRV_ERROR:
				if (trace <= 2)
				{
					fprintf(logfile,"Error(%s): %s : %s\n",module,name,msg);
					return -2;
				}
				break;
			case SRV_FATAL:
				if (trace <= 3)
				{
					fprintf(logfile,"Fatal(%s): %s : %s\n",module,name,msg);
					return -3;
				}
				break;
			default:
				fprintf(logfile,"Error(srv_error): ");
				fprintf(logfile,"Type %d not found in lookup table\n");
				return -1;
				break;
		}
	}
}

/****************************************************************
 * Function: add_event
 * Date: 01/17/91
 *
 * Description:
 *   This function reads information related to a specific event
 *   from the socket, fills in the SrvEvent structure appropriately,
 *   and adds this event to the event queue.
 * 
 * Linkage: int add_event(sc,type)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int type       - type of event
 *
 * Returns: srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PRIVATE
int
add_event(sc,type)
	SrvContext *sc;
	int type;
{
	SrvEvent event;
	long len;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"add_event",SRV_CONTEXT," ");
	}

	/* read in length of data coming after */
	if (read_f(sc->sock,"l",&len) < 0)
	{
		return srv_error(sc,"add_event",SRV_READ,"reading length");
	}

	/* Find out which type and fill event record accordingly */
	switch(type)
	{
		case EVT_DELETE_NOTEBOOK:
		{
			long nid;
			if (read_f(sc->sock,"l",&nid) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_DELETE_NOTE");
			}
			event.delete_notebook.type = SRV_DELETE_NOTEBOOK;
			event.delete_notebook.nid = nid;
			break;
		}
		case EVT_ACCESS_ALLOWED:
		{
			long nid;
			if (read_f(sc->sock,"l",&nid) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_ACCESS_ALLOWED");
			}
			event.access_allowed.type = SRV_ACCESS_ALLOWED;
			event.access_allowed.nid = nid;
			break;
		}
		case EVT_ACCESS_DISALLOWED:
		{
			long nid;
			if (read_f(sc->sock,"l",&nid) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_ACCESS_DISALLOWED");
			}
			event.access_disallowed.type = SRV_ACCESS_DISALLOWED;
			event.access_disallowed.nid = nid;
			break;
		}
		case EVT_MODIFY_NOTEBOOK:
		{
			long nid;
			if (read_f(sc->sock,"l",&nid) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_MODIFY_NOTEBOOK");
			}
			event.modify_notebook.type = SRV_MODIFY_NOTEBOOK;
			event.modify_notebook.nid = nid;
			break;
		}
		case EVT_CREATE_PAGE:
		{
			long nid,pid;
			if (read_f(sc->sock,"ll",&nid,&pid) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_CREATE_PAGE");
			}
			event.create_page.type = SRV_CREATE_PAGE;
			event.create_page.nid = nid;
			event.create_page.pid = pid;
			break;
		}
		case EVT_MODIFY_PAGE:
		{
			long nid,pid;
			if (read_f(sc->sock,"ll",&nid,&pid) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_MODIFY_PAGE");
			}
			event.modify_page.type = SRV_MODIFY_PAGE;
			event.modify_page.nid = nid;
			event.modify_page.pid = pid;
			break;
		}
		case EVT_DELETE_PAGE:
		{
			long nid,pid,del_links;
			if (read_f(sc->sock,"lll",&nid,&pid,&del_links) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_DELETE_PAGE");
			}
			event.delete_page.type = SRV_DELETE_PAGE;
			event.delete_page.nid = nid;
			event.delete_page.pid = pid;
			event.delete_page.del_links = del_links;
			break;
		}
		case EVT_DELETE_OBJECT:
		{
			long nid,pid,oid;
			if (read_f(sc->sock,"lll",&nid,&pid,&oid) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_DELETE_OBJECT");
			}
			event.delete_object.type = SRV_DELETE_OBJECT;
			event.delete_object.nid = nid;
			event.delete_object.pid = pid;
			event.delete_object.oid = oid;
			break;
		}
		case EVT_CREATE_OBJECT:
		{
			long nid,pid,oid;
			if (read_f(sc->sock,"lll",&nid,&pid,&oid) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_CREATE_OBJECT");
			}
			event.create_object.type = SRV_CREATE_OBJECT;
			event.create_object.nid = nid;
			event.create_object.pid = pid;
			event.create_object.oid = oid;
			break;
		}
		case EVT_MODIFY_OBJECT:
		{
			long nid,pid,oid;
			if (read_f(sc->sock,"lll",&nid,&pid,&oid) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_MODIFY_OBJECT");
			}
			event.modify_object.type = SRV_MODIFY_OBJECT;
			event.modify_object.nid = nid;
			event.modify_object.pid = pid;
			event.modify_object.oid = oid;
			break;
		}
		case EVT_OBJECT_CONTENTS_CHANGED:
		{
			long nid,pid,oid;
			if (read_f(sc->sock,"lll",&nid,&pid,&oid) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_OBJECT_CONTENTS_CHANGED");
			}
			event.modify_object.type = SRV_OBJECT_CONTENTS_CHANGED;
			event.modify_object.nid = nid;
			event.modify_object.pid = pid;
			event.modify_object.oid = oid;
			break;
		}
		case EVT_AUTHOR_LOGIN:
		{
			long aid;
			if (read_f(sc->sock,"l",&aid) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_AUTHOR_LOGIN");
			}
			event.author_login.type = SRV_AUTHOR_LOGIN;
			event.author_login.aid = aid;
			break;
		}
		case EVT_AUTHOR_LOGOUT:
		{
			long aid;
			if (read_f(sc->sock,"l",&aid) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_AUTHOR_LOGOUT");
			}
			event.author_logout.type = SRV_AUTHOR_LOGOUT;
			event.author_logout.aid = aid;
			break;
		}
		case EVT_SHUTDOWN:
		{
			long flag;
			if (read_f(sc->sock,"l",&flag) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"EVT_SHUTDOWN");
			}
			event.shutdown.type = SRV_SHUTDOWN;
			event.shutdown.flag = flag;
			break;
		}
		default:
			if (read_fluff(sc->sock,(int)len) < 0)
			{
				return srv_error(sc,"add_event",SRV_READ,"reading fluff");
			}
			return 1;
			break;
	}

	/* Now add it to event q */
	{
		SrvEventNode new = (SrvEventNode)malloc(sizeof *new);
		if (new == NULL)
		{
			srv_error(sc,"add_event",SRV_MALLOC,"adding event to queue");
		}
		new->event = event;
		new->next = NULL;
		if (sc->first == NULL)
		{
			sc->first = new;
		}
		else
		{
			sc->last->next = new;
		}
		sc->last = new;
	}
	return 0;
}

/****************************************************************
 * Function: wait_for_reply
 * Date: 01/03/91
 *
 * Description:
 *   Waits for the server to reply back to a command.
 * 
 * Linkage: int wait_for_reply(sc)
 *   SrvContext *sc - connection record (returned from srv_init)
 *
 * Revisions:
 ****************************************************************/
PRIVATE
int
wait_for_reply(sc)
	SrvContext *sc;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"wait_for_reply",SRV_CONTEXT," ");
	}

	for(;;)
	{
		long type;

		/* Read in Event type */
		if (read_f(sc->sock,"l",&type) < 0)
		{
			return -1;
		}

		/* If its is indeed data then return */
		if (type == EVT_DATA)
		{
			return 0;
		}

		if (type == EVT_ERR)
		{
			return -2;
		}

		/* Otherwise just add it to the q and wait for another */
		if (add_event(sc,(int)type) < 0)
		{
			return -1;
		}
	}
}

/****************************************************************
 *                     PUBLIC FUNCTIONS
 ****************************************************************/

/****************************************************************
 * ==> Library initialization and termination functions 
 ****************************************************************/

/****************************************************************
 * Function: srv_init
 * Date: 01/03/91
 *
 * Description:
 *   Initialize the srvlib and establish connection with dbsrv.
 *   This function all performs protocol version checking with
 *   dbsrv and will abort if there is a protocol mismatch error.
 *   This function returns the server id flag which is a required
 *   argument for all other srvlib functions.
 *
 * Notes:
 *   (1) This function does not call srv_error since this function
 *   is establishing the connection which srv_error depends. Instead,
 *   stderr is used to send all error messages that occurr during
 *   the execution of this function. If any error does occur, a
 *   message is printed to standard error output and -1 is returned.
 *   The application programmer should always check this function for
 *   -1 error return.
 * 
 * Linkage: SrvContext *srv_init(hostname,port,db)
 *   char *hostname - name of the host where dbsrv is running
 *   int port       - the port number on which to connect.
 *   char *db       - the name of the database.
 *
 * Returns:
 *  >=0 - the connection id (required by all other srvlib functions)
 *   -1 - if an eror occurred (error messages are printed to stderr)
 *
 * Notes:
 *   The SrvContect pointer that is returned points to an allocated
 *   data structure describing the connection. This pointer should
 *   not be freed by the user but instead should be passed to 
 *   functions srv_close (see below) to be freed properly.
 *
 * Revisions:
 * 01/17/91 (BDM): hostname, port, and db are now stored into the
 *   connection record when this function is called.
 ****************************************************************/
PUBLIC
SrvContext *
srv_init(hostname,port,db)
	char *hostname;
	int port;
	char *db;
{
	struct hostent *gethostbyname();
	extern char *getenv();
	SrvContext *sc;
	struct hostent *hp;
	int sock;
	char *trace;

	/* check if a database name was specified */
	if (db == NULL)
	{
		fprintf(stderr,"Error(srv_init): Database name is NULL\n");
		return NULL;
	}

	/* allocate connection record */
	sc = (SrvContext *)malloc(sizeof(SrvContext));
	if (sc == NULL)
	{
		fprintf(stderr,"Error(srv_init): Unable to allocate connection record\n");
		return NULL;
	}

	/* set the appropriate tracing level for the library */
	if (trace = getenv("SRVTRACE"))
	{
		int level = atoi(trace);
		if (level < 0 || level > 3)
		{
			sc->trace = 3;
		}
		else
		{
			sc->trace = level;
		}
	}
	else
	{
		/* the default is level 3 fatal errors */
		sc->trace = 3;
	}

	/* Get the host name (a NULL hostname is a valid parameter) */
	hp = gethostbyname(hostname == NULL ? "localhost" : hostname);
	if (hp == 0)
	{
		fprintf(stderr,"Error(srv_init): gethostbyname failed\n");
		return NULL;
	}

	/* Create the socket */
	sock = socket(AF_INET,SOCK_STREAM,0);
	if (sock < 0)
	{
		fprintf(stderr,"Error(srv_init): socket failed (sock = %d)\n",sock);
		return NULL;
	}

	/* Connect to the specified address using user supplied port number */
	{
		struct sockaddr_in server;
		server.sin_family = AF_INET;
		server.sin_port = htons(port);
		bcopy((char *)hp->h_addr,(char *)&server.sin_addr,
			hp->h_length);
		if (connect(sock,(struct sockaddr *)&server,sizeof server) < 0)
		{
			close(sock);
			fprintf(stderr,"Error(srv_init): connect failed (socket closed).\n");
			return NULL;
		}
	}

	/* fill in the connection record */
	sc->sock = sock;
	sc->first = sc->last = NULL;
	sc->event_table = NULL;
	sc->me = geteuid();
	sc->port = port;
	sc->hostname = strdup(hostname);
	sc->db = strdup(db);
	sc->logfile = stderr;

	/*
	 * protocol version checking: 
	 * send the major and minor protocol version numbers to dbsrv for version checking
	 */
	if (write_f(sock,"ll",srv_proto_major,srv_proto_minor) < 0)
	{
		fprintf(stderr,"Error(srv_init): writing version information to dbsrv\n");
		srv_close(sc);
		return NULL;
	}

	/* process protocol checking info returned from dbsrv */
	{
		long err;
		long db_major;
		long db_minor;
		if (read_f(sock,"lll",&err,&db_major,&db_minor) < 0)
		{
			fprintf(stderr,"Error(srv_init): reading dbsrv version information\n");
			srv_close(sc);
			return NULL;
		}

		if (err < 0)
		{
			fprintf(stderr,"Error(srv_init): Protocol mismatch between dbsrv and srvlib\n");
			fprintf(stderr,"dbsrv : version(%d.%d)\n",db_major,db_minor);
			fprintf(stderr,"srvlib: version(%d.%d)\n",srv_proto_major,srv_proto_minor);
			srv_close(sc);
			return NULL;
		}
	}

	/*
	 * Send Authorization information to dbsrv for connection confirmation.
	 */
	if (write_f(sock,"ls",sc->me,sc->db) < 0)
	{
		fprintf(stderr,"Error(srv_init): writing user id and database name\n");
		srv_close(sc);
		return NULL;
	}

	/* check if user is authorized or not */
	{
		long auth;
		/* read in confirmation flag */
		if (read_f(sock,"l",&auth) < 0)
		{
			fprintf(stderr,"Error(srv_init): reading dbsrv authorization info\n");
			srv_close(sc);
			return NULL;
		}
		/* invalied database */
		if (auth == -1)
		{
			fprintf(stderr,"Error(srv_init): connection denied:\n");
			fprintf(stderr,"Invalid database name for specified server: %s\n",sc->db);
			srv_close(sc);
			return NULL;
		}
		/* invalid author id */
		if (auth == -2)
		{
			fprintf(stderr,"Error(srv_init): connection denied:\n");
			fprintf(stderr,"Invalid uid for specified database: %d\n",sc->me);
			srv_close(sc);
			return NULL;
		}

		/* set the authorization information */
		sc->authorized = (int)auth;
	}

	return sc;
}

/****************************************************************
 * Function: srv_close
 * Date: 01/03/91
 *
 * Description:
 *   Closes the connection with dbsrv.
 *
 * Notes:
 *   This function frees the pointer to the connection structure.
 * 
 * Linkage: int srv_close(sc)
 *   SrvContext *sc - connection record (returned from srv_init)
 * 
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_close(sc)
	SrvContext *sc;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_close",SRV_CONTEXT," ");
	}

	/* free event queue */
	while(sc->first != NULL)
	{
		SrvEventNode first = sc->first;
		sc->first = first->next;
		free((char *)first);
	}
	/* free hostname and db name */
	if (sc->hostname != NULL)
	{
		free(sc->hostname);
		sc->hostname = NULL;
	}
	if (sc->db != NULL)
	{
		free(sc->db);
		sc->db = NULL;
	}
	/* free event handler table */
	while(sc->event_table != NULL)
	{
		SrvEventTable table = sc->event_table;
		sc->event_table = table->next;
		free((char *)table);
	}
	/* close the socket connection */
	close(sc->sock);

	/* free the connection record */
	free((char *)sc);

	return 0;
}

/****************************************************************
 * ==> Utilities for getting and setting basic connection info 
 ****************************************************************/

/****************************************************************
 * Function: srv_port
 * Date: 01/17/91
 *
 * Description:
 *   Obtain the current port used for connecting to dbsrv.
 * 
 * Linkage: int srv_port(sc)
 *   SrvContext *sc - connection record (returned from srv_init)
 * 
 * Returns:
 *   >=0 - port number
 *   < 0 - standard srvlib error code
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_port(sc)
	SrvContext *sc;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_port",SRV_CONTEXT," ");
	}
	return sc->port;
}

/****************************************************************
 * Function: srv_host
 * Date: 01/17/91
 *
 * Description:
 *   Obtain the hostname where dbsrv is located.
 * 
 * Linkage: char *srv_host(sc)
 *   SrvContext *sc - connection record (returned from srv_init)
 *
 * Returns: the host name or NULL if error
 *
 * Revisions:
 ****************************************************************/
PUBLIC
char *
srv_host(sc)
	SrvContext *sc;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		srv_error(sc,"srv_host",SRV_CONTEXT," ");
		return NULL;
	}
	return sc->hostname;
}

/****************************************************************
 * Function: srv_database
 * Date: 01/17/91
 *
 * Description:
 *   Obtain the database name that we are communicating with.
 * 
 * Linkage: char *srv_database(sc)
 *   SrvContext *sc - connection record (returned from srv_init)
 *
 * Returns: the database name or NULL if error
 *
 * Revisions:
 ****************************************************************/
PUBLIC
char *
srv_database(sc)
	SrvContext *sc;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		srv_error(sc,"srv_database",SRV_CONTEXT," ");
		return NULL;
	}
	return sc->db;
}

/****************************************************************
 * Function: srv_socket
 * Date: 01/03/91
 *
 * Description:
 *   This function returns the actual socket that is used for
 *   communication with dbsrv.
 * 
 * Linkage: int srv_socket(sc)
 *   SrvContext *sc - connection record (returned from srv_init)
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_socket(sc)
	SrvContext *sc;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_socket",SRV_CONTEXT," ");
	}
	return sc->sock;
}

/****************************************************************
 * Function: srv_uid
 * Date: 01/03/91
 *
 * Description:
 *   Get the user id for the invoker.
 * 
 * Linkage: int srv_uid(sc)
 *   SrvContext *sc - connection record (returned from srv_init)
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_uid(sc)
	SrvContext *sc;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_uid",SRV_CONTEXT," ");
	}
	return sc->me;
}

/****************************************************************
 * Function: srv_trace  
 * Date: 01/22/91
 *
 * Description:
 *   Set the level of tracing performed by srvlib.
 * 
 * Linkage: int srv_trace(sc,level)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int level      - level of tracing for srvlib:
 *                    = 0 : trace everything
 *                    = 1 : warnings, errors, and fatal errors only
 *                    = 2 : error and fatal errors
 *                    = 3 : fatal errors only
 *
 * Returns: serlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_trace(sc,level)
	SrvContext *sc;
	int level;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_trace",SRV_CONTEXT," ");
	}

	/* set trace level */
	sc->trace = level;

	return srv_error(sc,"srv_trace",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_set_logfile
 * Date: 01/22/91
 *
 * Description:
 *   Set the file pointer where log messages are sent.
 * 
 * Linkage: int srv_set_logfile(sc,fp)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   FILE *fp       - file pointer for logfile redirection.
 *
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_set_logfile(sc,fp)
	SrvContext *sc;
	FILE *fp;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_set_logfile",SRV_CONTEXT," ");
	}

	if (fp != NULL)
	{
		sc->logfile = fp;
	}
	return srv_error(sc,"srv_set_logfile",SRV_OK,"return");
}

/****************************************************************
 * ==> Notebook functions
 ****************************************************************/

/****************************************************************
 * Function: srv_get_allowed_notebooks  
 * Date: 01/03/91
 *
 * Description:
 *   Gets a list of notebook ids that are allowed to be used 
 *   by the invoker.
 * 
 * Linkage: int srv_get_allowed_notebooks(sc,l,n)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   long **l       - list of notebook ids (returned)
 *   int *n         - number of notebooks in list (returned)
 *
 * Notes:
 *   This function allocates space for the array of notebooks ids
 *   that are returned. The front-end application is responsible
 *   for freeing this memory. 
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_get_allowed_notebooks(sc,l,n)
	SrvContext *sc;
	long **l;
	int *n;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_get_allowed_notebooks",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"l",REQ_ALLOWED_NOTEBOOK_USE_LIST) < 0)
	{
		return srv_error(sc,"srv_get_allowed_notebooks",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_get_allowed_notebooks",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"a",l,n) < 0)
	{
		return srv_error(sc,"srv_get_allowed_notebooks",SRV_READ,"list of notebooi ids");
	}
	return srv_error(sc,"srv_get_allowed_notebooks",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_find_note_by_name
 * Date: 01/03/91
 *
 * Description:
 *   Find a notebook by the name of the notebook (title).
 *   The function returns the notebook id is the notebook exists
 *   or -1 if an error occurred.
 * 
 * Linkage: int srv_find_note_by_name(sc,title)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   char *title    - title of the notebook
 *
 * Returns:
 *   > 0 - the notebook id
 *   < 0 - notebook not found
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_find_note_by_name(sc,title)
	SrvContext *sc;
	char *title;
{
	long nid;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_find_note_by_name",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"ls",REQ_FIND_NOTEBOOK_BY_NAME,title) < 0)
	{
		return srv_error(sc,"srv_find_note_by_name",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_find_note_by_name",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"l",&nid) < 0)
	{
		return srv_error(sc,"srv_find_note_by_name",SRV_READ," ");
	}
	srv_error(sc,"srv_find_note_by_name",SRV_OK,"return");
	return nid;
}

/****************************************************************
 * Function: srv_inq_notebook_orphans  
 * Date: 01/03/91
 *
 * Description:
 *   Returns a list of orphan pages contained in a specified
 *   notebook.
 * 
 * Linkage: int srv_inq_notebook_orphans(sc,nid,l,n)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id 
 *   long **l       - list of orphaned page ids (returned)
 *   int *n         - number of pages in list (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function allocates space for the array of page ids
 *   that is returned. The front-end application is responsible
 *   for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_inq_notebook_orphans(sc,nid,l,n)
	SrvContext *sc;
	long **l;
	int *n;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_inq_notebook_orphans",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"ll",REQ_INQ_NOTEBOOK_ORPHANS,nid) < 0)
	{
		return srv_error(sc,"srv_inq_notebook_orphans",SRV_WRITE,"notebook id");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_inq_notebook_orphans",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"a",l,n) < 0)
	{
		return srv_error(sc,"srv_inq_notebook_orphans",SRV_READ,"list of page ids");
	}
	return srv_error(sc,"srv_inq_notebook_orphans",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_inq_notebook_pages  
 * Date: 01/03/91
 *
 * Description:
 *   Returns a list of page ids in a specified notebook.
 * 
 * Linkage: int srv_inq_notebook_pages(sc,nid,l,n)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id 
 *   long **l       - list of page ids (returned)
 *   int *n         - number of pages in list (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function allocates space for the array of page ids
 *   that is returned. The front-end application is responsible
 *   for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_inq_notebook_pages(sc,nid,l,n)
	SrvContext *sc;
	long **l;
	int *n;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_inq_notebook_pages",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"ll",REQ_INQ_NOTEBOOK_PAGES,nid) < 0)
	{
		return srv_error(sc,"srv_inq_notebook_pages",SRV_WRITE,"notebook id");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_inq_notebook_pages",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"a",l,n) < 0)
	{
		return srv_error(sc,"srv_inq_notebook_pages",SRV_READ,"notebook pages");
	}
	return srv_error(sc,"srv_inq_notebook_pages",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_create_notebook
 * Date: 01/03/91
 *
 * Description:
 *   Creates a notebook in the database based on information 
 *   contained in the notebook info structure. This function
 *   returns the notebook id for the new notebook.
 * 
 * Linkage: int srv_create_notebook(sc,info)
 *   SrvContext *sc      - connection record (returned from srv_init)
 *   Notebook_Info *info - Notebook information sturcture
 *
 * Return: returns the notebook id
 *   > 0 - notebook id is returned
 *    -1 - otherwise
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_create_notebook(sc,info)
	SrvContext *sc;
	Notebook_Info *info;
{
	long reply;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_create_notebook",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"lsl",REQ_CREATE_NOTEBOOK,info->title,info->home_page) < 0)
	{
		return srv_error(sc,"srv_create_notebook",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_create_notebook",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"l",&reply) < 0)
	{
		return srv_error(sc,"srv_create_notebook",SRV_READ," ");
	}
	srv_error(sc,"srv_create_notebook",SRV_OK,"return");
	return reply;
}

/****************************************************************
 * Function: srv_modify_notebook
 * Date: 01/03/91
 *
 * Description:
 *   Modify the information about a notebook given the notebook
 *   id and the new notebook info.
 * 
 * Linkage: int srv_modify_notebook(sc,nid,info)
 *   SrvContext *sc      - connection record (returned from srv_init)
 *   int nid             - notebook id
 *   Notebook_info *info - new notebook information
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_modify_notebook(sc,nid,info)
	SrvContext *sc;
	int nid;
	Notebook_Info *info;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_modify_notebook",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"llsl",REQ_MODIFY_NOTEBOOK,nid,info->title,info->home_page) < 0)
	{
		return srv_error(sc,"srv_modify_notebook",SRV_WRITE," ");
	}
	return srv_error(sc,"srv_modify_notebook",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_inq_notebook
 * Date: 01/03/91
 *
 * Description:
 *   Obtain information about a spcific notebook.
 * 
 * Linkage: int srv_inq_notebook(sc,nid,info)
 *   SrvContext *sc      - connection record (returned from srv_init)
 *   int nid             - notebook id
 *   Notebook_info *info - notebook information (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_inq_notebook(sc,nid,info)
	SrvContext *sc;
	int nid;
	Notebook_Info *info;
{
	long access;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_inq_notebook",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"ll",REQ_INQ_NOTEBOOK,nid) < 0)
	{
		return srv_error(sc,"srv_inq_notebook",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_inq_notebook",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"slll",info->title,sizeof info->title,&access,&info->aid,&info->home_page) < 0)
	{
		return srv_error(sc,"srv_inq_notebook",SRV_READ," ");
	}
	info->can_read = (access >= 1);
	info->can_write = (access >= 2);
	return srv_error(sc,"srv_inq_notebook",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_get_blurted_note
 * Date: 01/03/91
 *
 * Description:
 * 
 * Linkage: int srv_get_blurted_note(sc,notes,nnotes)
 *   SrvContext *sc         - connection record (returned from srv_init)
 *   Notebook_Block **notes - list of notebook blocks (returned)
 *   int *nnotes            - number of notebook blocks (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function allocates space for the array of notebook blocks
 *   that is returned. The front-end application is responsible
 *   for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_get_blurted_note(sc,notes,nnotes)
	SrvContext *sc;
	Notebook_Block **notes;
	int *nnotes;
{
	long nn;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_get_blurted_note",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"l",REQ_BLURT_NOTE) < 0)
	{
		return srv_error(sc,"srv_get_blurted_note",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_get_blurted_note",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"l",&nn) < 0)
	{
		return srv_error(sc,"srv_get_blurted_note",SRV_READ,"number of notebooks");
	}
	*notes = (Notebook_Block *)malloc(nn * sizeof(Notebook_Block));
	{
		int i;
		for (i = 0; i < nn; i++)
		{
			long access;
			Notebook_Info *info = &((*notes)[i].info);

			/* Read in notebook */
			if (read_f(sc->sock,"lslll",&(*notes)[i].nid,info->title,
				sizeof info->title,&access,&info->aid,&info->home_page) < 0)
			{
				return srv_error(sc,"srv_get_blurted_note",SRV_READ,"notebook info");
			}
			info->can_read = (access >= 1);
			info->can_write = (access >= 2);
		}
	}
	*nnotes = nn;
	return srv_error(sc,"srv_get_blurted_note",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_delete_notebook
 * Date: 01/03/91
 *
 * Description:
 *   Delete a notebook in the database given it's id number.
 * 
 * Linkage: int srv_delete_notebook(sc,nid)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_delete_notebook(sc,nid)
	SrvContext *sc;
	int nid;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_delete_notebook",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"ll",REQ_DELETE_NOTEBOOK,nid) < 0)
	{
		return srv_error(sc,"srv_delete_notebook",SRV_WRITE,"notebook id");
	}
	return srv_error(sc,"srv_delete_notebook",SRV_OK,"return");
}

/****************************************************************
 * ==> Page functions
 ****************************************************************/

/****************************************************************
 * Function: srv_create_page
 * Date: 01/03/91
 *
 * Description:
 *   Create a page in a spcified notebook in the database based
 *   on the the page information passed. This function returns
 *   the page id for the new page.
 * 
 * Linkage: int srv_create_page(sc,nid,info)
 *   SrvContext *sc  - connection record (returned from srv_init)
 *   int nid         - notebook id
 *   Page_Info *info - page information 
 *
 * Returns:
 *   > 0 - page id
 *   < 0 - standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_create_page(sc,nid,info)
	SrvContext *sc;
	int nid;
	Page_Info *info;
{
	long reply;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_create_page",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"llslls",REQ_CREATE_PAGE,nid,info->title,info->width,info->height,info->color) < 0)
	{
		return srv_error(sc,"srv_create_page",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_create_page",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"l",&reply) < 0)
	{
		return srv_error(sc,"srv_create_page",SRV_READ," ");
	}
	srv_error(sc,"srv_create_page",SRV_OK,"return");
	return reply;
}

/****************************************************************
 * Function: srv_validate_page
 * Date: 01/03/91
 *
 * Description:
 *   Obtain a list of object ids for a specific page in a 
 *   specific notebook.
 * 
 * Linkage: int srv_validate_page(sc,nid,pid)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id
 *   int pid        - page id
 *
 * Returns:
 *   1 - If the page is valid
 *   2 - If the page does no exist
 *   3 - If the page exists but the invoker does not have access.
 *
 * Revisions:
 *   2/13/91 (BDM) - changed return codes for consistency with
 *     the standard srvlid error return.
 ****************************************************************/
PUBLIC
int
srv_validate_page(sc,nid,pid)
	SrvContext *sc;
	int nid;
	int pid;
{
	long reply;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_validate_page",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"lll",REQ_VALIDATE_PAGE,nid,pid) < 0)
	{
		return srv_error(sc,"srv_validate_page",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_validate_page",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"l",&reply) < 0)
	{
		return srv_error(sc,"srv_validate_page",SRV_READ," ");
	}
	srv_error(sc,"srv_validate_page",SRV_OK,"return");
	return reply;
}

/****************************************************************
 * Function: srv_modify_page
 * Date: 01/03/91
 *
 * Description:
 *   Modify the basic page information given the page id, notebook
 *   id, and page information.
 * 
 * Linkage: int srv_modify_page(sc,nid,pid,info)
 *   SrvContext *sc  - connection record (returned from srv_init)
 *   int nid         - notebook id
 *   int pid         - page id
 *   Page_Info *info - page info
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_modify_page(sc,nid,pid,info)
	SrvContext *sc;
	int nid;
	int pid;
	Page_Info *info;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_modify_page",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"lllslls",REQ_MODIFY_PAGE,nid,pid,info->title,info->width,info->height,info->color) < 0)
	{
		return srv_error(sc,"srv_modify_page",SRV_WRITE,"page info");
	}
	return srv_error(sc,"srv_modify_page",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_find_page_by_name
 * Date: 01/03/91
 *
 * Description:
 *   Find a page by name in a given notebook.
 * 
 * Linkage: int srv_find_page_by_name(sc,nid,title)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id
 *   char *title    - page title
 *
 * Returns:
 *   > 0 - the page id is returned
 *   < 0 - page not found
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_find_page_by_name(sc,nid,title)
	SrvContext *sc;
	int nid;
	char *title;
{
	long pid;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_find_page_by_name",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"lls",REQ_FIND_PAGE_BY_NAME,nid,title) < 0)
	{
		return srv_error(sc,"srv_find_page_by_name",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_find_page_by_name",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"l",&pid) < 0)
	{
		return srv_error(sc,"srv_find_page_by_name",SRV_READ," ");
	}
	srv_error(sc,"srv_find_page_by_name",SRV_OK,"return");
	return pid;
}

/****************************************************************
 * Function: srv_inq_page_links  
 * Date: 01/03/91
 *
 * Description:
 *   Returns a list of links on page. The links are returned as
 *   a list of destination notebook ids and page ids.
 * 
 * Linkage: int srv_inq_page_links(sc,nid,pid,ln,lp,n)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id 
 *   int pid        - page id 
 *   long **ln      - list of destination notebook ids (returned)
 *   long **lp      - list of destination page ids (returned)
 *   int *n         - number of id in list (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function allocates space for the array of notebook ids
 *   and page ids that is returned. The front-end application is
 *   responsible for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_inq_page_links(sc,nid,pid,ln,lp,n)
	SrvContext *sc;
	long **ln;
	long **lp;
	int *n;
{
	int ntmp;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_inq_page_links",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"lll",REQ_INQ_PAGE_LINKS,nid,pid) < 0)
	{
		return srv_error(sc,"srv_inq_page_links",SRV_WRITE,"note id and page id");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_inq_page_links",SRV_WAIT," ");
	}
	/* read in notebook ids */
	if (read_f(sc->sock,"aa",ln,n,lp,&ntmp) < 0)
	{
		return srv_error(sc,"srv_inq_page_links",SRV_READ,"return lists");
	}
	return srv_error(sc,"srv_inq_page_links",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_inq_page_objects
 * Date: 01/03/91
 *
 * Description:
 *   Obtain a list of object ids for a specific page in a 
 *   specific notebook.
 * 
 * Linkage: int srv_inq_page_objects(sc,nid,pid,l,n)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid  - notebook id
 *   int pid  - page id
 *   long **l - list of object ids (returned)
 *   int *n   - number of object ids in list (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function allocates space for the array of object ids
 *   that is returned. The front-end application is responsible
 *   for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_inq_page_objects(sc,nid,pid,l,n)
	SrvContext *sc;
	int nid;
	int pid;
	long **l;
	int *n;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_inq_page_objects",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"lll",REQ_INQ_PAGE_OBJECTS,nid,pid) < 0)
	{
		return srv_error(sc,"srv_inq_page_objects",SRV_WRITE,"note id and page id");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_inq_page_objects",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"a",l,n) < 0)
	{
		return srv_error(sc,"srv_inq_page_objects",SRV_READ,"object id list");
	}
	return srv_error(sc,"srv_inq_page_objects",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_inq_page
 * Date: 01/03/91
 *
 * Description:
 *   Obtain information about a given page in a given notebook.
 * 
 * Linkage: int srv_inq_page(sc,nid,pid,info)
 *   SrvContext *sc  - connection record (returned from srv_init)
 *   int nid         - notebook id
 *   int pid         - page id
 *   Page_Info *info - page information (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_inq_page(sc,nid,pid,info)
	SrvContext *sc;
	int nid;
	int pid;
	Page_Info *info;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_inq_page",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"lll",REQ_INQ_PAGE,nid,pid) < 0)
	{
		return srv_error(sc,"srv_inq_page",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_inq_page",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"sllssl",info->title,sizeof info->title,&info->width,&info->height,info->color,sizeof info->color,info->create_date,sizeof info->create_date,&info->aid) < 0)
	{
		return srv_error(sc,"srv_inq_page",SRV_READ," ");
	}
	return srv_error(sc,"srv_inq_page",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_get_blurted_page
 * Date: 01/03/91
 *
 * Description:
 *   Obtain information about a spcific page and all of the 
 *   objects on that page in one shot. This function should be
 *   used when rendering a page to reduce the number of round
 *   trip transfers between srvlib and dbsrv.
 * 
 * Linkage: int srv_get_blurted_page(sc,nid,pid,page,object,nobjects)
 *   SrvContext *sc         - connection record (returned from srv_init)
 *   int nid                - notebook id
 *   int pid                - page id
 *   Page_Info *page        - page informatio (returned)
 *   Object_Block **objects - list or object blocks (returned)
 *   int *nobjects          - number of object blocks (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function allocates space for the array of object blocks 
 *   that is returned. The front-end application is responsible
 *   for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_get_blurted_page(sc,nid,pid,page,objects,nobjects)
	SrvContext *sc;
	int nid;
	int pid;
	Page_Info *page;
	Object_Block **objects;
	int *nobjects;
{
	long nobjs;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_get_blurted_page",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"lll",REQ_BLURT_PAGE,nid,pid) < 0)
	{
		return srv_error(sc,"srv_get_blurted_page",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_get_blurted_page",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"sllssll",page->title,sizeof page->title,&page->width,&page->height,page->color,sizeof page->color,page->create_date,sizeof page->create_date,&nobjs,&page->aid) < 0)
	{
		return srv_error(sc,"srv_get_blurted_page",SRV_READ,"page info");
	}
	*objects = (Object_Block *)malloc(nobjs * sizeof(Object_Block));
	{
		int i;
		for(i = 0; i < nobjs; i++)
		{
			long type;
			Object_Info *info = &((*objects)[i].info);

			/* Read in object info */
			if (read_f(sc->sock,"lllllll",&(*objects)[i].oid,&info->x,&info->y,&info->width,&info->height,&type,&info->aid) < 0)
			{
				return srv_error(sc,"srv_get_blurted_page",SRV_READ,"object info");
			}

			switch(type)
			{
				case IMAGE_OBJECT_TYPE:
				{
					info->type = OT_IMAGE;
					if (read_f(sc->sock,"llz",&info->data.i_info.create_cmap,
						&info->data.i_info.low_res_sw,&(*objects)[i].img) < 0)
					{
						return srv_error(sc,"srv_get_blurted_page",SRV_READ,"image object");
					}
					break;
				}
				case TEXT_OBJECT_TYPE:
				{
					long lstr;
					int bc_read;
					info->type = OT_TXT;
					if (read_f(sc->sock,"s",
						info->data.t_info.font,sizeof info->data.t_info.font) < 0)
					{
						return srv_error(sc,"srv_get_blurted_page",SRV_READ,"text: font");
					}
					if (read_f(sc->sock,"s",
						info->data.t_info.color,sizeof info->data.t_info.color) < 0)
					{
						return srv_error(sc,"srv_get_blurted_page",SRV_READ,"text: color");
					}
					if (read_f(sc->sock,"l",&lstr) < 0)
					{
						return srv_error(sc,"srv_get_blurted_page",SRV_READ,"text: lstr");
					}
					if (lstr < 0)
					{
						return srv_error(sc,"srv_get_blurted_page",SRV_READ,"text: lstr < 0");
					}
					(*objects)[i].txt = malloc(lstr + 1);
					bc_read = must_read(sc->sock,(*objects)[i].txt,lstr);
					if (bc_read != lstr)
					{
						return srv_error(sc,"srv_get_blurted_page",SRV_READ,"text: must_read");
					}
					(*objects)[i].txt[lstr] = 0;
					break;
				}
				case ACTION_LINK_OBJECT_TYPE:
					info->type = OT_ACTION_LINK;
					if (read_f(sc->sock,"ssss",
						info->data.al_info.command,sizeof info->data.al_info.command,
						info->data.al_info.title,sizeof info->data.al_info.title,
						info->data.al_info.font,sizeof info->data.al_info.font,
						info->data.al_info.color,sizeof info->data.al_info.color) < 0)
					{
						return srv_error(sc,"srv_get_blurted_page",SRV_READ,"action link");
					}
					break;
				case LINK_OBJECT_TYPE:
					info->type = OT_LINK;
					if (read_f(sc->sock,"llsss",
						&info->data.l_info.dest_note,
						&info->data.l_info.dest_page,
						info->data.l_info.title,sizeof info->data.l_info.title,
						info->data.l_info.font,sizeof info->data.l_info.font,
						info->data.l_info.color,sizeof info->data.l_info.color) < 0)
					{
						return srv_error(sc,"srv_get_blurted_page",SRV_READ,"link");
					}
					break;
			}
		}
	}
	*nobjects = nobjs;
	return srv_error(sc,"srv_get_blurted_page",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_delete_page
 * Date: 01/03/91
 *
 * Description:
 *   Delete a page in the database given the notebook id and
 *   the page id. The additional argument is used to tell dbsrv
 *   whether to delete all reference links to the page (1) or
 *   to leave the links dangling (0).
 * 
 * Linkage: int srv_delete_page(sc,nid,pid,del_links)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id
 *   int pid        - page id
 *   int del_links  - (0) leave links dangling (1) delete ref links
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_delete_page(sc,nid,pid,del_links)
	SrvContext *sc;
	int nid;
	int pid;
	int del_links;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_delete_page",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"llll",REQ_DELETE_PAGE,nid,pid,del_links) < 0)
	{
		return srv_error(sc,"srv_delete_page",SRV_WRITE," ");
	}
	return srv_error(sc,"srv_delete_page",SRV_OK,"return");
}

/****************************************************************
 * ==> Object functions
 ****************************************************************/

/****************************************************************
 * Function: srv_create_object
 * Date: 01/03/91
 *
 * Description:
 *   Create a new object in the database for a given page in a
 *   given notebook. This function returns the object id for
 *   the new object.
 * 
 * Linkage: int srv_create_object(sc,nid,pid,info)
 *   SrvContext *sc    - connection record (returned from srv_init)
 *   int nid           - notebook id
 *   int pid           - page id
 *   Object_Info *info - new object info
 * 
 * Returns:
 *   > 0 - object id
 *   < 0 - standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_create_object(sc,nid,pid,info)
	SrvContext *sc;
	int nid;
	int pid;
	Object_Info *info;
{
	long reply;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_create_object",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"lllllll",REQ_CREATE_OBJECT,nid,pid,info->x,info->y,info->width,info->height) < 0)
	{
		return srv_error(sc,"srv_create_object",SRV_WRITE,"object info");
	}
	switch(info->type)
	{
		case OT_IMAGE:
			if (write_f(sc->sock,"lll",IMAGE_OBJECT_TYPE,
					info->data.i_info.create_cmap,
					info->data.i_info.low_res_sw) < 0)
			{
				return srv_error(sc,"srv_create_object",SRV_WRITE,"iamge object");
			}
			break;
		case OT_TXT:
			if (write_f(sc->sock,"lss",TEXT_OBJECT_TYPE,
					info->data.t_info.font,
					info->data.t_info.color) < 0)
			{
				return srv_error(sc,"srv_create_object",SRV_WRITE,"text object");
			}
			break;
		case OT_ACTION_LINK:
			if (write_f(sc->sock,"lssss",ACTION_LINK_OBJECT_TYPE,
					info->data.al_info.command,
					info->data.al_info.title,
					info->data.al_info.font,
					info->data.al_info.color) < 0)
			{
				return srv_error(sc,"srv_create_object",SRV_WRITE,"action link object");
			}
			break;
		case OT_LINK:
			if (write_f(sc->sock,"lllsss",LINK_OBJECT_TYPE,
					info->data.l_info.dest_note,
					info->data.l_info.dest_page,
					info->data.l_info.title,
					info->data.l_info.font,
					info->data.l_info.color) < 0)
			{
				return srv_error(sc,"srv_create_object",SRV_WRITE,"link object");
			}
			break;
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_create_object",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"l",&reply) < 0)
	{
		return srv_error(sc,"srv_create_object",SRV_READ," ");
	}
	srv_error(sc,"srv_create_object",SRV_OK,"return");
	return reply;
}

/****************************************************************
 * Function: srv_modify_object
 * Date: 01/03/91
 *
 * Description:
 *   Obtain a list of object ids for a specific page in a 
 *   specific notebook.
 * 
 * Linkage: int srv_modify_object(sc,nid,pid,oid,info)
 *   SrvContext *sc    - connection record (returned from srv_init)
 *   int nid           - notebook id
 *   int pid           - page id
 *   int oid           - object id
 *   Object_Info *info - new object info
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_modify_object(sc,nid,pid,oid,info)
	SrvContext *sc;
	int nid;
	int pid;
	int oid;
	Object_Info *info;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_modify_object",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"llllllll",REQ_MODIFY_OBJECT,nid,pid,oid,info->x,info->y,info->width,info->height) < 0)
	{
		return srv_error(sc,"srv_modify_object",SRV_WRITE,"object info");
	}
	switch(info->type)
	{
		case OT_IMAGE:
			if (write_f(sc->sock,"lll",IMAGE_OBJECT_TYPE,
					info->data.i_info.create_cmap,
					info->data.i_info.low_res_sw) < 0)
			{
				return srv_error(sc,"srv_modify_object",SRV_WRITE,"image object");
			}
			break;
		case OT_TXT:
			if (write_f(sc->sock,"lss",TEXT_OBJECT_TYPE,
					info->data.t_info.font,
					info->data.t_info.color) < 0)
			{
				return srv_error(sc,"srv_modify_object",SRV_WRITE,"text object");
			}
			break;
		case OT_ACTION_LINK:
			if (write_f(sc->sock,"lssss",ACTION_LINK_OBJECT_TYPE,
					info->data.al_info.command,
					info->data.al_info.title,
					info->data.al_info.font,
					info->data.al_info.color) < 0)
			{
				return srv_error(sc,"srv_modify_object",SRV_WRITE,"action link object");
			}
			break;
		case OT_LINK:
			if (write_f(sc->sock,"lllsss",LINK_OBJECT_TYPE,
					info->data.l_info.dest_note,
					info->data.l_info.dest_page,
					info->data.l_info.title,
					info->data.l_info.font,
					info->data.l_info.color) < 0)
			{
				return srv_error(sc,"srv_modify_object",SRV_WRITE,"link object");
			}
			break;
	}
	return srv_error(sc,"srv_modify_object",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_inq_object
 * Date: 01/03/91
 *
 * Description:
 *   Obtain spcific information about a specific object on a 
 *   page in a notebook. The full path to the object is required
 *   which includes the notebook id, page id, and object id.
 * 
 * Linkage: int srv_inq_object(sc,nid,pid,oid,info)
 *   SrvContext *sc    - connection record (returned from srv_init)
 *   int nid           - notebook id
 *   int pid           - page id
 *   int oid           - object id
 *   Object_Info *info - object information (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_inq_object(sc,nid,pid,oid,info)
	SrvContext *sc;
	int nid;
	int pid;
	int oid;
	Object_Info *info;
{
	long type;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_inq_object",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"llll",REQ_INQ_OBJECT,nid,pid,oid) < 0)
	{
		return srv_error(sc,"srv_inq_object",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_inq_object",SRV_WAIT," ");
	}
	/* read in object info */
	if (read_f(sc->sock,"llllll",&info->x,&info->y,&info->width,&info->height,&type,&info->aid) < 0)
	{
		return srv_error(sc,"srv_inq_object",SRV_READ,"object info");
	}
	switch(type)
	{
		case IMAGE_OBJECT_TYPE:
			info->type = OT_IMAGE;
			if (read_f(sc->sock,"ll",&info->data.i_info.create_cmap,
					&info->data.i_info.low_res_sw) < 0)
			{
				return srv_error(sc,"srv_inq_object",SRV_READ,"image object");
			}
			break;
		case TEXT_OBJECT_TYPE:
			info->type = OT_TXT;
			if (read_f(sc->sock,"ss",
				info->data.t_info.font,sizeof info->data.t_info.font,
				info->data.t_info.color,sizeof info->data.t_info.color) < 0)
			{
				return srv_error(sc,"srv_inq_object",SRV_READ,"text object");
			}
			break;
		case ACTION_LINK_OBJECT_TYPE:
			info->type = OT_ACTION_LINK;
			if (read_f(sc->sock,"ssss",
				info->data.al_info.command,sizeof info->data.al_info.command,
				info->data.al_info.title,sizeof info->data.al_info.title,
				info->data.al_info.font,sizeof info->data.al_info.font,
				info->data.al_info.color,sizeof info->data.al_info.color) < 0)
			{
				return srv_error(sc,"srv_inq_object",SRV_READ,"action link object");
			}
			break;
		case LINK_OBJECT_TYPE:
			info->type = OT_LINK;
			if (read_f(sc->sock,"llsss",
				&info->data.l_info.dest_note,
				&info->data.l_info.dest_page,
				info->data.l_info.title,sizeof info->data.l_info.title,
				info->data.l_info.font,sizeof info->data.l_info.font,
				info->data.l_info.color,sizeof info->data.l_info.color) < 0)
			{
				return srv_error(sc,"srv_inq_object",SRV_READ,"link object");
			}
			break;
	}

	return srv_error(sc,"srv_inq_object",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_delete_object
 * Date: 01/03/91
 *
 * Description:
 *   Delete an object in the database given the notebook id,
 *   page id, and object id (full path to the object).
 * 
 * Linkage: int srv_delete_object(sc,nid,pid,oid)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id
 *   int pid        - page id
 *   int oid        - object id
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_delete_object(sc,nid,pid,oid)
	SrvContext *sc;
	int nid;
	int pid;
	int oid;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_delete_object",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"llll",REQ_DELETE_OBJECT,nid,pid,oid) < 0)
	{
		return srv_error(sc,"srv_delete_object",SRV_WRITE,"nid, pid, oid");
	}
	return srv_error(sc,"srv_delete_object",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_set_object_text
 * Date: 01/03/91
 *
 * Description:
 *   Set the object text for a specified text object.
 * 
 * Linkage: int srv_set_object_text(sc,nid,pid,oid,txt)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id
 *   int pid        - page id
 *   int oid        - text object id
 *   char *txt      - text string
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function copies the text passed to dbsrv. The front-end
 *   application may free this memory after calling this function
 *   without affecting the database.
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_set_object_text(sc,nid,pid,oid,txt)
	SrvContext *sc;
	int nid;
	int pid;
	int oid;
	char *txt;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_set_object_text",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"lllls",REQ_SET_TEXT,nid,pid,oid,txt) < 0)
	{
		return srv_error(sc,"srv_set_object_text",SRV_WRITE," ");
	}
	return srv_error(sc,"srv_set_object_text",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_get_object_text
 * Date: 01/03/91
 *
 * Description:
 *   Get a pointer to the text for a specific text object.
 *   The function returns a pointer to the text.
 * 
 * Linkage: char *srv_get_object_text(sc,nid,pid,oid)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id
 *   int pid        - page id
 *   int oid        - object id
 *
 * Returns: pointer to text for object or NULL otherwise.
 *
 * Notes:
 *   This function allocates space for the text pointer 
 *   that is returned. The front-end application is responsible
 *   for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
char *
srv_get_object_text(sc,nid,pid,oid)
	SrvContext *sc;
	int nid;
	int pid;
	int oid;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		srv_error(sc,"srv_get_object_text",SRV_CONTEXT," ");
		return NULL;
	}

	if (write_f(sc->sock,"llll",REQ_GET_TEXT,nid,pid,oid) < 0)
	{
		srv_error(sc,"srv_get_object_text",SRV_WRITE," ");
		return NULL;
	}
	if (wait_for_reply(sc) < 0)
	{
		srv_error(sc,"srv_get_object_text",SRV_WAIT," ");
		return NULL;
	}
	{
		long l;
		char *txt;
		if (read_f(sc->sock,"l",&l) < 0)
		{
			srv_error(sc,"srv_get_object_text",SRV_READ," ");
			return NULL;
		}
		txt = malloc(l + 1);
		if (must_read(sc->sock,txt,l) != l)
		{
			srv_error(sc,"srv_get_object_text",SRV_READ,"must_read");
			return NULL;
		}
		txt[l] = 0;
		srv_error(sc,"srv_get_object_text",SRV_OK,"return");
		return txt;
	}
}

/****************************************************************
 * Function: srv_get_object_img
 * Date: 01/03/91
 *
 * Description:
 *   Get the image data in raster image format for a spcific
 *   image object.
 * 
 * Linkage: int srv_get_object_img(sc,nid,pid,oid,img)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id
 *   int pid        - page id
 *   int oid        - object id
 *   Rast_Img *img  - raster image (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function allocates space for the raster image
 *   that is returned. The front-end application is responsible
 *   for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_get_object_img(sc,nid,pid,oid,img)
	SrvContext *sc;
	int nid;
	int pid;
	int oid;
	Rast_Img *img;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_get_object_img",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"llll",REQ_GET_IMAGE,nid,pid,oid) < 0)
	{
		return srv_error(sc,"srv_get_object_img",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_get_object_img",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"z",img) < 0)
	{
		return srv_error(sc,"srv_get_object_img",SRV_READ," ");
	}
	return srv_error(sc,"srv_get_object_img",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_set_object_img
 * Date: 01/03/91
 *
 * Description:
 *   Store a rater image for a spcific image object.
 * 
 * Linkage: int srv_set_object_img(sc,nid,pid,oid,img)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id
 *   int pid        - page id
 *   int oid        - object id
 *   Rast_Img *img  - raster image to be stored
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function copies the raster image to dbsrv. The front-end
 *   application may free this memory after calling this function
 *   without affecting the database.
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_set_object_img(sc,nid,pid,oid,img)
	SrvContext *sc;
	int nid;
	int pid;
	int oid;
	Rast_Img *img;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_set_object_img",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"llllz",REQ_SET_IMAGE,nid,pid,oid,img) < 0)
	{
		return srv_error(sc,"srv_set_object_img",SRV_WRITE," ");
	}
	return srv_error(sc,"srv_set_object_img",SRV_OK,"return");
}

/****************************************************************
 * ==> Event  functions
 ****************************************************************/

/****************************************************************
 * Function: srv_add_event_handler  
 * Date: 01/12/91
 *
 * Description:
 *   Registers an event handler with srvlib for a specific
 *   dynamic event. These event handlers will be called by
 *   using srv_dispatch_event which handles searching and calling
 *   the appropriate event handler for a given event.
 *
 * Callback Syntax:
 *
 *   void EventHandler(sc,event,client_data)
 *   SrvContext *sc    - connection record (returned from srv_init)
 *   SrvEvnet *event   - event information for spcific event
 *   char *client_data - client data specific when adding handler
 * 
 * Linkage: int srv_add_event_handler(sc,type,proc,data)
 *   SrvContext *sc    - connection record (returned from srv_init)
 *   SrvEventType type - the type of event the handler handles
 *   void (*proc)()    - pointer to the event handler function
 *   char *data;       - client data passed to the event handler
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_add_event_handler(sc,type,proc,data)
	SrvContext *sc;
	SrvEventType type;
	void (*proc)();
	char *data;
{
	SrvEventRec *table, *prev;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_add_event_handler",SRV_CONTEXT," ");
	}

	table = sc->event_table;
	prev = NULL;

	while((table != NULL) && (table->proc != proc || table->data != data))
	{
		prev = table;
		table = table->next;
	}

	if (table == NULL)
	{
		table = (SrvEventTable)malloc(sizeof(SrvEventRec));
		if (table == NULL)
		{
			return srv_error(sc,"srv_add_event_handler",SRV_MALLOC,"event table");
		}
		table->type = type;
		table->proc = proc;
		table->data = data;

		if (prev == NULL)
		{
			table->next = sc->event_table;
			sc->event_table = table;
		}
		else
		{
			prev->next = table;
			table->next = NULL;
		}
	}

	return srv_error(sc,"srv_add_event_handler",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_dispatch_event  
 * Date: 01/12/91
 *
 * Description:
 *   Dispatches the passed event by calling all registered 
 *   event handlers for the spcified event.
 * 
 * Linkage: int srv_dispatch_event(sc,event)
 *   SrvContext *sc  - connection record (returned from srv_init)
 *   SrvEvent *event - srvlib dynamic event
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_dispatch_event(sc,event)
	SrvContext *sc;
	SrvEvent *event;
{
	SrvEventTable table;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_dispatch_event",SRV_CONTEXT," ");
	}

	/* scan event table and execute event handlers */
	table = sc->event_table;
	while (table != NULL)
	{
		if (table->type == event->type)
		{
			(*table->proc)(sc,event,table->data);
		}

		table = table->next;
	}

	return srv_error(sc,"srv_dispatch_event",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_next_event
 * Date: 01/03/91
 *
 * Description:
 *   Retrieves the next event from the event queue.
 * 
 * Linkage: int srv_next_event(sc,event)
 *   SrvContext *sc  - connection record (returned from srv_init)
 *   SrvEvent *event - next event (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_next_event(sc,event)
	SrvContext *sc;
	SrvEvent *event;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_next_event",SRV_CONTEXT," ");
	}

	/* While the queue is empty */
	while(sc->first == NULL)
	{
		long type;

		/* read in event type */
		if (read_f(sc->sock,"l",&type) < 0)
		{
			return srv_error(sc,"srv_next_event",SRV_READ,"event type");
		}

		/* Add it to the queue */
		if (add_event(sc,(int)type) < 0)
		{
			return -1;
		}
	}

	/* Remove the first one from the queue */
	{
		SrvEventNode node = sc->first;
		sc->first = node->next;
		*event = node->event;
		free((char *)node);
	}
	return srv_error(sc,"srv_next_event",SRV_OK," ");
}

/****************************************************************
 * Function: srv_pending
 * Date: 01/03/91
 *
 * Description:
 *   Check if there are any pending events in the event queue.
 * 
 * Linkage: int srv_pending(sc)
 *   SrvContext *sc - connection record (returned from srv_init)
 * 
 * Returns:
 *   0 - No pending events
 *   1 - events are pending
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_pending(sc)
	SrvContext *sc;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_pending",SRV_CONTEXT," ");
	}
	return (sc->first != NULL);
}

/****************************************************************
 * ==> Author functions
 ****************************************************************/

/****************************************************************
 * Function: srv_get_author_list 
 * Date: 01/03/91
 *
 * Description:
 *   Returns a list of valids user ids. These ids correspond to
 *   the user ids specified in the /etc/passwd file.
 * 
 * Linkage: int srv_get_author_list(sc,l,n)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   long **l       - list of user ids (returned)
 *   int *n         - number of user ids in list (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function allocates space for the array of author ids 
 *   that is returned. The front-end application is responsible
 *   for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_get_author_list(sc,l,n)
	SrvContext *sc;
	long **l;
	int *n;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_get_author_list",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"l",REQ_GET_AUTHOR_LIST) < 0)
	{
		return srv_error(sc,"srv_get_author_list",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_get_author_list",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"a",l,n) < 0)
	{
		return srv_error(sc,"srv_get_author_list",SRV_READ,"author list");
	}
	return srv_error(sc,"srv_get_author_list",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_get_active_authors 
 * Date: 01/03/91
 *
 * Description:
 *   Returns a list of valids user ids for users who are currently
 *   connected to dbsrv.
 * 
 * Linkage: int srv_get_active_authors(sc,l,n)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   long **l       - list of user ids (returned)
 *   int *n         - number of user ids in list (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function allocates space for the array of author ids 
 *   that is returned. The front-end application is responsible
 *   for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_get_active_authors(sc,l,n)
	SrvContext *sc;
	long **l;
	int *n;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_get_active_authors",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"l",REQ_GET_ACTIVE_AUTHORS) < 0)
	{
		return srv_error(sc,"srv_get_active_authors",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_get_active_authors",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"a",l,n) < 0)
	{
		return srv_error(sc,"srv_get_active_authors",SRV_READ,"author list");
	}
	return srv_error(sc,"srv_get_active_authors",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_allow_author
 * Date: 01/03/91
 *
 * Description:
 *   Allow an author access to a spcific notebook.
 * 
 * Linkage: int srv_allow_author(sc,aid,nid,access)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int aid        - author id
 *   int nid        - notebook id
 *   int access     - access flag 
 *                    = 0 - read access
 *                    = 1 - read/write access
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_allow_author(sc,aid,nid,access)
	SrvContext *sc;
	int aid;
	int nid;
	int access;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_allow_author",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"llll",REQ_ALLOW_AUTHOR,nid,aid,access) < 0)
	{
		return srv_error(sc,"srv_allow_author",SRV_WRITE," ");
	}
	return srv_error(sc,"srv_allow_author",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_disallow_author
 * Date: 01/03/91
 *
 * Description:
 *   Disallow an author access to a specific notebook.
 * 
 * Linkage: int srv_disallow_author(sc,aid,nid)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int aid        - author id
 *   int nid        - notebook id
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_disallow_author(sc,aid,nid)
	SrvContext *sc;
	int aid;
	int nid;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_disallow_author",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"lll",REQ_DISALLOW_AUTHOR,nid,aid) < 0)
	{
		return srv_error(sc,"srv_disallow_author",SRV_WRITE," ");
	}
	return srv_error(sc,"srv_disallow_author",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_get_author_access
 * Date: 01/03/91
 *
 * Description:
 *   Get the access information for a specific notebook.
 * 
 * Linkage: int srv_get_author_access(sc,nid,nacc,acc)
 *   SrvContext *sc    - connection record (returned from srv_init)
 *   int nid           - notebook id
 *   int nacc          - number of access items (returned)
 *   Access_Item **acc - list of access items (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function allocates space for the array of access items
 *   that is returned. The front-end application is responsible
 *   for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_get_author_access(sc,nid,nacc,acc)
	SrvContext *sc;
	int nid;
	Access_Item **acc;
	int *nacc;
{
	long naccesses;
	Access_Item *accesses;

	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_get_author_access",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"ll",REQ_GET_AUTHOR_ACCESS,nid) < 0)
	{
		return srv_error(sc,"srv_get_author_access",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_get_author_access",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"l",&naccesses) < 0)
	{
		return srv_error(sc,"srv_get_author_access",SRV_READ,"number of access");
	}
	accesses = (Access_Item *)malloc(naccesses * sizeof *accesses);
	{
		int i;
		for(i = 0; i < naccesses; i++)
		{
			if (read_f(sc->sock,"ll",&accesses[i].aid,&accesses[i].access) < 0)
			{
				free((char *)accesses);
				return srv_error(sc,"srv_get_author_access",SRV_READ,"access list");
			}
		}
	}
	*acc = accesses;
	*nacc = naccesses;
	return srv_error(sc,"srv_get_author_access",SRV_OK,"return");
}

/****************************************************************
 * ==> Search Functions
 ****************************************************************/

/****************************************************************
 * Function: srv_search_database
 * Date: 01/03/91
 *
 * Description:
 *   Search the database by page title given a spcifiec string.
 *   The function supports case sensitive searches and allows
 *   the user to specifiy a specific wildcard contained within
 *   the string.
 * 
 * Linkage: int srv_search_database(sc,case_sens,wild,s,ln,lp,n)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int case_sens  - (0) off (1) on : case sensitive searches
 *   int wild       - wild card character code
 *   char *s        - search string
 *   long **ln      - list of notebook ids (returned)
 *   long **lp      - list of page ids (returned)
 *   int *n         - number of notebook ids and page ids (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function allocates space for the array of notebook ids
 *   and page ids that is returned. The front-end application is 
 *   responsible for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_search_database(sc,case_sens,wild,s,ln,lp,n)
	SrvContext *sc;
	int case_sens;
	int wild;
	char *s;
	long **ln;
	long **lp;
	int *n;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_search_database",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"llls",REQ_SEARCH_DATABASE,case_sens,wild,s) < 0)
	{
		return srv_error(sc,"srv_search_database",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_search_database",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"a",ln,n) < 0)
	{
		return srv_error(sc,"srv_search_database",SRV_READ,"note id list");
	}
	if (read_f(sc->sock,"a",lp,n) < 0)
	{
		return srv_error(sc,"srv_search_database",SRV_READ,"page id list");
	}
	return srv_error(sc,"srv_search_database",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_search_notebook
 * Date: 01/03/91
 *
 * Description:
 *   Search a notebook for a page by page name
 * 
 * Linkage: int srv_search_notebook(sc,nid,case_sens,wild,s,lp,n)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int nid        - notebook id
 *   int case_sens  - (0) off (1) on : case sensitive searches
 *   int wild       - wild card character code
 *   char *s        - search string
 *   long **lp      - list of page ids (returned)
 *   int *n         - number of page ids (returned)
 * 
 * Returns: standard srvlib error flag
 *
 * Notes:
 *   This function allocates space for the array of page ids
 *   that is returned. The front-end application is responsible
 *   for freeing this memory. 
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_search_notebook(sc,nid,case_sens,wild,s,lp,n)
	SrvContext *sc;
	int nid;
	int case_sens;
	int wild;
	char *s;
	long **lp;
	int *n;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_search_notebook",SRV_CONTEXT," ");
	}

	if (write_f(sc->sock,"lllls",REQ_SEARCH_NOTEBOOK,nid,case_sens,wild,s) < 0)
	{
		return srv_error(sc,"srv_search_notebook",SRV_WRITE," ");
	}
	if (wait_for_reply(sc) < 0)
	{
		return srv_error(sc,"srv_search_notebook",SRV_WAIT," ");
	}
	if (read_f(sc->sock,"a",lp,n) < 0)
	{
		return srv_error(sc,"srv_search_notebook",SRV_READ," ");
	}
	return srv_error(sc,"srv_search_notebook",SRV_OK,"return");
}

/****************************************************************
 * ==> System administration functions (restricted access)
 ****************************************************************/

/****************************************************************
 * Function: srv_shutdown_clients
 * Date: 01/03/91
 *
 * Description:
 *   Shutdown all of the clients connected to the server.
 *
 * Restrictions:
 *   This function will only work for authorized users and is
 *   for system administration only.
 * 
 * Linkage: srv_shutdown_clients(sc)
 *   SrvContext *sc - connection record (returned from srv_init)
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_shutdown_clients(sc)
	SrvContext *sc;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_shutdown_clients",SRV_CONTEXT," ");
	}

	/* check if the invoker is authorized to use this function */
	if (sc->authorized == 0)
	{
		return srv_error(sc,"srv_shutdown_clients",SRV_NOT_AUTHORIZED," ");
	}

	if (write_f(sc->sock,"l",REQ_SHUTDOWN_CLIENTS) < 0) 
	{
		return srv_error(sc,"srv_shutdown_clients",SRV_WRITE,"request to kill the server");
	}

	return srv_error(sc,"srv_shutdown_clients",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_kill
 * Date: 01/03/91
 *
 * Description:
 *   Kill the server.
 *
 * Restrictions:
 *   This function will only work for authorized super and is
 *   for system administration only.
 * 
 * Linkage: srv_kill(sc)
 *   SrvContext *sc - connection record (returned from srv_init)
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_kill(sc)
	SrvContext *sc;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_kill",SRV_CONTEXT," ");
	}

	/* check if the invoker is authorized to used this function */
	if (sc->authorized == 0)
	{
		return srv_error(sc,"srv_kill",SRV_NOT_AUTHORIZED," ");
	}

	if (write_f(sc->sock,"l",REQ_KILL) < 0) 
	{
		return srv_error(sc,"srv_kill",SRV_WRITE,"request to kill the server");
	}

	return srv_error(sc,"srv_kill",SRV_OK,"return");
}

/****************************************************************
 * Function: srv_add_user
 * Date: 01/03/91
 *
 * Description:
 *   This function adds a user to the database. The user id should
 *   be the same as the UID in /etc/passwd for any given user.
 *
 * Restrictions:
 *   This function will only work for authorized users and is
 *   for system administration only.
 * 
 * Linkage: int srv_add_user(sc,user)
 *   SrvContext *sc - connection record (returned from srv_init)
 *   int user       - user id (as in /etc/passwd)
 * 
 * Returns: standard srvlib error flag
 *
 * Revisions:
 ****************************************************************/
PUBLIC
int
srv_add_user(sc,user)
	SrvContext *sc;
	int user;
{
	/* check for valid pointer to the connection record */
	if (sc == NULL)
	{
		return srv_error(sc,"srv_add_user",SRV_CONTEXT," ");
	}

	/* check if the invoker is authorized to used this function */
	if (sc->authorized == 0)
	{
		return srv_error(sc,"srv_add_user",SRV_NOT_AUTHORIZED," ");
	}

	if (write_f(sc->sock,"ll",REQ_ADD_USER,user) < 0)
	{
		return srv_error(sc,"srv_add_user",SRV_WRITE," ");
	}
	return srv_error(sc,"srv_add_user",SRV_OK,"return");
}
