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

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

/*
 * events.c - support to deal with the PdReportEvent call
 */

#include <stdio.h>
#include "PDsrvr.h"
#include "events.h"

void
PDif_PdReportEvent_local ( fBinding, objectClass, objectId, eventLog,
                          eventTime, eventType, eventInfo, pdError)
     HRPCBinding *fBinding;
     String objectClass;
     String objectId;
     String eventLog;
     LongCardinal eventTime;
     String eventType;
     String eventInfo;
     PDif_PdError *pdError;
{
  struct List *entry;
  short Vtype;
  value_list list;
  int size;
  short EntityType;  
  char linkFrom[MAXPATHLEN+1], linkTo[MAXPATHLEN+1];
  char *printerName;
  char jobIdBuf[64];	/* This is used for the sscanf purposes and jobId
			   points to that later. */
  char *jobId = jobIdBuf;
  
  debugmsg("Entering PdReportEvent");

  if (strcmp(eventType, XFERCOMPLETE) == 0) {
    char msg[128];
    PDif_JobState stat;

    /*** XferComplete ***/

    /* here, one would change the job status from 'intransfer'
       to 'pending', then call schedule_new_job in the scheduler
       to get the thing either printed or queued */

    /* 
     * Pradeep: Change due to change in directory structure.
     *    First link the file from the client directory to the
     *    printer directory.
     */
    if (!strcmp(objectClass,PRINTEROBJECT))
      printerName = objectId;
    else {
      char errmsg[128];
      sprintf(errmsg, "Xfercomplete on unknown job %s from %s",
	      jobId, objectId);
      SetError(pdError, NOSUCHJOB);
      LogMessage(LOG_INFO, "ReportEvent", errmsg);
      debugmsg("Exiting PdReportEvent");
      return;
    }
      
    jobId = savestr(eventInfo);
    sprintf(msg, "XferComplete for %s", jobId);

    sprintf(linkFrom,"%s/%s/%s%s",spoolDir, CLIENTDIR, DATAPREFIX, jobId);
    sprintf(linkTo,"%s/%s/%s%s",spoolDir, printerName, DATAPREFIX, jobId);
    if (link(linkFrom, linkTo) < 0) {
      printf("link(%s,%s) failed, errno = %d",linkFrom, linkTo, errno);
      return;
    }

    /*
     * Now unlink the old file. We should actually use rename() but later
     * we use unlinking in SupervisorDir without unlinking in PrinterDir.
     * So let's keep consistent code.
     */
#ifdef DEBUG
    syslog(LOG_INFO,"unlinking the file in ClientDir, %s",linkFrom);
#endif

    if (unlink(linkFrom) < 0)
      syslog(LOG_INFO,"unlink(%s) failed, errno = %d", linkFrom, errno);

    new_char_pair(&list, JOBID, jobId, &size);
    EntityType = JOB;
    if ((entry = Find(EntityType, list)) == LNULL) {
      char errmsg[128];
      sprintf(errmsg, "Xfercomplete on unknown job %s from %s",
	      jobId, objectId);
      SetError(pdError, NOSUCHJOB);
      LogMessage(PDLOG_IMPORTANT, "ReportEvent", errmsg);
      debugmsg("Exiting PdReportEvent");
      return;
    }
    /* set jobstate to pending */
    stat = (PDif_JobState) RetrieveKey(entry, 
				       CURRENTJOBSTATE,	&Vtype);
    SetKey(entry, EntityType, PREVIOUSJOBSTATE, (int) stat, INT);
    /*
     * Pradeep  - later
     *  If the server is PAUSED, shouldn't I be changing the
     *  CURRENTJOBSTATE to Hold instead of Pending.
     *  OTHERWISE the ScheduleFreeSpvr() will pick up this job.
     *  Later in PdResume we can change this state to Pending.
     *  NO, when I pause SERVER, I pause scheduler also. Hence above
     *  thing is not needed.
     */ 
    SetKey(entry, EntityType, CURRENTJOBSTATE, (int) Pending, INT);


    /* Call scheduler to try and enqueue only if server is scheduling jobs */
    
    if (ServerState.printingJobs == TRUE) {
      if (PdScheduleNewJob(jobId))
	debugmsg("ScheduleNewJob() failed");
    } else {
      debugmsg("Server paused, job accepted but won't be scheduled");
    }
    SetError(pdError, NOERROR);
    debugmsg("Exiting PdReportEvent");
    return;

  } else if (!strcmp(eventType, JOBCOMPLETE)) {

    /*** JobComplete ***/
    LongCardinal time;
    PDif_JobState stat;
    int currentTime, retentionTime;
    char location[64];	/* enough for Bldg/room# */

    sscanf(eventInfo, "%s %d %s",jobId, &time, location);
    new_char_pair(&list, JOBID, jobId, &size);
    EntityType = JOB;
    if ((entry = Find(EntityType, list)) == LNULL) {
      char errmsg[128];
      sprintf(errmsg, "JobComplete on unknown job %s from %s\n",
	      jobId, objectId);
      SetError(pdError, NOSUCHJOB);
      LogMessage(PDLOG_IMPORTANT, "ReportEvent", errmsg);
      debugmsg("Exiting PdReportEvent");
      return;
    }
    stat = (PDif_JobState) RetrieveKey(entry, 
				       CURRENTJOBSTATE,	&Vtype);
    /* TODO: if Vtype ain't int, freak out */
    SetKey(entry, EntityType, PREVIOUSJOBSTATE, (int) stat, INT);
    debugmsg("Previous job state changed");
    SetKey(entry, EntityType, CURRENTJOBSTATE, (int) Completed, INT);
    debugmsg("Current job state updated");
    /* The next one becomes zero if not there in the JOB file */
    retentionTime = (int) RetrieveKey(entry, JOBRETENTIONTIME, &Vtype);
    /* now log the information */
    Accounting(objectId,
	       (char *) RetrieveKey(entry, 
				    USERNAME, &Vtype),
	       (int ) RetrieveKey(entry, 
				  ACCOUNT, &Vtype),
	       (int ) RetrieveKey(entry, 
				  JOBPRIORITY, &Vtype),
	       (int ) RetrieveKey(entry, 
				  SUBMISSIONTIME,
				  &Vtype),
	       99999,
	       (int) RetrieveKey(entry, 
				 PAGESUSED, &Vtype),
	       (int) RetrieveKey(entry, 
				 SECONDSUSED, &Vtype));
    NotifyComplete(entry, location);
    new_char_pair(&list, SPVRNAME, objectId, &size);
    if ((entry = Find(SPVR, list)) == LNULL) {
      debugmsg("Couldn't get supervisor entry to change state to FREE, so it's all broken now");
      return;
    }
    SetKey(entry, SPVR, "SupervisorState", (int) 0, INT);

    /*
     * Pradeep
     *   After the job is completed we see of there is any retention time.
     *   If yes then keep it (and eventually the skulker will remove it)
     *   else remove it now. Makes Listjobs() also faster.
     */
    currentTime = current_time();

#ifdef DEBUG
    syslog(LOG_INFO,"currentTime = %d, retentionTime = %d", currentTime, 
	   retentionTime);
#endif DEBUG
    if (currentTime > retentionTime) {
      /* Blow it away */
      char jobFile[MAXPATHLEN+1], dataFile[MAXPATHLEN+1];
      char *ptr;

      strcpy(jobFile,GetJobFileName(jobId));
      if (*jobFile == '\0') {
	syslog(LOG_INFO,"No JOB in the database matching jobId = %s", jobId);
	return;
      }
      strcpy(dataFile,jobFile);
      if ((ptr = rindex(dataFile,'/')) == NULL)
	dataFile[0] = 'd';
      else
	*++ptr = 'd';
#ifdef DEBUG
      syslog(LOG_INFO,"About to unlink %s & %s after completion",
	     jobFile, dataFile);
#endif
      (void) unlink(jobFile);
      (void) unlink(dataFile);
    }
    SetError(pdError, NOERROR);
    debugmsg("Exiting PdReportEvent");
    return;

  } else if (!strcmp(eventType, JOBSTARTED)) {

    /*** JobStarted ***/
    LongCardinal time;
    PDif_JobState stat;

    sscanf(eventInfo, "%s %d",jobId, &time);
    new_char_pair(&list, JOBID, jobId, &size);
    EntityType = JOB;
    if ((entry = Find(EntityType, list)) == LNULL) {
      char errmsg[128];
      sprintf(errmsg, "JobComplete on unknown job %s from %s",
	      jobId, objectId);
      SetError(pdError, NOSUCHJOB);
      LogMessage(PDLOG_IMPORTANT, "ReportEvent", errmsg);
      debugmsg("Exiting PdReportEvent");
      return;
    }
    NotifyStarted(entry);
    SetError(pdError, NOERROR);
    debugmsg("Exiting PdReportEvent");
    return;
  } else if (!strcmp(eventType, ACCOUNTING)) {

    /*** Accounting ***/
    int  pages, copies;
    LongCardinal time_delta, time;
    
    sscanf(eventInfo,"%s %d %d %d", jobId, &copies, &pages, &time);
    new_char_pair(&list, JOBID, jobId, &size);
    EntityType = JOB;
    if ((entry = Find(EntityType, list)) == LNULL) {
      char errmsg[128];
      sprintf(errmsg,
	      "AccountingInfo event recieved for unknown job %s from %s\n",
	      jobId, objectId);
      SetError(pdError, NOSUCHJOB);
      LogMessage(PDLOG_IMPORTANT, "ReportEvent", errmsg);
      return;
    }
    SetKey(entry, EntityType, COPIESCOMPLETED, copies, INT);
    SetKey(entry, EntityType, PAGESUSED, pages, INT);
    SetKey(entry, EntityType, SECONDSUSED, time, INT);
    SetError(pdError, NOERROR);
    debugmsg("Exiting PdReportEvent");
    return;
  } else if (!strcmp(eventType, JOBABORTED)) {

    /*** JobAborted ***/
    LongCardinal time;
    PDif_JobState stat;
    int currentTime, retentionTime;

    sscanf(eventInfo,"%s %d", jobId);
    new_char_pair(&list, JOBID, jobId, &size);
    EntityType = JOB;
    if ((entry = Find(EntityType, list)) == LNULL) {
      char errmsg[128];
      sprintf(errmsg, "JobAborted on unknown job %s from %s",
	      jobId, objectId);
      SetError(pdError, NOSUCHJOB);
      LogMessage(PDLOG_IMPORTANT, "ReportEvent", errmsg);
      debugmsg("Exiting PdReportEvent");
      return;
    }
    stat = (PDif_JobState) RetrieveKey(entry,
				       CURRENTJOBSTATE, &Vtype);
    SetKey(entry, JOB, PREVIOUSJOBSTATE, (int)stat, INT);
    SetKey(entry, EntityType, CURRENTJOBSTATE, (int)Aborted, INT);
    /* Do some cleaning of files when aborted */
    /* The next one becomes zero if not there in the JOB file */
    retentionTime = (int) RetrieveKey(entry, JOBRETENTIONTIME, &Vtype);
    /* now log the information */
    Accounting(objectId,
	       (char *) RetrieveKey(entry, 
				    USERNAME, &Vtype),
	       (int ) RetrieveKey(entry, 
				  ACCOUNT, &Vtype),
	       (int ) RetrieveKey(entry, 
				  JOBPRIORITY, &Vtype),
	       (int ) RetrieveKey(entry, 
				  SUBMISSIONTIME,
				  &Vtype),
	       99999,
	       (int) RetrieveKey(entry, 
				 PAGESUSED, &Vtype),
	       (int) RetrieveKey(entry, 
				 SECONDSUSED, &Vtype));
    NotifyAbort(entry);
    /*
     * Even when the SPVR is aborted the state of SPVR becomes FREE
     * as it is ready to take more jobs.
     */
    new_char_pair(&list, SPVRNAME, objectId, &size);
    if ((entry = Find(SPVR, list)) == LNULL) {
      debugmsg("Couldn't get supervisor entry to change state to FREE, so it's all broken now");
      return;
    }
    SetKey(entry, SPVR, "SupervisorState", (int) 0, INT);

    /*
     * Pradeep
     *   After the job is aborted we see of there is any retention time.
     *   If yes then keep it (and eventually the skulker will remove it)
     *   else remove it now. Makes Listjobs() also faster.
     */
    currentTime = current_time();

#ifdef DEBUG
    syslog(LOG_INFO, "Cleaning of files when job aborted");
    syslog(LOG_INFO,"currentTime = %d, retentionTime = %d", currentTime, 
	   retentionTime);
#endif DEBUG
    if (currentTime > retentionTime) {
      /* Blow it away */
      char jobFile[MAXPATHLEN+1], dataFile[MAXPATHLEN+1];
      char *ptr;

      strcpy(jobFile,GetJobFileName(jobId));
      if (*jobFile == '\0') {
	syslog(LOG_INFO,"No JOB in the database matching jobId = %s", jobId);
	return;
      }
      strcpy(dataFile,jobFile);
      if ((ptr = rindex(dataFile,'/')) == NULL)
	dataFile[0] = 'd';
      else
	*++ptr = 'd';
#ifdef DEBUG
      syslog(LOG_INFO,"About to unlink %s & %s after completion",
	     jobFile, dataFile);
#endif
      (void) unlink(jobFile);
      (void) unlink(dataFile);
    }
    SetError(pdError, NOERROR);
    debugmsg("Exiting PdReportEvent");
    return;
  } else if (!strcmp(eventType, READYFORJOB)) {
    /* deal with the ready for job call */
    debugmsg("Got ready for job call");
    if (PdScheduleFreeSpvr(eventInfo))
      debugmsg("Failure in PdScheduleFreeSpvr()");
    SetError(pdError, NOERROR);
    debugmsg("Exiting PdReportEvent");
    return;
  } else {
    SetError(pdError, NOSUCHEVENT);
    debugmsg("Exiting PdReportEvent");
    return;
  }
}

