
/* $Header: $ */
/* $Source: $ */
/* $Author: $ */

#ifndef lint
static char rcs_id[] = "$Header: $";
#endif lint

/*
 * support for PdRegister command
 */

#include <stdio.h>
#include "PDsrvr.h"
#include "adm_changes.h"
#include <syslog.h>
#include <sys/time.h>

AttribSeq_to_ValueList(Attrib, vlist, listsize)
     PDif_T_p90125_41 Attrib;
     value_list *vlist;
     int *listsize;
{
  String name, strval;
  int intval, i;
  register PDif_Attribute *ap;

  ap = Attrib.sequence;
  for (i = 0; i < Attrib.length; i++) {
    name = ap->attributeId;
    switch (ap->attributeValue.designator) {
    case integer:
      intval = ap->attributeValue.integer_case;
      if (i==0)
	new_int_pair(vlist, name, intval, listsize);
      else
	add_int_pair(vlist, name, intval, listsize);
      break;
    case utcTime:
      intval = ap->attributeValue.utcTime_case;
      if (i==0)
	new_int_pair(vlist, name, intval, listsize);
      else
	add_int_pair(vlist, name, intval, listsize);
      break;
    case ia5String:
      strval = ap->attributeValue.ia5String_case;
      if (i==0)
	new_char_pair(vlist, name, strval, listsize);
      else
	add_char_pair(vlist, name, strval, listsize);
      break;
    case hierarchicalName:
      strval = ap->attributeValue.hierarchicalName_case;
      if (i==0)
	new_char_pair(vlist, name, strval, listsize);
      else
	add_char_pair(vlist, name, strval, listsize);
      break;
    case octetString:
      strval = ap->attributeValue.octetString_case;
      if (i==0)
	new_char_pair(vlist, name, strval, listsize);
      else
	add_char_pair(vlist, name, strval, listsize);
    }
    ap++;
  }
  free(Attrib.sequence);
}

/* 
 *  PRADEEP
 *    Here we also maintain a list of all the SUPERVISORS registered
 *    with this server. This list also is updated for unregistering.
 *    This list is not NULL terminated, as we keep a number also for 
 *    faster searching.
 */

char **supervisorsRegistered;
int numSupervisorsRegistered = 0;
 
int
RegisterSupervisor(objectId, objectAttributes)
     String objectId;
     PDif_T_p90125_41 *objectAttributes;

