/*
 * "$Id: policy.c 5838 2006-08-17 14:41:42Z mike $"
 *
 *   Policy routines for the Common UNIX Printing System (CUPS).
 *
 *   Copyright 1997-2006 by Easy Software Products, all rights reserved.
 *
 *   These coded instructions, statements, and computer programs are the
 *   property of Easy Software Products 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 missing or damaged please contact Easy Software Products
 *   at:
 *
 *       Attn: CUPS Licensing Information
 *       Easy Software Products
 *       44141 Airport View Drive, Suite 204
 *       Hollywood, Maryland 20636 USA
 *
 *       Voice: (301) 373-9600
 *       EMail: cups-info@cups.org
 *         WWW: http://www.cups.org
 *
 * Contents:
 *
 *   cupsdAddPolicy()         - Add a policy to the system.
 *   cupsdAddPolicyOp()       - Add an operation to a policy.
 *   cupsdCheckPolicy()       - Check the IPP operation and username against
 *                              a policy.
 *   cupsdDeleteAllPolicies() - Delete all policies in memory.
 *   cupsdFindPolicy()        - Find a named policy.
 *   cupsdFindPolicyOp()      - Find a policy operation.
 */

/*
 * Include necessary headers...
 */

#include "cupsd.h"


/*
 * 'AddPolicy()' - Add a policy to the system.
 */

cupsd_policy_t *			/* O - Policy */
cupsdAddPolicy(const char *policy)	/* I - Name of policy */
{
  cupsd_policy_t	*temp,		/* Pointer to policy */
			**tempa;	/* Pointer to policy array */


  if (policy == NULL)
    return (NULL);

  if (NumPolicies == 0)
    tempa = malloc(sizeof(cupsd_policy_t *));
  else
    tempa = realloc(Policies, sizeof(cupsd_policy_t *) * (NumPolicies + 1));

  if (tempa == NULL)
    return (NULL);

  Policies = tempa;
  tempa    += NumPolicies;

  if ((temp = calloc(1, sizeof(cupsd_policy_t))) != NULL)
  {
    temp->name = strdup(policy);
    *tempa     = temp;

    NumPolicies ++;
  }

  return (temp);
}


/*
 * 'cupsdAddPolicyOp()' - Add an operation to a policy.
 */

cupsd_location_t *			/* O - New policy operation */
cupsdAddPolicyOp(cupsd_policy_t   *p,	/* I - Policy */
                 cupsd_location_t *po,	/* I - Policy operation to copy */
                 ipp_op_t         op)	/* I - IPP operation code */
{
  int			i;		/* Looping var */
  cupsd_location_t	*temp,		/* New policy operation */
			**tempa;	/* New policy operation array */
  char			name[1024];	/* Interface name */


  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddPolicyOp(p=%p, po=%p, op=%x(%s))",
                  p, po, op, ippOpString(op));

  if (p == NULL)
    return (NULL);

  if (p->num_ops == 0)
    tempa = malloc(sizeof(cupsd_location_t *));
  else
    tempa = realloc(p->ops, sizeof(cupsd_location_t *) * (p->num_ops + 1));

  if (tempa == NULL)
    return (NULL);

  p->ops = tempa;

  if ((temp = calloc(1, sizeof(cupsd_location_t))) != NULL)
  {
    p->ops            = tempa;
    tempa[p->num_ops] = temp;
    p->num_ops ++;

    temp->op    = op;
    temp->limit = AUTH_LIMIT_IPP;

    if (po)
    {
     /*
      * Copy the specified policy to the new one...
      */

      temp->order_type = po->order_type;
      temp->type       = po->type;
      temp->level      = po->level;
      temp->satisfy    = po->satisfy;
      temp->encryption = po->encryption;

      for (i = 0; i < po->num_names; i ++)
        cupsdAddName(temp, po->names[i]);

      for (i = 0; i < po->num_allow; i ++)
        switch (po->allow[i].type)
	{
	  case AUTH_IP :
	      cupsdAllowIP(temp, po->allow[i].mask.ip.address,
	              po->allow[i].mask.ip.netmask);
	      break;

          case AUTH_INTERFACE :
	      snprintf(name, sizeof(name), "@IF(%s)",
	               po->allow[i].mask.name.name);
              cupsdAllowHost(temp, name);
	      break;

          default :
              cupsdAllowHost(temp, po->allow[i].mask.name.name);
	      break;
        }

      for (i = 0; i < po->num_deny; i ++)
        switch (po->deny[i].type)
	{
	  case AUTH_IP :
	      cupsdDenyIP(temp, po->deny[i].mask.ip.address,
	              po->deny[i].mask.ip.netmask);
	      break;

          case AUTH_INTERFACE :
	      snprintf(name, sizeof(name), "@IF(%s)",
	               po->deny[i].mask.name.name);
              cupsdDenyHost(temp, name);
	      break;

          default :
              cupsdDenyHost(temp, po->deny[i].mask.name.name);
	      break;
        }
    }
  }

  return (temp);
}


