/*
 * "$Id: quotas.c 10996 2013-05-29 11:51:34Z msweet $"
 *
 *   Quota routines for the CUPS scheduler.
 *
 *   Copyright 2007-2011 by Apple Inc.
 *   Copyright 1997-2007 by Easy Software Products.
 *
 *   These coded instructions, statements, and computer programs are the
 *   property of Apple Inc. and are protected by Federal copyright
 *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
 *   which should have been included with this file.  If this file is
 *   file is missing or damaged, see the license at "http://www.cups.org/".
 *
 * Contents:
 *
 *   cupsdFindQuota()   - Find a quota record.
 *   cupsdFreeQuotas()  - Free quotas for a printer.
 *   cupsdUpdateQuota() - Update quota data for the specified printer and user.
 *   add_quota()        - Add a quota record for this printer and user.
 *   compare_quotas()   - Compare two quota records...
 */

/*
 * Include necessary headers...
 */

#include "cupsd.h"


/*
 * Local functions...
 */

static cupsd_quota_t	*add_quota(cupsd_printer_t *p, const char *username);
static int		compare_quotas(const cupsd_quota_t *q1,
			               const cupsd_quota_t *q2);


/*
 * 'cupsdFindQuota()' - Find a quota record.
 */

cupsd_quota_t *				/* O - Quota data */
cupsdFindQuota(
    cupsd_printer_t *p,			/* I - Printer */
    const char      *username)		/* I - User */
{
  cupsd_quota_t	*q,			/* Quota data pointer */
		match;			/* Search data */
  char		*ptr;			/* Pointer into username */


  if (!p || !username)
    return (NULL);

  strlcpy(match.username, username, sizeof(match.username));
  if ((ptr = strchr(match.username, '@')) != NULL)
    *ptr = '\0';			/* Strip @domain/@KDC */

  if ((q = (cupsd_quota_t *)cupsArrayFind(p->quotas, &match)) != NULL)
    return (q);
  else
    return (add_quota(p, username));
}


/*
 * 'cupsdFreeQuotas()' - Free quotas for a printer.
 */

void
cupsdFreeQuotas(cupsd_printer_t *p)	/* I - Printer */
{
  cupsd_quota_t *q;			/* Current quota record */


  if (!p)
    return;

  for (q = (cupsd_quota_t *)cupsArrayFirst(p->quotas);
       q;
       q = (cupsd_quota_t *)cupsArrayNext(p->quotas))
    free(q);

  cupsArrayDelete(p->quotas);

  p->quotas = NULL;
}


/*
 * 'cupsdUpdateQuota()' - Update quota data for the specified printer and user.
 */

cupsd_quota_t *				/* O - Quota data */
cupsdUpdateQuota(
    cupsd_printer_t *p,			/* I - Printer */
    const char      *username,		/* I - User */
    int             pages,		/* I - Number of pages */
    int             k)			/* I - Number of kilobytes */
{
  cupsd_quota_t		*q;		/* Quota data */
  cupsd_job_t		*job;		/* Current job */
  time_t		curtime;	/* Current time */
  ipp_attribute_t	*attr;		/* Job attribute */


  if (!p || !username)
    return (NULL);

  if (!p->k_limit && !p->page_limit)
    return (NULL);

  if ((q = cupsdFindQuota(p, username)) == NULL)
    return (NULL);

  cupsdLogMessage(CUPSD_LOG_DEBUG,
                  "cupsdUpdateQuota: p=%s username=%s pages=%d k=%d",
                  p->name, username, pages, k);

  curtime = time(NULL);

  if (curtime < q->next_update)
  {
    q->page_count += pages;
    q->k_count    += k;

    return (q);
  }

  if (p->quota_period)
    curtime -= p->quota_period;
  else
    curtime = 0;

  q->next_update = 0;
  q->page_count  = 0;
  q->k_count     = 0;

  for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
       job;
       job = (cupsd_job_t *)cupsArrayNext(Jobs))
  {
   /*
    * We only care about the current printer/class and user...
    */

    if (_cups_strcasecmp(job->dest, p->name) != 0 ||
        _cups_strcasecmp(job->username, q->username) != 0)
      continue;

   /*
    * Make sure attributes are loaded; we always call cupsdLoadJob() to ensure
    * the access_time member is updated so the job isn't unloaded right away...
    */

    if (!cupsdLoadJob(job))
      continue;

    if ((attr = ippFindAttribute(job->attrs, "time-at-completion",
                                 IPP_TAG_INTEGER)) == NULL)
      if ((attr = ippFindAttribute(job->attrs, "time-at-processing",
                                   IPP_TAG_INTEGER)) == NULL)
        attr = ippFindAttribute(job->attrs, "time-at-creation",
                                IPP_TAG_INTEGER);

    if (attr->values[0].integer < curtime)
    {
     /*
      * This job is too old to count towards the quota, ignore it...
      */

      if (JobAutoPurge && !job->printer && job->state_value > IPP_JOB_STOPPED)
        cupsdDeleteJob(job, CUPSD_JOB_PURGE);

      continue;
    }

    if (q->next_update == 0)
      q->next_update = attr->values[0].integer + p->quota_period;

    if ((attr = ippFindAttribute(job->attrs, "job-media-sheets-completed",
                                 IPP_TAG_INTEGER)) != NULL)
      q->page_count += attr->values[0].integer;

    if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
                                 IPP_TAG_INTEGER)) != NULL)
      q->k_count += attr->values[0].integer;
  }

  return (q);
}


/*
 * 'add_quota()' - Add a quota record for this printer and user.
 */

static cupsd_quota_t *			/* O - Quota data */
add_quota(cupsd_printer_t *p,		/* I - Printer */
          const char      *username)	/* I - User */
{
  cupsd_quota_t	*q;			/* New quota data */
  char		*ptr;			/* Pointer into username */


  if (!p || !username)
    return (NULL);

  if (!p->quotas)
    p->quotas = cupsArrayNew((cups_array_func_t)compare_quotas, NULL);

  if (!p->quotas)
    return (NULL);

  if ((q = calloc(1, sizeof(cupsd_quota_t))) == NULL)
    return (NULL);

  strlcpy(q->username, username, sizeof(q->username));
  if ((ptr = strchr(q->username, '@')) != NULL)
    *ptr = '\0';			/* Strip @domain/@KDC */

  cupsArrayAdd(p->quotas, q);

  return (q);
}


/*
 * 'compare_quotas()' - Compare two quota records...
 */

static int				/* O - Result of comparison */
compare_quotas(const cupsd_quota_t *q1,	/* I - First quota record */
               const cupsd_quota_t *q2)	/* I - Second quota record */
{
  return (_cups_strcasecmp(q1->username, q2->username));
}


/*
 * End of "$Id: quotas.c 10996 2013-05-29 11:51:34Z msweet $".
 */