{
  struct List *entry, *tmpHead;
  short EntityType, Vtype;
  value_list pairs;
  int listlen;
  char scratch[256], **activePrinters, *printerName;
  Entity entity;
  int i = 0, found;
  
  /* First, is this logical printer already registered? */
  new_char_pair(&pairs, SPVRNAME, objectId, &listlen);
  pairs[listlen] = NULL;
  EntityType = SPVR;
  if ((entry = Find(EntityType, pairs)) == LNULL) {
    /* then this is a new entry - proceed as necessary */
    /* turn the objectAttributes into name=value pairs */
    listlen=0;
    AttribSeq_to_ValueList(*objectAttributes, &pairs, &listlen);
    if (AddEntry(EntityType, pairs) != 0) {
      return(NODBACCESS);
    }
  } else {
    register PDif_Attribute *ap;
    int result, count;
    char *name, *hold;
    short Vtype;
    
    /* this logprinter is already registered - I'm assuming
       that this means that the Attributes in the list
       are to be changed. Loop through, and if the attrib is
       already there, change it, otherwise add the pair */
    ap = objectAttributes->sequence;

    for (count = 0; count < objectAttributes->length; count++) {
      if (strcmp(ap->attributeId, SPVRNAME)) {
	/* because of a bad database bug, we can't SetKey for
	   the Name, so skip it */
	name = ap->attributeId;
	/* replace the value of the attribute */
	result = set_attribute(entry, EntityType, name, ap->attributeValue);
	if (result != 0)   /* it didn't work */
	  return(result);
      }
      ap++;
    }
  }

  /* 
   * UPDATE 'supervisorsRegistered' structure & number.
   * IMP: Update only if a new SPVR is added, reregistrations of the
   * same SPVR should not bump up the number.
   */
     
  if (numSupervisorsRegistered == 0) {
    supervisorsRegistered = (char **) 
      malloc(++numSupervisorsRegistered * sizeof(char *));
    supervisorsRegistered[0] = savestr(objectId);
  }
  else { 		/* check if it is already registered */
    for (i = 0; i < numSupervisorsRegistered; i++) {
      if (!strcmp(objectId,supervisorsRegistered[i]))
	break;		/* Found, don't add to list, break away */
      if (i == (numSupervisorsRegistered - 1)) {       /* Couldn't find, add */
	supervisorsRegistered = (char **) 
	  realloc(supervisorsRegistered, ++numSupervisorsRegistered * sizeof(char *));
	supervisorsRegistered[numSupervisorsRegistered - 1] = savestr(objectId);
      }
    }
  }

  /* UPDATE the head[LOGPRINTER] struture */
  /* Get the entry just added *or* updated. */
  listlen = 0;
  new_char_pair(&pairs, SPVRNAME, objectId, &listlen);
  pairs[listlen] = NULL;
  EntityType = SPVR;
  if ((entry = Find(EntityType, pairs)) == LNULL) {
    syslog(LOG_INFO,"The SPVR %s just added can't be found",objectId);
    return(NODBACCESS);
  }
  activePrinters = (char **) RetrieveKey(entry,LOGICALPRINTERS,&Vtype);
  while (*activePrinters) {
    found = FALSE;
    /* First check if already there */
    for ( tmpHead = head[LOGPRINTER]; tmpHead != LNULL; 
	 tmpHead = tmpHead->l_next) {
      entity = tmpHead->l_entity;
      printerName = entity[EntityLookup(entity,"Name",VERBOSE)].e_str_value;
      if (!strcmp(*activePrinters,printerName)) {
	found = TRUE;
	break;
      }
    }
    if (!found) {
      /* reuse tmpHead & entity for new entity */
      entity = (Entity) malloc(2 * sizeof(struct entry));
      entity[0].e_name = "Name";
      entity[0].e_vtype = CHARPTR;
      entity[0].e_union.u_str_value = *activePrinters;
      entity[1].e_name = NULL;
      entity[1].e_vtype = NULLTYPE;
      entity[1].e_union.u_str_value = NULL;
      Create(&tmpHead, entity);
      INSERT(head[LOGPRINTER],tmpHead);
      numEntities[LOGPRINTER]++;
    }
    activePrinters++;
  }
  
  /* UPDATE IN SCHEDULER DATABASE ALSO */
  if (PdKickScheduler(objectId) < 0) {
    syslog(LOG_INFO,"Unable to inform scheduler of SPVR registration");
    return(SCHEDULERCOMMFAILED);
  }

  /*
   * Pradeep Chetal
   *   There may be cases where the Supervisor died while actively 
   *   printing a job. So when the SPVR tries to (re)register there will
   *   A (THE) JOB for THIS SPVR which has CurrentState "Active" BUT is
   *   *not* Active b'cos the SPVR died. So it may never be considered by
   *   the scheduler. Find this JOB and make it's current state "Pending".
   */

  new_char_pair(&pairs, SUPERVISORNAME, objectId, &listlen);
  add_int_pair(&pairs, CURRENTJOBSTATE, Active, &listlen);
  pairs[listlen] = NULL;
  EntityType = JOB;
  if ((entry = Find(EntityType, pairs)) != LNULL) {
    /* Found an un-finished job, change CURRENTSTATE to Pending again */
    char *jobId = RetrieveKey(entry, JOBID, &Vtype);

    if (SetKey(entry, JOB, PREVIOUSJOBSTATE,
		 RetrieveKey(entry, CURRENTJOBSTATE, &Vtype), INT) < 0) {
	syslog(LOG_INFO,"RegisterSupervisor: Can't set prevjobState (%s) state to currentjobstate\n", jobId);
	return(NODBACCESS);
      }
    if (SetKey(entry, JOB, CURRENTJOBSTATE, (char *) Pending, INT) < 0) {
	syslog(LOG_INFO," RegisterSupervisor: Can't set CurrentJobState(%s) state to Active.\n",jobId);
	return(NODBACCESS);
      }
  }
  /* and we're done!! */
  return(NOERROR);
}

int
add_attribute(e, type, lhs, attribval)
     struct List *e;
     short type;
     char *lhs;
     PDif_Any attribval;
{
  short Vtype;

  switch (attribval.designator){
  case integer:
    if (InsertKey(e, type, lhs, attribval.integer_case, INT))
      return(NODBACCESS);
    break;
  case utcTime:
    if (InsertKey(e, type, lhs, attribval.utcTime_case, INT))
      return(NODBACCESS);
    break;
  case ia5String:
    if (InsertKey(e, type, lhs, attribval.ia5String_case, CHARPTR))
      return(NODBACCESS);
    break;
  case hierarchicalName:
    if (InsertKey(e, type, lhs, attribval.hierarchicalName_case, CHARPTR))
      return(NODBACCESS);
    break;
  case octetString:
    if (InsertKey(e, type, lhs, attribval.octetString_case, CHARPTR))
      return(NODBACCESS);
    break;
  }
  return(NOERROR);
}
    