NotifyComplete(jobentry, location)
     struct List *jobentry;
     char location[];
{
  char **trigs, message[256];
  short Vtype;

  trigs = (char **)RetrieveKey(jobentry, NOTIFYTRIGS, &Vtype);
  while (trigs && *trigs) {
    if (!strcmp(*trigs, EVENT_JOBEND)) {
      char *method;
      method = (char *)RetrieveKey(jobentry, NOTIFYMETHOD, &Vtype);
      sprintf(message,"Your job, (%s), on (%s) at (%s), is finished:\n(%d) pages were printed, using the printer for (%d) seconds\n",
	      (char *)RetrieveKey(jobentry, JOBTITLE, &Vtype),
	      (char *)RetrieveKey(jobentry, PRINTERNAME, &Vtype),
	      location,
	      (int)RetrieveKey(jobentry, PAGESUSED, &Vtype),
	      (int)RetrieveKey(jobentry, SECONDSUSED, &Vtype));
      if (!strcmp(method, NOTIFY_BY_ZEPHYR))
	NotifyUser((char *)RetrieveKey(jobentry, NOTIFYNAME, &Vtype), message);
      else
	Mail((char *)RetrieveKey(jobentry, NOTIFYNAME, &Vtype), message);
    }
    trigs++;
  }
}


