/*
 * server.h,v 1.1 1994/01/28 17:05:27 franktor Exp
 */

#include <high/sr_structcodec.h>

#define PROTOCOL_VERSIONS PROTO_V2

#define SERVICE_SEARCH	0x0001
#define SERVICE_PRESENT	0x0002
#define SERVICE_DEL_SET	0x0004

#define SUPPORTED_OPTIONS (SERVICE_SEARCH|SERVICE_PRESENT|SERVICE_DEL_SET)

#define IMPLEMENTATION_ID	"eapi_server"
#define IMPLEMENTATION_NAME	"High Level API Server"
#define IMPLEMENTATION_VERSION	"v0.1"

#define DIAG_BIB_1_PERMANENT_SYSTEM_ERROR	1
#define DIAG_BIB_1_TEMPORARY_SYSTEM_ERROR	2
#define DIAG_BIB_1_UNSUPPORTED_SEARCH		3
#define DIAG_BIB_1_PRESENT_REQUEST_OUT_OF_RANGE	13
#define DIAG_BIB_1_COMBINATION_OF_DATABASES	23
#define DIAG_BIB_1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST	30
#define DIAG_BIB_1_RESULT_SET_EXISTS_AND_REPLACE_INDICATOR_OFF 21

#define FROM_CLIENT 0
#define FROM_DATABASE 1

#define MAX_HLVL_LOOPS 10	/* To "fix" a structcodec bug */

/*
 * Where to look first for the config file.  It will also check the
 * current directory if it fails to find it here.
 */
#define DEFAULT_CONFIG_FILE \
	"/use/gandalf/us2/srnet/SR/highlvl/server/%s-config"

#define BUF_SIZE 255

/*
 * Global variables.
 */

int line; /* Used when reading the configuration file */
char *localname;
fd_set eapi_read;
fd_set eapi_write;
fd_set eapi_exceptions;
struct DatabaseServer *first_database_server;
struct Client *first_client;
struct DatabaseServerConnection *first_database_server_connection;
struct ConvertInfo *first_convert_info;
int exit_on_idle; /* The program will exit when the last client leaves */
int keeplog;	  /* Don't delete the old logfile */
int ttydebug;	  /* Write logs to tty, not file */
char *debug_logfile; /* Where the debug is written */

/*
 * Information about which format we can convert between, and which
 * OIDs they represent.
 */

typedef struct ConvertInfo
{
  struct ConvertInfo *next;
  char *converter_name;	/* Name of the format */
  Oid abstractSyntax;
  Oid transferSyntax;
} ConvertInfo;

/*
 * Information about the eapi connection.
 */

typedef struct EapiConnectInfo
{
  EapiConnectTypes method;
  union
  {
    struct {
      char *address;
      unsigned int port;
    } ip;
    char *program;
  } u;
} EapiConnectInfo;

typedef struct SupportedSyntaxes {
  struct SupportedSyntaxes *next;
  Oid abstractSyntax;
  Oid transferSyntax;
} SupportedSyntaxes;

/*
 * The DatabaseServer struct is a linked list of available databaseservers.
 * The global variable first_database_server points to the first entry.
 */

typedef struct DatabaseServer
{
  struct DatabaseServer *next;
  char *name;
  DatabaseName *databases;
  Boolean can_interrupt;
  Boolean handle_several_clients;
  Boolean configurable_result_set_names;
  int max_result_sets;
  SupportedSyntaxes *supported_syntaxes;
  EapiConnectInfo connection;
} DatabaseServer;

/*
 * If only one databaseserver can be used for each client-connection, then
 * use ResultSetList instead of ClientResultSetList.
 */

typedef struct ClientResultSetList
{
  char *resultSetId;	/* Used toward the client (low-API) */
  int resultSetNumber;	/* Used toward the server (high-API) */
  struct ClientDatabaseServer *server; /* Which connection it is used with */
  struct ClientResultSetList *next;
  int numberOfRecords;	/* How many records there are in this resultset */
} ClientResultSetList;

typedef enum ClientServerConnectionStatuses {
  clientServerReady = 0,	/* Ready to receive packets		*/
  clientServerOpening = 1,	/* Waiting for OpenResponse		*/
  clientServerClosing = 2,	/* Waiting for CloseResponse		*/
  clientServerBusy = 3		/* Not implemented, since it's nonblocking */
} ClientServerConnectionStatuses;

/*
 * ClientDatabaseServer:
 * Contains information needed for a client to send a packet
 * to a specific database server.
 * In the future it will be possible for several clients to point
 * to the same ClientDatabaseServer structure, but that will
 * require more advanced handling of the elements "data" and "pending_packet".
 */

typedef struct DatabaseServerConnection
{
  struct DatabaseServerConnection *next;
  DatabaseServer *database_server;	/* Information about the connection */
  ClientServerConnectionStatuses state;
  int file_descriptor;	/* Filedescriptor to be listened to in select() */
  void *data;		/* Used by send/receive routines */
  int refcount;		/* How many clients are using this connection now */
  Boolean selectOnWrite;	   /* True if we have to select on write */
  int loopcount;	/* DEBUG: To avoid endless loop from structcodec bug */
} DatabaseServerConnection;