set_attribute(e, type, lhs, attribval)
     struct List *e;
     short type;
     char *lhs;
     PDif_Any attribval;
{
  short Vtype;
  char *hold;

  if (((hold = RetrieveKey(e,  lhs, &Vtype)) == NULL) &&
      Vtype != INT)
    return(NODBACCESS);

  switch(attribval.designator) {
  case integer:
    if (Vtype != INT)
      return(IMPROPERATTRIBTYPE);
    SetKey(e, type, lhs, attribval.integer_case, Vtype);
    break;
  case utcTime:
    if (Vtype != INT)
      return(IMPROPERATTRIBTYPE);
    SetKey(e,type, lhs, attribval.utcTime_case, Vtype);
    break;
  case ia5String:
    if ((Vtype != CHARPTR) && (Vtype != LIST))
      return(IMPROPERATTRIBTYPE);
    if (Vtype == CHARPTR)
      SetKey(e, type, lhs, attribval.ia5String_case, Vtype);
    else {
      char buf[256];
      char *name, *value;
      short vType;

      sprintf(buf,"%s = %s", lhs, attribval.hierarchicalName_case);
      GetNameValueType(buf, &name, &value, &vType);
      SetKey(e, type, lhs, value, Vtype);
    }
    break;
  case hierarchicalName:
    if ((Vtype != CHARPTR) && (Vtype != LIST))
      return(IMPROPERATTRIBTYPE);
    if (Vtype == CHARPTR)
      SetKey(e, type, lhs, attribval.hierarchicalName_case, Vtype);
    else {
      char buf[256];
      char *name, *value;
      short vType;

      sprintf(buf,"%s = %s", lhs, attribval.hierarchicalName_case);
      GetNameValueType(buf, &name, &value, &vType);
      SetKey(e, type, lhs, value, Vtype);
    }
    break;
  case octetString:
    if ((Vtype != CHARPTR) && (Vtype != LIST))
      return(IMPROPERATTRIBTYPE);
    if (Vtype == CHARPTR)
      SetKey(e, type, lhs, attribval.octetString_case, Vtype);
    else {
      char buf[256];
      char *name, *value;
      short vType;

      sprintf(buf,"%s = %s", lhs, attribval.hierarchicalName_case);
      GetNameValueType(buf, &name, &value, &vType);
      SetKey(e, type, lhs, value, Vtype);
    }
    break;
  }
  return(NOERROR);
}
      

/*
 * PdRegister - another entity wishes to register with this server
 */

void
PDif_PdRegister_local( fBinding, objectClass, objectId,
		      objectAttributes, security, pdError)
     HRPCBinding *fBinding;
     String objectClass;
     String objectId;
     PDif_T_p90125_41 *objectAttributes;
     PDif_SecurityAccessAccounting *security;
     PDif_PdError *pdError;
{
  struct List entry;
  value_list *vlist;
  short Vtype;
  int vlistc;
  short EntityType;
  
  debugmsg("Entering PdRegister");
  
  /* security check goes here */
  if (UserAuthenticated(security) != TRUE) {
    SetError(pdError, NOACCESS);
    debugmsg("Exiting PdRegister");
    return;
  }

  if (UserAuthorized(security) != TRUE) {
    SetError(pdError, NOACCESS);
    debugmsg("Exiting PdRegister");
    return;
  }

  /* I am assuming that a supervisor calls PdRegister with
     the class and name of the printer it oversees and that
     the mapping in the association table is from
     printername <--> logicalname, as opposed to
     supervisorname <-->logicalname */

  /* The major question is: does the Supervisor call here with the
     printer name, which is mapped to logical printers *here*, or
     does it use the association table on its own and call
     PdRegister for each one??? if the former, then why pass
     objectAttributes? */

  /* And what about re-registration? What does that do? */

  if (!strcmp(objectClass, SUPERVISOROBJECT)) {
    syslog(LOG_INFO, "Registering the %s named %s\n",
	   objectClass, objectId);
    SetError(pdError, RegisterSupervisor(objectId, objectAttributes));
    debugmsg("Exiting PdRegister");
  } else {
    SetError(pdError, OBJECTCANNOTREGISTER);
    debugmsg("Exiting PdRegister");
    return;
  }
}


/*
 * PdUnregister - an entity wishes to unregister with this server
 */

void
PDif_PdUnregister_local ( fBinding, objectClass, objectId, security, pdError )
     HRPCBinding *fBinding;
     String objectClass;
     String objectId;
     PDif_SecurityAccessAccounting *security;
     PDif_PdError *pdError;
{
	 debugmsg("Entering PdUnregister");

	 debugmsg("Exiting PdUnregister");
}


/*
 * PdModifyAttributes - modify the attributes of whatever it
 *                      is that has attributes to modify, whatever
 *                      attributes are, anyway
 */

void
PDif_PdModifyAttributes_local(fBinding, objectClass, objectId,
			      objectAttributes, security, pdError)
     HRPCBinding *fBinding;
     String objectClass;
     String objectId;
     PDif_T_p90125_43 *objectAttributes;
     PDif_SecurityAccessAccounting *security;
     PDif_PdError *pdError;
{
  debugmsg("Entering PdModifyAttributes");
  /* The assumption here is that the attributes passed in
     are deltas - eg, they are replacements for the values
     that are already in the database, or if the database
     doesn't currently hold that attribute, it is added. */
  
  /* Assumption: only Supervisors can have their attributes modified */
  if (strcmp(objectClass, SUPERVISOROBJECT)) {
    /* its not a supervisor, so bitch */
    SetError(pdError, OBJECTCANNOTREGISTER);
    debugmsg("Exiting PdModifyAttributes");
    return;
  }

  /* RegisterSupervisor deals properly with both delta lists
     and complete lists, so just pass attributes to it */
  SetError(pdError, RegisterSupervisor(objectId, objectAttributes));
  debugmsg("Exiting PdModifyAttributes");
  return;
}