/*
 * 'cupsdCheckPolicy()' - Check the IPP operation and username against a policy.
 */

http_status_t				/* I - 1 if OK, 0 otherwise */
cupsdCheckPolicy(cupsd_policy_t *p,	/* I - Policy */
                 cupsd_client_t *con,	/* I - Client connection */
	         const char     *owner)	/* I - Owner of object */
{
  cupsd_location_t	*po;		/* Current policy operation */


 /*
  * Range check...
  */

  if (!p || !con)
  {
    cupsdLogMessage(CUPSD_LOG_CRIT, "cupsdCheckPolicy: p=%p, con=%p!", p, con);

    return ((http_status_t)0);
  }

 /*
  * Find a match for the operation...
  */

  if ((po = cupsdFindPolicyOp(p, con->request->request.op.operation_id)) == NULL)
  {
    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCheckPolicy: No matching operation, returning 0!");
    return ((http_status_t)0);
  }

  con->best = po;

 /*
  * Return the status of the check...
  */

  return (cupsdIsAuthorized(con, owner));
}


/*
 * 'cupsdDeleteAllPolicies()' - Delete all policies in memory.
 */

void
cupsdDeleteAllPolicies(void)
{
  int			i, j;		/* Looping vars */
  cupsd_policy_t	**p;		/* Current policy */
  cupsd_location_t	**po;		/* Current policy op */


  if (NumPolicies == 0)
    return;

  for (i = NumPolicies, p = Policies; i > 0; i --, p ++)
  {
    for (j = (*p)->num_ops, po = (*p)->ops; j > 0; j --, po ++)
      cupsdDeleteLocation(*po);

    if ((*p)->num_ops > 0)
      free((*p)->ops);

    free(*p);
  }

  free(Policies);

  NumPolicies = 0;
  Policies    = NULL;
}


/*
 * 'cupsdFindPolicy()' - Find a named policy.
 */

cupsd_policy_t *			/* O - Policy */
cupsdFindPolicy(const char *policy)	/* I - Name of policy */
{
  int			i;		/* Looping var */
  cupsd_policy_t	**p;		/* Current policy */


 /*
  * Range check...
  */

  if (policy == NULL)
    return (NULL);

 /*
  * Check the operation against the available policies...
  */

  for (i = NumPolicies, p = Policies; i > 0; i --, p ++)
    if (!strcasecmp(policy, (*p)->name))
      return (*p);

  return (NULL);
}


/*
 * 'cupsdFindPolicyOp()' - Find a policy operation.
 */

cupsd_location_t *			/* O - Policy operation */
cupsdFindPolicyOp(cupsd_policy_t *p,	/* I - Policy */
                  ipp_op_t       op)	/* I - IPP operation */
{
  int			i;		/* Looping var */
  cupsd_location_t	**po;		/* Current policy operation */


  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindPolicyOp(p=%p, op=%x(%s))\n",
                  p, op, ippOpString(op));

 /*
  * Range check...
  */

  if (p == NULL)
    return (NULL);

 /*
  * Check the operation against the available policies...
  */

  for (i = p->num_ops, po = p->ops; i > 0; i --, po ++)
    if ((*po)->op == op)
    {
      cupsdLogMessage(CUPSD_LOG_DEBUG2,
                      "cupsdFindPolicyOp: Found exact match...");
      return (*po);
    }

  for (i = p->num_ops, po = p->ops; i > 0; i --, po ++)
    if ((*po)->op == IPP_ANY_OPERATION)
    {
      cupsdLogMessage(CUPSD_LOG_DEBUG2,
                      "cupsdFindPolicyOp: Found wildcard match...");
      return (*po);
    }

  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindPolicyOp: No match found!");

  return (NULL);
}


/*
 * End of "$Id: policy.c 5838 2006-08-17 14:41:42Z mike $".
 */