typedef struct ClientDatabaseServer
{
  struct ClientDatabaseServer *next;
  DatabaseServerConnection *connection;	/* This connection can be shared     */
  EapiPacket *pending_packet;	   /* Packet which will be sent as soon      */
				   /* as the connection has been established */
  Boolean waiting_for_packet;	   /* True if the client is blocked for this */
} ClientDatabaseServer;

/*
 * Due to the asynchronous nature of the API protocols, a status-variable
 * to tell what the different clients are doing at the moment is desired.
 * Some of the values specified below are not used since the client
 * never need to stay in that state for long (NU = not used), but the value
 * is included for completedness anyway (apprehending future changes).
 */

typedef enum ClientStatuses {
  clientWaiting = 0,			/* Awaiting new data from client */
  clientOpenRequest = 1,		/* Processing an Open Request (NU) */
  clientCloseRequest = 2,		/* Processing a Close Request */
  clientSearchRequest = 3,		/* Processing a Search Request */
  clientPresentRequest = 4,		/* Processing a Present Request */
  clientDeleteResultSetRequest = 5,	/* Processing DeleteResultSetRequest */
  clientInterruptRequest = 6,		/* Processing an InterruptRequest */
  clientAbort = 7			/* Processing an Abort.  Note that client-connection */
					/* has already been closed; just waiting for the */
					/* databaseserver connections to terminate */
} ClientStatuses;

/*
 * The Client structure is a linked list of currently connected clients.
 * The global variable first_client is a pointer to the first entry.
 */

typedef struct Client
{
  ClientStatuses state;			/* What the client is doing right now */
  int ref;				/* Unique reference                   */
  struct Client *next;			/* Next client in a linked list       */
  ClientDatabaseServer *databaseServers; /* All active connections            */
  ClientResultSetList *resultsets;	/* Linked list of all resultsets.     */

/*
 * Some information from the Initialisation Request:
 * (PROBLEM: What if the different Databases need different authentication?)
 */
  u_int protocolVersion;
  int preferredMessageSize;
  int maximumMessageSize;
  char *user;
  char *password;
  char *account;

/*
 * The implementation information from the client is stored in
 * case the information will be logged in the future.
 */
  char *implementationId;
  char *implementationName;
  char *implementationVersion;
  
/*
 * Unanswered search request.  This can happen since we can only
 * send one delete request at a time through the high-level API.
 * This is very likely to be changed in the near future.
 */
  SRSearchRequest *pending_request;
/*
 * When we receive a search-response we are told how many records were
 * found.  This number must be stored somewhere until we are ready to
 * send the search-response to the client (ie, while we send a present-request
 * back to the server, since the present-response doesn't contain this information).
 * numberOfRecords, which is copied from the selection data, helps calculate
 * the nextResultSetPosition in the PresentResponse.
 */
  int numberOfRecordsFound;
  int numberOfRecords;

/*
 * When processing a present-request, we need to store the resultSetStartPoint,
 * so that we will be able to calculate the nextResultSetPosition in the
 * present-response.
 * When we delete resultsets used in more than one connection at the same
 * time, we must know how many deleteResultSetRespone's we're supposed
 * to get back, hence resultSetsLeftToDelete.
 */
  int resultSetStartPoint;
  int resultSetsLeftToDelete;

/*
 * When calling various handle<whatever>, I suppose I could have
 * given the file-descriptor as an argument.  Right now I store it
 * in the client, and fetch it from the client structure after the call.
 */
  int file_descriptor;

/*
 * Various other variables to store while working:
 */
  Oid preferredRecordSyntax;		/* Which flavour the client prefers */
  ClientDatabaseServer *current_server;	/* Which server we're working with */
					/* (Is set in search/present requests*/
  ConvertInfo *conv_from;		/* Which syntax to convert _from_ */
  ConvertInfo *conv_to;			/* Which syntax to convert _to_ */
  ClientResultSetList *working_delete_request; /* Working on this right now */
  ResultSetList *pending_delete_requests; /* Unfinished delete requests */
  ListStatuses *finished_delete_requests; /* Those acknowledged by DB-server */
  EapiPacket *eapi_incoming;		/* Pointer to incoming packet */
} Client;

typedef enum config_vars {
  eapi_config_illegal = 0,
  eapi_config_abort = 1,
  eapi_config_end = 2,
  eapi_config_method = 4,
  eapi_config_ip_address = 5,
  eapi_config_ip_port = 6,
  eapi_config_program = 7,
  eapi_config_handle_several_clients = 8,
  eapi_config_max_result_sets = 9,
  eapi_config_configurable_result_set_names = 10,
  eapi_config_query_syntax = 11,
  eapi_config_can_interrupt = 12,
  eapi_config_databases = 13,
  eapi_config_supported_syntaxes = 14
} config_vars;