NotifyStarted(jobentry)
     struct List *jobentry;
{
  char **trigs, message[256];
  short Vtype;

  trigs = (char **)RetrieveKey(jobentry, NOTIFYTRIGS, &Vtype);
  while (trigs && *trigs) {
    if (!strcmp(*trigs, EVENT_PRINTSTART)) {
      char *method;
      method = (char *)RetrieveKey(jobentry, NOTIFYMETHOD, &Vtype);
      sprintf(message,"Your job, (%s), on (%s), has started:\n\n",
	      (char *)RetrieveKey(jobentry, JOBTITLE, &Vtype),
	      (char *)RetrieveKey(jobentry, PRINTERNAME, &Vtype));
      if (!strcmp(method, NOTIFY_BY_ZEPHYR))
	NotifyUser((char *)RetrieveKey(jobentry, NOTIFYNAME, &Vtype), message);
      else
	Mail((char *)RetrieveKey(jobentry, NOTIFYNAME, &Vtype), message);
    }
    trigs++;
  }
}


NotifyAbort(jobentry)
     struct List *jobentry;
{
  char **trigs, message[256];
  short Vtype;

  trigs = (char **)RetrieveKey(jobentry, NOTIFYTRIGS, &Vtype);
  while (trigs && *trigs) {
    if (!strcmp(*trigs, EVENT_JOBABORT)) {
      char *method;
      method = (char *)RetrieveKey(jobentry, NOTIFYMETHOD, &Vtype);
      sprintf(message,"Your job, (%s), on (%s), was ABORTED;\n(%d) pages were printed, using the printer for (%d) seconds\n",
	      (char *)RetrieveKey(jobentry, JOBTITLE, &Vtype),
	      (char *)RetrieveKey(jobentry, PRINTERNAME, &Vtype),
	      (int)RetrieveKey(jobentry, PAGESUSED, &Vtype),
	      (int)RetrieveKey(jobentry, SECONDSUSED, &Vtype));
      if (!strcmp(method, NOTIFY_BY_ZEPHYR))
	NotifyUser((char *)RetrieveKey(jobentry, NOTIFYNAME, &Vtype), message);
      else
	Mail((char *)RetrieveKey(jobentry, NOTIFYNAME, &Vtype), message);
    }
    trigs++;
  }
}
