Commit message: DBUS notifications could crash the scheduler (Issue #5143)

- scheduler/ipp.c: Make sure requesting-user-name string is valid UTF-8.
CVE: CVE-2017-18248
CWE: CWE-20
Vulnerability description: The add_job function in scheduler/ipp.c in CUPS before 2.2.6, when D-Bus support is enabled, can be crashed by remote attackers by sending print jobs with an invalid username, related to a D-Bus notification.

Project: cups
File: ipp.c

accept_jobs(cupsd_client_t  *con,	/* I - Client connection */
            ipp_attribute_t *uri)	/* I - Printer or class URI */
{
  http_status_t	status;			/* Policy status */
  cups_ptype_t	dtype;			/* Destination type (printer/class) */
  cupsd_printer_t *printer;		/* Printer data */


  cupsdLogMessage(CUPSD_LOG_DEBUG2, "accept_jobs(%p[%d], %s)", con,
                  con->number, uri->values[0].string.text);

 /*
  * Is the destination valid?
  */

  if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
  {
   /*
    * Bad URI...
    */

    send_ipp_status(con, IPP_NOT_FOUND,
                    _("The printer or class does not exist."));
    return;
  }

 /*
  * Check policy...
  */

  if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
  {
    send_http_error(con, status, printer);
    return;
  }

 /*
  * Accept jobs sent to the printer...
  */

  printer->accepting        = 1;
  printer->state_message[0] = '\0';

  cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
                "Now accepting jobs.");

  if (dtype & CUPS_PRINTER_CLASS)
  {
    cupsdMarkDirty(CUPSD_DIRTY_CLASSES);

    cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" now accepting jobs (\"%s\").",
                    printer->name, get_username(con));
  }
  else
  {
    cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);

    cupsdLogMessage(CUPSD_LOG_INFO,
                    "Printer \"%s\" now accepting jobs (\"%s\").",
                    printer->name, get_username(con));
  }

 /*
  * Everything was ok, so return OK status...
  */

  con->response->request.status.status_code = IPP_OK;
}

Project: cups
File: ipp.c

add_class(cupsd_client_t  *con,		/* I - Client connection */
          ipp_attribute_t *uri)		/* I - URI of class */
{
  http_status_t	status;			/* Policy status */
  int		i;			/* Looping var */
  char		scheme[HTTP_MAX_URI],	/* Method portion of URI */
		username[HTTP_MAX_URI],	/* Username portion of URI */
		host[HTTP_MAX_URI],	/* Host portion of URI */
		resource[HTTP_MAX_URI];	/* Resource portion of URI */
  int		port;			/* Port portion of URI */
  cupsd_printer_t *pclass,		/* Class */
		*member;		/* Member printer/class */
  cups_ptype_t	dtype;			/* Destination type */
  ipp_attribute_t *attr;		/* Printer attribute */
  int		modify;			/* Non-zero if we just modified */
  int		need_restart_job;	/* Need to restart job? */


  cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_class(%p[%d], %s)", con,
                  con->number, uri->values[0].string.text);

 /*
  * Do we have a valid URI?
  */

  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
                  sizeof(scheme), username, sizeof(username), host,
		  sizeof(host), &port, resource, sizeof(resource));


  if (strncmp(resource, "/classes/", 9) || strlen(resource) == 9)
  {
   /*
    * No, return an error...
    */

    send_ipp_status(con, IPP_BAD_REQUEST,
                    _("The printer-uri must be of the form "
		      "\"ipp://HOSTNAME/classes/CLASSNAME\"."));
    return;
  }

 /*
  * Do we have a valid printer name?
  */

  if (!validate_name(resource + 9))
  {
   /*
    * No, return an error...
    */

    send_ipp_status(con, IPP_BAD_REQUEST,
                    _("The printer-uri \"%s\" contains invalid characters."),
		    uri->values[0].string.text);
    return;
  }

 /*
  * See if the class already exists; if not, create a new class...
  */

  if ((pclass = cupsdFindClass(resource + 9)) == NULL)
  {
   /*
    * Class doesn't exist; see if we have a printer of the same name...
    */

    if ((pclass = cupsdFindPrinter(resource + 9)) != NULL)
    {
     /*
      * Yes, return an error...
      */

      send_ipp_status(con, IPP_NOT_POSSIBLE,
                      _("A printer named \"%s\" already exists."),
		      resource + 9);
      return;
    }

   /*
    * No, check the default policy and then add the class...
    */

    if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
    {
      send_http_error(con, status, NULL);
      return;
    }

    pclass = cupsdAddClass(resource + 9);
    modify = 0;
  }
  else if ((status = cupsdCheckPolicy(pclass->op_policy_ptr, con,
                                      NULL)) != HTTP_OK)
  {
    send_http_error(con, status, pclass);
    return;
  }
  else
    modify = 1;

 /*
  * Look for attributes and copy them over as needed...
  */

  need_restart_job = 0;

  if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
    cupsdSetString(&pclass->location, attr->values[0].string.text);

  if ((attr = ippFindAttribute(con->request, "printer-geo-location", IPP_TAG_URI)) != NULL && !strncmp(attr->values[0].string.text, "geo:", 4))
    cupsdSetString(&pclass->geo_location, attr->values[0].string.text);

  if ((attr = ippFindAttribute(con->request, "printer-organization", IPP_TAG_TEXT)) != NULL)
    cupsdSetString(&pclass->organization, attr->values[0].string.text);

  if ((attr = ippFindAttribute(con->request, "printer-organizational-unit", IPP_TAG_TEXT)) != NULL)
    cupsdSetString(&pclass->organizational_unit, attr->values[0].string.text);

  if ((attr = ippFindAttribute(con->request, "printer-info",
                               IPP_TAG_TEXT)) != NULL)
    cupsdSetString(&pclass->info, attr->values[0].string.text);

  if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
                               IPP_TAG_BOOLEAN)) != NULL &&
      attr->values[0].boolean != pclass->accepting)
  {
    cupsdLogMessage(CUPSD_LOG_INFO,
                    "Setting %s printer-is-accepting-jobs to %d (was %d.)",
                    pclass->name, attr->values[0].boolean, pclass->accepting);

    pclass->accepting = attr->values[0].boolean;

    cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, pclass, NULL, "%s accepting jobs.",
		  pclass->accepting ? "Now" : "No longer");
  }

  if ((attr = ippFindAttribute(con->request, "printer-is-shared", IPP_TAG_BOOLEAN)) != NULL)
  {
    if (pclass->type & CUPS_PRINTER_REMOTE)
    {
     /*
      * Cannot re-share remote printers.
      */

      send_ipp_status(con, IPP_BAD_REQUEST, _("Cannot change printer-is-shared for remote queues."));
      if (!modify)
	cupsdDeletePrinter(pclass, 0);

      return;
    }

    if (pclass->shared && !ippGetBoolean(attr, 0))
      cupsdDeregisterPrinter(pclass, 1);

    cupsdLogMessage(CUPSD_LOG_INFO,
                    "Setting %s printer-is-shared to %d (was %d.)",
                    pclass->name, attr->values[0].boolean, pclass->shared);

    pclass->shared = ippGetBoolean(attr, 0);
  }

  if ((attr = ippFindAttribute(con->request, "printer-state",
                               IPP_TAG_ENUM)) != NULL)
  {
    if (attr->values[0].integer != IPP_PRINTER_IDLE &&
        attr->values[0].integer != IPP_PRINTER_STOPPED)
    {
      send_ipp_status(con, IPP_BAD_REQUEST,
                      _("Attempt to set %s printer-state to bad value %d."),
                      pclass->name, attr->values[0].integer);
      if (!modify)
	cupsdDeletePrinter(pclass, 0);

      return;
    }

    cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)",
                    pclass->name, attr->values[0].integer, pclass->state);

    if (attr->values[0].integer == IPP_PRINTER_STOPPED)
      cupsdStopPrinter(pclass, 0);
    else
    {
      cupsdSetPrinterState(pclass, (ipp_pstate_t)(attr->values[0].integer), 0);
      need_restart_job = 1;
    }
  }
  if ((attr = ippFindAttribute(con->request, "printer-state-message",
                               IPP_TAG_TEXT)) != NULL)
  {
    strlcpy(pclass->state_message, attr->values[0].string.text,
            sizeof(pclass->state_message));

    cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, pclass, NULL, "%s",
                  pclass->state_message);
  }
  if ((attr = ippFindAttribute(con->request, "member-uris",
                               IPP_TAG_URI)) != NULL)
  {
   /*
    * Clear the printer array as needed...
    */

    need_restart_job = 1;

    if (pclass->num_printers > 0)
    {
      free(pclass->printers);
      pclass->num_printers = 0;
    }

   /*
    * Add each printer or class that is listed...
    */

    for (i = 0; i < attr->num_values; i ++)
    {
     /*
      * Search for the printer or class URI...
      */

      if (!cupsdValidateDest(attr->values[i].string.text, &dtype, &member))
      {
       /*
	* Bad URI...
	*/

	send_ipp_status(con, IPP_NOT_FOUND,
                	_("The printer or class does not exist."));
	if (!modify)
	  cupsdDeletePrinter(pclass, 0);

	return;
      }
      else if (dtype & CUPS_PRINTER_CLASS)
      {
        send_ipp_status(con, IPP_BAD_REQUEST,
			_("Nested classes are not allowed."));
	if (!modify)
	  cupsdDeletePrinter(pclass, 0);

        return;
      }

     /*
      * Add it to the class...
      */

      cupsdAddPrinterToClass(pclass, member);
    }
  }

  if (!set_printer_defaults(con, pclass))
  {
    if (!modify)
      cupsdDeletePrinter(pclass, 0);

    return;
  }

  if ((attr = ippFindAttribute(con->request, "auth-info-required",
                               IPP_TAG_KEYWORD)) != NULL)
    cupsdSetAuthInfoRequired(pclass, NULL, attr);

  pclass->config_time = time(NULL);

 /*
  * Update the printer class attributes and return...
  */

  cupsdSetPrinterAttrs(pclass);
  cupsdMarkDirty(CUPSD_DIRTY_CLASSES);

  if (need_restart_job && pclass->job)
  {
   /*
    * Reset the current job to a "pending" status...
    */

    cupsdSetJobState(pclass->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
                     "Job restarted because the class was modified.");
  }

  cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);

  if (modify)
  {
    cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED,
		  pclass, NULL, "Class \"%s\" modified by \"%s\".",
		  pclass->name, get_username(con));

    cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" modified by \"%s\".",
                    pclass->name, get_username(con));
  }
  else
  {
    cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
		  pclass, NULL, "New class \"%s\" added by \"%s\".",
		  pclass->name, get_username(con));

    cupsdLogMessage(CUPSD_LOG_INFO, "New class \"%s\" added by \"%s\".",
                    pclass->name, get_username(con));
  }

  con->response->request.status.status_code = IPP_OK;
}

Project: cups
File: ipp.c

add_file(cupsd_client_t *con,		/* I - Connection to client */
         cupsd_job_t    *job,		/* I - Job to add to */
         mime_type_t    *filetype,	/* I - Type of file */
	 int            compression)	/* I - Compression */
{
  mime_type_t	**filetypes;		/* New filetypes array... */
  int		*compressions;		/* New compressions array... */


  cupsdLogMessage(CUPSD_LOG_DEBUG2,
        	  "add_file(con=%p[%d], job=%d, filetype=%s/%s, "
		  "compression=%d)", con, con ? con->number : -1, job->id,
		  filetype->super, filetype->type, compression);

 /*
  * Add the file to the job...
  */

  if (job->num_files == 0)
  {
    compressions = (int *)malloc(sizeof(int));
    filetypes    = (mime_type_t **)malloc(sizeof(mime_type_t *));
  }
  else
  {
    compressions = (int *)realloc(job->compressions,
                                  (size_t)(job->num_files + 1) * sizeof(int));
    filetypes    = (mime_type_t **)realloc(job->filetypes,
                                           (size_t)(job->num_files + 1) *
					   sizeof(mime_type_t *));
  }

  if (compressions)
    job->compressions = compressions;

  if (filetypes)
    job->filetypes = filetypes;

  if (!compressions || !filetypes)
  {
    cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
                     "Job aborted because the scheduler ran out of memory.");

    if (con)
      send_ipp_status(con, IPP_INTERNAL_ERROR,
		      _("Unable to allocate memory for file types."));

    return (-1);
  }

  job->compressions[job->num_files] = compression;
  job->filetypes[job->num_files]    = filetype;

  job->num_files ++;

  job->dirty = 1;
  cupsdMarkDirty(CUPSD_DIRTY_JOBS);

  return (0);
}

Project: cups
File: ipp.c

add_job_subscriptions(
    cupsd_client_t *con,		/* I - Client connection */
    cupsd_job_t    *job)		/* I - Newly created job */
{
  int			i;		/* Looping var */
  ipp_attribute_t	*prev,		/* Previous attribute */
			*next,		/* Next attribute */
			*attr;		/* Current attribute */
  cupsd_subscription_t	*sub;		/* Subscription object */
  const char		*recipient,	/* notify-recipient-uri */
			*pullmethod;	/* notify-pull-method */
  ipp_attribute_t	*user_data;	/* notify-user-data */
  int			interval;	/* notify-time-interval */
  unsigned		mask;		/* notify-events */


 /*
  * Find the first subscription group attribute; return if we have
  * none...
  */

  for (attr = job->attrs->attrs; attr; attr = attr->next)
    if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
      break;

  if (!attr)
    return;

 /*
  * Process the subscription attributes in the request...
  */

  while (attr)
  {
    recipient = NULL;
    pullmethod = NULL;
    user_data  = NULL;
    interval   = 0;
    mask       = CUPSD_EVENT_NONE;

    while (attr && attr->group_tag != IPP_TAG_ZERO)
    {
      if (!strcmp(attr->name, "notify-recipient-uri") &&
          attr->value_tag == IPP_TAG_URI)
      {
       /*
        * Validate the recipient scheme against the ServerBin/notifier
	* directory...
	*/

	char	notifier[1024],		/* Notifier filename */
		scheme[HTTP_MAX_URI],	/* Scheme portion of URI */
		userpass[HTTP_MAX_URI],	/* Username portion of URI */
		host[HTTP_MAX_URI],	/* Host portion of URI */
		resource[HTTP_MAX_URI];	/* Resource portion of URI */
        int	port;			/* Port portion of URI */


        recipient = attr->values[0].string.text;

	if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
	                    scheme, sizeof(scheme), userpass, sizeof(userpass),
			    host, sizeof(host), &port,
			    resource, sizeof(resource)) < HTTP_URI_OK)
        {
          send_ipp_status(con, IPP_NOT_POSSIBLE,
	                  _("Bad notify-recipient-uri \"%s\"."), recipient);
	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
	                "notify-status-code", IPP_URI_SCHEME);
	  return;
	}

        snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
	         scheme);
        if (access(notifier, X_OK))
	{
          send_ipp_status(con, IPP_NOT_POSSIBLE,
	                  _("notify-recipient-uri URI \"%s\" uses unknown "
			    "scheme."), recipient);
	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
	                "notify-status-code", IPP_URI_SCHEME);
	  return;
	}

        if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
	{
          send_ipp_status(con, IPP_NOT_POSSIBLE,
	                  _("notify-recipient-uri URI \"%s\" is already used."),
			  recipient);
	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
	                "notify-status-code", IPP_ATTRIBUTES);
	  return;
	}
      }
      else if (!strcmp(attr->name, "notify-pull-method") &&
               attr->value_tag == IPP_TAG_KEYWORD)
      {
        pullmethod = attr->values[0].string.text;

        if (strcmp(pullmethod, "ippget"))
	{
          send_ipp_status(con, IPP_NOT_POSSIBLE,
	                  _("Bad notify-pull-method \"%s\"."), pullmethod);
	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
	                "notify-status-code", IPP_ATTRIBUTES);
	  return;
	}
      }
      else if (!strcmp(attr->name, "notify-charset") &&
               attr->value_tag == IPP_TAG_CHARSET &&
	       strcmp(attr->values[0].string.text, "us-ascii") &&
	       strcmp(attr->values[0].string.text, "utf-8"))
      {
        send_ipp_status(con, IPP_CHARSET,
	                _("Character set \"%s\" not supported."),
			attr->values[0].string.text);
	return;
      }
      else if (!strcmp(attr->name, "notify-natural-language") &&
               (attr->value_tag != IPP_TAG_LANGUAGE ||
	        strcmp(attr->values[0].string.text, DefaultLanguage)))
      {
        send_ipp_status(con, IPP_CHARSET,
	                _("Language \"%s\" not supported."),
			attr->values[0].string.text);
	return;
      }
      else if (!strcmp(attr->name, "notify-user-data") &&
               attr->value_tag == IPP_TAG_STRING)
      {
        if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
	{
          send_ipp_status(con, IPP_REQUEST_VALUE,
	                  _("The notify-user-data value is too large "
			    "(%d > 63 octets)."),
			  attr->values[0].unknown.length);
	  return;
	}

        user_data = attr;
      }
      else if (!strcmp(attr->name, "notify-events") &&
               attr->value_tag == IPP_TAG_KEYWORD)
      {
        for (i = 0; i < attr->num_values; i ++)
	  mask |= cupsdEventValue(attr->values[i].string.text);
      }
      else if (!strcmp(attr->name, "notify-lease-duration"))
      {
        send_ipp_status(con, IPP_BAD_REQUEST,
	                _("The notify-lease-duration attribute cannot be "
			  "used with job subscriptions."));
	return;
      }
      else if (!strcmp(attr->name, "notify-time-interval") &&
               attr->value_tag == IPP_TAG_INTEGER)
        interval = attr->values[0].integer;

      attr = attr->next;
    }

    if (!recipient && !pullmethod)
      break;

    if (mask == CUPSD_EVENT_NONE)
      mask = CUPSD_EVENT_JOB_COMPLETED;

    if ((sub = cupsdAddSubscription(mask, cupsdFindDest(job->dest), job,
                                    recipient, 0)) != NULL)
    {
      sub->interval = interval;

      cupsdSetString(&sub->owner, job->username);

      if (user_data)
      {
	sub->user_data_len = user_data->values[0].unknown.length;
	memcpy(sub->user_data, user_data->values[0].unknown.data,
	       (size_t)sub->user_data_len);
      }

      ippAddSeparator(con->response);
      ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
		    "notify-subscription-id", sub->id);

      cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
                      sub->id, job->id);
    }

    if (attr)
      attr = attr->next;
  }

  cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);

 /*
  * Remove all of the subscription attributes from the job request...
  *
  * TODO: Optimize this since subscription groups have to come at the
  * end of the request...
  */

  for (attr = job->attrs->attrs, prev = NULL; attr; attr = next)
  {
    next = attr->next;

    if (attr->group_tag == IPP_TAG_SUBSCRIPTION ||
        attr->group_tag == IPP_TAG_ZERO)
    {
     /*
      * Free and remove this attribute...
      */

      ippDeleteAttribute(NULL, attr);

      if (prev)
        prev->next = next;
      else
        job->attrs->attrs = next;
    }
    else
      prev = attr;
  }

  job->attrs->last    = prev;
  job->attrs->current = prev;
}

Project: cups
File: ipp.c

add_job_uuid(cupsd_job_t *job)		/* I - Job */
{
  char			uuid[64];	/* job-uuid string */


 /*
  * Add a job-uuid attribute if none exists...
  */

  if (!ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI))
    ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL,
		 httpAssembleUUID(ServerName, RemotePort, job->dest, job->id,
		                  uuid, sizeof(uuid)));
}

Project: cups
File: ipp.c

add_printer(cupsd_client_t  *con,	/* I - Client connection */
            ipp_attribute_t *uri)	/* I - URI of printer */
{
  http_status_t	status;			/* Policy status */
  int		i;			/* Looping var */
  char		scheme[HTTP_MAX_URI],	/* Method portion of URI */
		username[HTTP_MAX_URI],	/* Username portion of URI */
		host[HTTP_MAX_URI],	/* Host portion of URI */
		resource[HTTP_MAX_URI];	/* Resource portion of URI */
  int		port;			/* Port portion of URI */
  cupsd_printer_t *printer;		/* Printer/class */
  ipp_attribute_t *attr;		/* Printer attribute */
  cups_file_t	*fp;			/* Script/PPD file */
  char		line[1024];		/* Line from file... */
  char		srcfile[1024],		/* Source Script/PPD file */
		dstfile[1024];		/* Destination Script/PPD file */
  int		modify;			/* Non-zero if we are modifying */
  int		changed_driver,		/* Changed the PPD? */
		need_restart_job,	/* Need to restart job? */
		set_device_uri,		/* Did we set the device URI? */
		set_port_monitor;	/* Did we set the port monitor? */


  cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer(%p[%d], %s)", con,
                  con->number, uri->values[0].string.text);

 /*
  * Do we have a valid URI?
  */

  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
                  sizeof(scheme), username, sizeof(username), host,
		  sizeof(host), &port, resource, sizeof(resource));

  if (strncmp(resource, "/printers/", 10) || strlen(resource) == 10)
  {
   /*
    * No, return an error...
    */

    send_ipp_status(con, IPP_BAD_REQUEST,
                    _("The printer-uri must be of the form "
		      "\"ipp://HOSTNAME/printers/PRINTERNAME\"."));
    return;
  }

 /*
  * Do we have a valid printer name?
  */

  if (!validate_name(resource + 10))
  {
   /*
    * No, return an error...
    */

    send_ipp_status(con, IPP_BAD_REQUEST,
                    _("The printer-uri \"%s\" contains invalid characters."),
		    uri->values[0].string.text);
    return;
  }

 /*
  * See if the printer already exists; if not, create a new printer...
  */

  if ((printer = cupsdFindPrinter(resource + 10)) == NULL)
  {
   /*
    * Printer doesn't exist; see if we have a class of the same name...
    */

    if ((printer = cupsdFindClass(resource + 10)) != NULL)
    {
     /*
      * Yes, return an error...
      */

      send_ipp_status(con, IPP_NOT_POSSIBLE,
                      _("A class named \"%s\" already exists."),
        	      resource + 10);
      return;
    }

   /*
    * No, check the default policy then add the printer...
    */

    if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
    {
      send_http_error(con, status, NULL);
      return;
    }

    printer = cupsdAddPrinter(resource + 10);
    modify  = 0;
  }
  else if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
                                      NULL)) != HTTP_OK)
  {
    send_http_error(con, status, printer);
    return;
  }
  else
    modify = 1;

 /*
  * Look for attributes and copy them over as needed...
  */

  changed_driver   = 0;
  need_restart_job = 0;

  if ((attr = ippFindAttribute(con->request, "printer-is-temporary", IPP_TAG_BOOLEAN)) != NULL)
    printer->temporary = ippGetBoolean(attr, 0);

  if ((attr = ippFindAttribute(con->request, "printer-location",
                               IPP_TAG_TEXT)) != NULL)
    cupsdSetString(&printer->location, attr->values[0].string.text);

  if ((attr = ippFindAttribute(con->request, "printer-geo-location", IPP_TAG_URI)) != NULL && !strncmp(attr->values[0].string.text, "geo:", 4))
    cupsdSetString(&printer->geo_location, attr->values[0].string.text);

  if ((attr = ippFindAttribute(con->request, "printer-organization", IPP_TAG_TEXT)) != NULL)
    cupsdSetString(&printer->organization, attr->values[0].string.text);

  if ((attr = ippFindAttribute(con->request, "printer-organizational-unit", IPP_TAG_TEXT)) != NULL)
    cupsdSetString(&printer->organizational_unit, attr->values[0].string.text);

  if ((attr = ippFindAttribute(con->request, "printer-info",
                               IPP_TAG_TEXT)) != NULL)
    cupsdSetString(&printer->info, attr->values[0].string.text);

  set_device_uri = 0;

  if ((attr = ippFindAttribute(con->request, "device-uri",
                               IPP_TAG_URI)) != NULL)
  {
   /*
    * Do we have a valid device URI?
    */

    http_uri_status_t	uri_status;	/* URI separation status */
    char		old_device_uri[1024];
					/* Old device URI */

    need_restart_job = 1;

    uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
				 attr->values[0].string.text,
				 scheme, sizeof(scheme),
				 username, sizeof(username),
				 host, sizeof(host), &port,
				 resource, sizeof(resource));

    cupsdLogMessage(CUPSD_LOG_DEBUG, "%s device-uri: %s", printer->name, httpURIStatusString(uri_status));

    if (uri_status < HTTP_URI_OK)
    {
      send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri \"%s\"."),
		      attr->values[0].string.text);
      if (!modify)
        cupsdDeletePrinter(printer, 0);

      return;
    }

    if (!strcmp(scheme, "file"))
    {
     /*
      * See if the administrator has enabled file devices...
      */

      if (!FileDevice && strcmp(resource, "/dev/null"))
      {
       /*
        * File devices are disabled and the URL is not file:/dev/null...
	*/

	send_ipp_status(con, IPP_NOT_POSSIBLE,
	                _("File device URIs have been disabled. "
	                  "To enable, see the FileDevice directive in "
			  "\"%s/cups-files.conf\"."),
			ServerRoot);
	if (!modify)
	  cupsdDeletePrinter(printer, 0);

	return;
      }
    }
    else
    {
     /*
      * See if the backend exists and is executable...
      */

      snprintf(srcfile, sizeof(srcfile), "%s/backend/%s", ServerBin, scheme);
      if (access(srcfile, X_OK))
      {
       /*
        * Could not find device in list!
	*/

	send_ipp_status(con, IPP_NOT_POSSIBLE,
                        _("Bad device-uri scheme \"%s\"."), scheme);
	if (!modify)
	  cupsdDeletePrinter(printer, 0);

	return;
      }
    }

    if (printer->sanitized_device_uri)
      strlcpy(old_device_uri, printer->sanitized_device_uri,
              sizeof(old_device_uri));
    else
      old_device_uri[0] = '\0';

    cupsdSetDeviceURI(printer, attr->values[0].string.text);

    cupsdLogMessage(CUPSD_LOG_INFO,
                    "Setting %s device-uri to \"%s\" (was \"%s\".)",
        	    printer->name, printer->sanitized_device_uri,
		    old_device_uri);

    set_device_uri = 1;
  }

  set_port_monitor = 0;

  if ((attr = ippFindAttribute(con->request, "port-monitor",
                               IPP_TAG_NAME)) != NULL)
  {
    ipp_attribute_t	*supported;	/* port-monitor-supported attribute */


    need_restart_job = 1;

    supported = ippFindAttribute(printer->ppd_attrs, "port-monitor-supported",
                                 IPP_TAG_NAME);
    if (supported)
    {
      for (i = 0; i < supported->num_values; i ++)
        if (!strcmp(supported->values[i].string.text,
                    attr->values[0].string.text))
          break;
    }

    if (!supported || i >= supported->num_values)
    {
      send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad port-monitor \"%s\"."),
        	      attr->values[0].string.text);
      if (!modify)
        cupsdDeletePrinter(printer, 0);

      return;
    }

    cupsdLogMessage(CUPSD_LOG_INFO,
                    "Setting %s port-monitor to \"%s\" (was \"%s\".)",
                    printer->name, attr->values[0].string.text,
	            printer->port_monitor ? printer->port_monitor : "none");

    if (strcmp(attr->values[0].string.text, "none"))
      cupsdSetString(&printer->port_monitor, attr->values[0].string.text);
    else
      cupsdClearString(&printer->port_monitor);

    set_port_monitor = 1;
  }

  if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
                               IPP_TAG_BOOLEAN)) != NULL &&
      attr->values[0].boolean != printer->accepting)
  {
    cupsdLogMessage(CUPSD_LOG_INFO,
                    "Setting %s printer-is-accepting-jobs to %d (was %d.)",
                    printer->name, attr->values[0].boolean, printer->accepting);

    printer->accepting = attr->values[0].boolean;

    cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
                  "%s accepting jobs.",
		  printer->accepting ? "Now" : "No longer");
  }

  if ((attr = ippFindAttribute(con->request, "printer-is-shared", IPP_TAG_BOOLEAN)) != NULL)
  {
    if (ippGetBoolean(attr, 0) &&
        printer->num_auth_info_required == 1 &&
	!strcmp(printer->auth_info_required[0], "negotiate"))
    {
      send_ipp_status(con, IPP_BAD_REQUEST,
                      _("Cannot share a remote Kerberized printer."));
      if (!modify)
        cupsdDeletePrinter(printer, 0);

      return;
    }

    if (printer->type & CUPS_PRINTER_REMOTE)
    {
     /*
      * Cannot re-share remote printers.
      */

      send_ipp_status(con, IPP_BAD_REQUEST, _("Cannot change printer-is-shared for remote queues."));
      if (!modify)
        cupsdDeletePrinter(printer, 0);

      return;
    }

    if (printer->shared && !ippGetBoolean(attr, 0))
      cupsdDeregisterPrinter(printer, 1);

    cupsdLogMessage(CUPSD_LOG_INFO,
                    "Setting %s printer-is-shared to %d (was %d.)",
                    printer->name, attr->values[0].boolean, printer->shared);

    printer->shared = ippGetBoolean(attr, 0);
    if (printer->shared && printer->temporary)
      printer->temporary = 0;
  }

  if ((attr = ippFindAttribute(con->request, "printer-state",
                               IPP_TAG_ENUM)) != NULL)
  {
    if (attr->values[0].integer != IPP_PRINTER_IDLE &&
        attr->values[0].integer != IPP_PRINTER_STOPPED)
    {
      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad printer-state value %d."),
                      attr->values[0].integer);
      if (!modify)
        cupsdDeletePrinter(printer, 0);

      return;
    }

    cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)",
                    printer->name, attr->values[0].integer, printer->state);

    if (attr->values[0].integer == IPP_PRINTER_STOPPED)
      cupsdStopPrinter(printer, 0);
    else
    {
      need_restart_job = 1;
      cupsdSetPrinterState(printer, (ipp_pstate_t)(attr->values[0].integer), 0);
    }
  }

  if ((attr = ippFindAttribute(con->request, "printer-state-message",
                               IPP_TAG_TEXT)) != NULL)
  {
    strlcpy(printer->state_message, attr->values[0].string.text,
            sizeof(printer->state_message));

    cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL, "%s",
                  printer->state_message);
  }

  if ((attr = ippFindAttribute(con->request, "printer-state-reasons",
                               IPP_TAG_KEYWORD)) != NULL)
  {
    if (attr->num_values >
            (int)(sizeof(printer->reasons) / sizeof(printer->reasons[0])))
    {
      send_ipp_status(con, IPP_NOT_POSSIBLE,
                      _("Too many printer-state-reasons values (%d > %d)."),
		      attr->num_values,
		      (int)(sizeof(printer->reasons) /
		            sizeof(printer->reasons[0])));
      if (!modify)
        cupsdDeletePrinter(printer, 0);

      return;
    }

    for (i = 0; i < printer->num_reasons; i ++)
      _cupsStrFree(printer->reasons[i]);

    printer->num_reasons = 0;
    for (i = 0; i < attr->num_values; i ++)
    {
      if (!strcmp(attr->values[i].string.text, "none"))
        continue;

      printer->reasons[printer->num_reasons] =
          _cupsStrRetain(attr->values[i].string.text);
      printer->num_reasons ++;

      if (!strcmp(attr->values[i].string.text, "paused") &&
          printer->state != IPP_PRINTER_STOPPED)
      {
	cupsdLogMessage(CUPSD_LOG_INFO,
	                "Setting %s printer-state to %d (was %d.)",
			printer->name, IPP_PRINTER_STOPPED, printer->state);
	cupsdStopPrinter(printer, 0);
      }
    }

    if (PrintcapFormat == PRINTCAP_PLIST)
      cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);

    cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
                  "Printer \"%s\" state changed.", printer->name);
  }

  if (!set_printer_defaults(con, printer))
  {
    if (!modify)
      cupsdDeletePrinter(printer, 0);

    return;
  }

  if ((attr = ippFindAttribute(con->request, "auth-info-required",
                               IPP_TAG_KEYWORD)) != NULL)
    cupsdSetAuthInfoRequired(printer, NULL, attr);

 /*
  * See if we have all required attributes...
  */

  if (!printer->device_uri)
    cupsdSetString(&printer->device_uri, "file:///dev/null");

 /*
  * See if we have a PPD file attached to the request...
  */

  if (con->filename)
  {
    need_restart_job = 1;
    changed_driver   = 1;

    strlcpy(srcfile, con->filename, sizeof(srcfile));

    if ((fp = cupsFileOpen(srcfile, "rb")))
    {
     /*
      * Yes; get the first line from it...
      */

      line[0] = '\0';
      cupsFileGets(fp, line, sizeof(line));
      cupsFileClose(fp);

     /*
      * Then see what kind of file it is...
      */

      if (strncmp(line, "*PPD-Adobe", 10))
      {
	send_ipp_status(con, IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED, _("Bad PPD file."));
	if (!modify)
	  cupsdDeletePrinter(printer, 0);

	return;
      }

      snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
               printer->name);

     /*
      * The new file is a PPD file, so move the file over to the ppd
      * directory...
      */

      if (copy_file(srcfile, dstfile, ConfigFilePerm))
      {
	send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file - %s"), strerror(errno));
	if (!modify)
	  cupsdDeletePrinter(printer, 0);

	return;
      }

      cupsdLogMessage(CUPSD_LOG_DEBUG, "Copied PPD file successfully");
    }
  }
  else if ((attr = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME)) != NULL)
  {
    const char *ppd_name = ippGetString(attr, 0, NULL);
					/* ppd-name value */

    need_restart_job = 1;
    changed_driver   = 1;

    if (!strcmp(ppd_name, "raw"))
    {
     /*
      * Raw driver, remove any existing PPD file.
      */

      snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot, printer->name);
      unlink(dstfile);
    }
    else if (strstr(ppd_name, "../"))
    {
      send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Invalid ppd-name value."));
      if (!modify)
	cupsdDeletePrinter(printer, 0);

      return;
    }
    else
    {
     /*
      * PPD model file...
      */

      snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot, printer->name);

      if (copy_model(con, ppd_name, dstfile))
      {
        send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file."));
	if (!modify)
	  cupsdDeletePrinter(printer, 0);

	return;
      }

      cupsdLogMessage(CUPSD_LOG_DEBUG, "Copied PPD file successfully");
    }
  }

  if (changed_driver)
  {
   /*
    * If we changed the PPD, then remove the printer's cache file and clear the
    * printer-state-reasons...
    */

    char cache_name[1024];		/* Cache filename for printer attrs */

    snprintf(cache_name, sizeof(cache_name), "%s/%s.data", CacheDir, printer->name);
    unlink(cache_name);

    cupsdSetPrinterReasons(printer, "none");

   /*
    * (Re)register color profiles...
    */

    cupsdRegisterColor(printer);
  }

 /*
  * If we set the device URI but not the port monitor, check which port
  * monitor to use by default...
  */

  if (set_device_uri && !set_port_monitor)
  {
    ppd_file_t	*ppd;			/* PPD file */
    ppd_attr_t	*ppdattr;		/* cupsPortMonitor attribute */


    httpSeparateURI(HTTP_URI_CODING_ALL, printer->device_uri, scheme,
                    sizeof(scheme), username, sizeof(username), host,
		    sizeof(host), &port, resource, sizeof(resource));

    snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot,
	     printer->name);
    if ((ppd = _ppdOpenFile(srcfile, _PPD_LOCALIZATION_NONE)) != NULL)
    {
      for (ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
	   ppdattr;
	   ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL))
        if (!strcmp(scheme, ppdattr->spec))
	{
	  cupsdLogMessage(CUPSD_LOG_INFO,
			  "Setting %s port-monitor to \"%s\" (was \"%s\".)",
			  printer->name, ppdattr->value,
			  printer->port_monitor ? printer->port_monitor
			                        : "none");

	  if (strcmp(ppdattr->value, "none"))
	    cupsdSetString(&printer->port_monitor, ppdattr->value);
	  else
	    cupsdClearString(&printer->port_monitor);

	  break;
	}

      ppdClose(ppd);
    }
  }

  printer->config_time = time(NULL);

 /*
  * Update the printer attributes and return...
  */

  cupsdSetPrinterAttrs(printer);
  if (!printer->temporary)
    cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);

  if (need_restart_job && printer->job)
  {
   /*
    * Restart the current job...
    */

    cupsdSetJobState(printer->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
                     "Job restarted because the printer was modified.");
  }

  cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);

  if (modify)
  {
    cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED,
                  printer, NULL, "Printer \"%s\" modified by \"%s\".",
		  printer->name, get_username(con));

    cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" modified by \"%s\".",
                    printer->name, get_username(con));
  }
  else
  {
    cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
                  printer, NULL, "New printer \"%s\" added by \"%s\".",
		  printer->name, get_username(con));

    cupsdLogMessage(CUPSD_LOG_INFO, "New printer \"%s\" added by \"%s\".",
                    printer->name, get_username(con));
  }

  con->response->request.status.status_code = IPP_OK;
}

Project: cups
File: ipp.c

add_printer_state_reasons(
    cupsd_client_t  *con,		/* I - Client connection */
    cupsd_printer_t *p)			/* I - Printer info */
{
  cupsdLogMessage(CUPSD_LOG_DEBUG2,
                  "add_printer_state_reasons(%p[%d], %p[%s])",
                  con, con->number, p, p->name);

  if (p->num_reasons == 0)
    ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
                 "printer-state-reasons", NULL, "none");
  else
    ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
                  "printer-state-reasons", p->num_reasons, NULL,
		  (const char * const *)p->reasons);
}

Project: cups
File: ipp.c

apply_printer_defaults(
    cupsd_printer_t *printer,		/* I - Printer */
    cupsd_job_t     *job)		/* I - Job */
{
  int		i,			/* Looping var */
		num_options;		/* Number of default options */
  cups_option_t	*options,		/* Default options */
		*option;		/* Current option */


  cupsdLogJob(job, CUPSD_LOG_DEBUG, "Applying default options...");

 /*
  * Collect all of the default options and add the missing ones to the
  * job object...
  */

  for (i = printer->num_options, num_options = 0, options = NULL,
           option = printer->options;
       i > 0;
       i --, option ++)
    if (!ippFindAttribute(job->attrs, option->name, IPP_TAG_ZERO))
    {
      if (!strcmp(option->name, "print-quality") && ippFindAttribute(job->attrs, "cupsPrintQuality", IPP_TAG_NAME))
        continue;                     /* Don't override cupsPrintQuality */

      cupsdLogJob(job, CUPSD_LOG_DEBUG, "Adding default %s=%s", option->name, option->value);

      num_options = cupsAddOption(option->name, option->value, num_options, &options);
    }

 /*
  * Encode these options as attributes in the job object...
  */

  cupsEncodeOptions2(job->attrs, num_options, options, IPP_TAG_JOB);
  cupsFreeOptions(num_options, options);
}

Project: cups
File: ipp.c

cancel_all_jobs(cupsd_client_t  *con,	/* I - Client connection */
	        ipp_attribute_t *uri)	/* I - Job or Printer URI */
{
  int		i;			/* Looping var */
  http_status_t	status;			/* Policy status */
  cups_ptype_t	dtype;			/* Destination type */
  char		scheme[HTTP_MAX_URI],	/* Scheme portion of URI */
		userpass[HTTP_MAX_URI],	/* Username portion of URI */
		hostname[HTTP_MAX_URI],	/* Host portion of URI */
		resource[HTTP_MAX_URI];	/* Resource portion of URI */
  int		port;			/* Port portion of URI */
  ipp_attribute_t *attr;		/* Attribute in request */
  const char	*username = NULL;	/* Username */
  cupsd_jobaction_t purge = CUPSD_JOB_DEFAULT;
					/* Purge? */
  cupsd_printer_t *printer;		/* Printer */
  ipp_attribute_t *job_ids;		/* job-ids attribute */
  cupsd_job_t	*job;			/* Job */


  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_all_jobs(%p[%d], %s)", con,
                  con->number, uri->values[0].string.text);

 /*
  * Get the jobs to cancel/purge...
  */

  switch (con->request->request.op.operation_id)
  {
    case IPP_PURGE_JOBS :
       /*
	* Get the username (if any) for the jobs we want to cancel (only if
	* "my-jobs" is specified...
	*/

        if ((attr = ippFindAttribute(con->request, "my-jobs",
                                     IPP_TAG_BOOLEAN)) != NULL &&
            attr->values[0].boolean)
	{
	  if ((attr = ippFindAttribute(con->request, "requesting-user-name",
				       IPP_TAG_NAME)) != NULL)
	    username = attr->values[0].string.text;
	  else
	  {
	    send_ipp_status(con, IPP_BAD_REQUEST,
			    _("Missing requesting-user-name attribute."));
	    return;
	  }
	}

       /*
	* Look for the "purge-jobs" attribute...
	*/

	if ((attr = ippFindAttribute(con->request, "purge-jobs",
				     IPP_TAG_BOOLEAN)) != NULL)
	  purge = attr->values[0].boolean ? CUPSD_JOB_PURGE : CUPSD_JOB_DEFAULT;
	else
	  purge = CUPSD_JOB_PURGE;
	break;

    case IPP_CANCEL_MY_JOBS :
        if (con->username[0])
          username = con->username;
        else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
					  IPP_TAG_NAME)) != NULL)
          username = attr->values[0].string.text;
        else
        {
	  send_ipp_status(con, IPP_BAD_REQUEST,
			  _("Missing requesting-user-name attribute."));
	  return;
        }

    default :
        break;
  }

  job_ids = ippFindAttribute(con->request, "job-ids", IPP_TAG_INTEGER);

 /*
  * See if we have a printer URI...
  */

  if (strcmp(uri->name, "printer-uri"))
  {
    send_ipp_status(con, IPP_BAD_REQUEST,
                    _("The printer-uri attribute is required."));
    return;
  }

 /*
  * And if the destination is valid...
  */

  if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
  {
   /*
    * Bad URI?
    */

    httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text,
                    scheme, sizeof(scheme), userpass, sizeof(userpass),
		    hostname, sizeof(hostname), &port,
		    resource, sizeof(resource));

    if ((!strncmp(resource, "/printers/", 10) && resource[10]) ||
        (!strncmp(resource, "/classes/", 9) && resource[9]))
    {
      send_ipp_status(con, IPP_NOT_FOUND,
                      _("The printer or class does not exist."));
      return;
    }

   /*
    * Check policy...
    */

    if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
    {
      send_http_error(con, status, NULL);
      return;
    }

    if (job_ids)
    {
      for (i = 0; i < job_ids->num_values; i ++)
      {
	if ((job = cupsdFindJob(job_ids->values[i].integer)) == NULL)
	  break;

        if (con->request->request.op.operation_id == IPP_CANCEL_MY_JOBS &&
            _cups_strcasecmp(job->username, username))
          break;
      }

      if (i < job_ids->num_values)
      {
	send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
			job_ids->values[i].integer);
	return;
      }

      for (i = 0; i < job_ids->num_values; i ++)
      {
	job = cupsdFindJob(job_ids->values[i].integer);

	cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
	                 purge == CUPSD_JOB_PURGE ? "Job purged by user." :
	                                            "Job canceled by user.");
      }

      cupsdLogMessage(CUPSD_LOG_INFO, "Selected jobs were %s by \"%s\".",
		      purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
		      get_username(con));
    }
    else
    {
     /*
      * Cancel all jobs on all printers...
      */

      cupsdCancelJobs(NULL, username, purge);

      cupsdLogMessage(CUPSD_LOG_INFO, "All jobs were %s by \"%s\".",
		      purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
		      get_username(con));
    }
  }
  else
  {
   /*
    * Check policy...
    */

    if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
                                   NULL)) != HTTP_OK)
    {
      send_http_error(con, status, printer);
      return;
    }

    if (job_ids)
    {
      for (i = 0; i < job_ids->num_values; i ++)
      {
	if ((job = cupsdFindJob(job_ids->values[i].integer)) == NULL ||
	    _cups_strcasecmp(job->dest, printer->name))
	  break;

        if (con->request->request.op.operation_id == IPP_CANCEL_MY_JOBS &&
            _cups_strcasecmp(job->username, username))
          break;
      }

      if (i < job_ids->num_values)
      {
	send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
			job_ids->values[i].integer);
	return;
      }

      for (i = 0; i < job_ids->num_values; i ++)
      {
	job = cupsdFindJob(job_ids->values[i].integer);

	cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
	                 purge == CUPSD_JOB_PURGE ? "Job purged by user." :
	                                            "Job canceled by user.");
      }

      cupsdLogMessage(CUPSD_LOG_INFO, "Selected jobs were %s by \"%s\".",
		      purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
		      get_username(con));
    }
    else
    {
     /*
      * Cancel all of the jobs on the named printer...
      */

      cupsdCancelJobs(printer->name, username, purge);

      cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".",
		      printer->name,
		      purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
		      get_username(con));
    }
  }

  con->response->request.status.status_code = IPP_OK;

  cupsdCheckJobs();
}

Project: cups
File: ipp.c

cancel_job(cupsd_client_t  *con,	/* I - Client connection */
	   ipp_attribute_t *uri)	/* I - Job or Printer URI */
{
  ipp_attribute_t *attr;		/* Current attribute */
  int		jobid;			/* Job ID */
  char		scheme[HTTP_MAX_URI],	/* Scheme portion of URI */
		username[HTTP_MAX_URI],	/* Username portion of URI */
		host[HTTP_MAX_URI],	/* Host portion of URI */
		resource[HTTP_MAX_URI];	/* Resource portion of URI */
  int		port;			/* Port portion of URI */
  cupsd_job_t	*job;			/* Job information */
  cups_ptype_t	dtype;			/* Destination type (printer/class) */
  cupsd_printer_t *printer;		/* Printer data */
  cupsd_jobaction_t purge;		/* Purge the job? */


  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", con,
                  con->number, uri->values[0].string.text);

 /*
  * See if we have a job URI or a printer URI...
  */

  if (!strcmp(uri->name, "printer-uri"))
  {
   /*
    * Got a printer URI; see if we also have a job-id attribute...
    */

    if ((attr = ippFindAttribute(con->request, "job-id",
                                 IPP_TAG_INTEGER)) == NULL)
    {
      send_ipp_status(con, IPP_BAD_REQUEST,
                      _("Got a printer-uri attribute but no job-id."));
      return;
    }

    if ((jobid = attr->values[0].integer) == 0)
    {
     /*
      * Find the current job on the specified printer...
      */

      if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
      {
       /*
	* Bad URI...
	*/

	send_ipp_status(con, IPP_NOT_FOUND,
                	_("The printer or class does not exist."));
	return;
      }

     /*
      * See if there are any pending jobs...
      */

      for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
	   job;
	   job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
	if (job->state_value <= IPP_JOB_PROCESSING &&
	    !_cups_strcasecmp(job->dest, printer->name))
	  break;

      if (job)
	jobid = job->id;
      else
      {
       /*
        * No, try stopped jobs...
	*/

	for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
	     job;
	     job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
	  if (job->state_value == IPP_JOB_STOPPED &&
	      !_cups_strcasecmp(job->dest, printer->name))
	    break;

	if (job)
	  jobid = job->id;
	else
	{
	  send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s."),
			  printer->name);
	  return;
	}
      }
    }
  }
  else
  {
   /*
    * Got a job URI; parse it to get the job ID...
    */

    httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
                    sizeof(scheme), username, sizeof(username), host,
		    sizeof(host), &port, resource, sizeof(resource));

    if (strncmp(resource, "/jobs/", 6))
    {
     /*
      * Not a valid URI!
      */

      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
                      uri->values[0].string.text);
      return;
    }

    jobid = atoi(resource + 6);
  }

 /*
  * Look for the "purge-job" attribute...
  */

  if ((attr = ippFindAttribute(con->request, "purge-job",
                               IPP_TAG_BOOLEAN)) != NULL)
    purge = attr->values[0].boolean ? CUPSD_JOB_PURGE : CUPSD_JOB_DEFAULT;
  else
    purge = CUPSD_JOB_DEFAULT;

 /*
  * See if the job exists...
  */

  if ((job = cupsdFindJob(jobid)) == NULL)
  {
   /*
    * Nope - return a "not found" error...
    */

    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
    return;
  }

 /*
  * See if the job is owned by the requesting user...
  */

  if (!validate_user(job, con, job->username, username, sizeof(username)))
  {
    send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
                    cupsdFindDest(job->dest));
    return;
  }

 /*
  * See if the job is already completed, canceled, or aborted; if so,
  * we can't cancel...
  */

  if (job->state_value >= IPP_JOB_CANCELED && purge != CUPSD_JOB_PURGE)
  {
    switch (job->state_value)
    {
      case IPP_JOB_CANCELED :
	  send_ipp_status(con, IPP_NOT_POSSIBLE,
                	  _("Job #%d is already canceled - can\'t cancel."),
			  jobid);
          break;

      case IPP_JOB_ABORTED :
	  send_ipp_status(con, IPP_NOT_POSSIBLE,
                	  _("Job #%d is already aborted - can\'t cancel."),
			  jobid);
          break;

      default :
	  send_ipp_status(con, IPP_NOT_POSSIBLE,
                	  _("Job #%d is already completed - can\'t cancel."),
			  jobid);
          break;
    }

    return;
  }

 /*
  * Cancel the job and return...
  */

  cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
                   purge == CUPSD_JOB_PURGE ? "Job purged by \"%s\"" :
		                              "Job canceled by \"%s\"",
		   username);
  cupsdCheckJobs();

  if (purge == CUPSD_JOB_PURGE)
    cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Purged by \"%s\".", jobid,
		    username);
  else
    cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Canceled by \"%s\".", jobid,
		    username);

  con->response->request.status.status_code = IPP_OK;
}

Project: cups
File: ipp.c

cancel_subscription(
    cupsd_client_t *con,		/* I - Client connection */
    int            sub_id)		/* I - Subscription ID */
{
  http_status_t		status;		/* Policy status */
  cupsd_subscription_t	*sub;		/* Subscription */


  cupsdLogMessage(CUPSD_LOG_DEBUG2,
                  "cancel_subscription(con=%p[%d], sub_id=%d)",
                  con, con->number, sub_id);

 /*
  * Is the subscription ID valid?
  */

  if ((sub = cupsdFindSubscription(sub_id)) == NULL)
  {
   /*
    * Bad subscription ID...
    */

    send_ipp_status(con, IPP_NOT_FOUND,
                    _("Subscription #%d does not exist."), sub_id);
    return;
  }

 /*
  * Check policy...
  */

  if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
                                             DefaultPolicyPtr,
                                 con, sub->owner)) != HTTP_OK)
  {
    send_http_error(con, status, sub->dest);
    return;
  }

 /*
  * Cancel the subscription...
  */

  cupsdDeleteSubscription(sub, 1);

  con->response->request.status.status_code = IPP_OK;
}

Project: cups
File: ipp.c

check_quotas(cupsd_client_t  *con,	/* I - Client connection */
             cupsd_printer_t *p)	/* I - Printer or class */
{
  char		username[33],		/* Username */
		*name;			/* Current user name */
  cupsd_quota_t	*q;			/* Quota data */
#ifdef HAVE_MBR_UID_TO_UUID
 /*
  * Use Apple membership APIs which require that all names represent
  * valid user account or group records accessible by the server.
  */

  uuid_t	usr_uuid;		/* UUID for job requesting user  */
  uuid_t	usr2_uuid;		/* UUID for ACL user name entry  */
  uuid_t	grp_uuid;		/* UUID for ACL group name entry */
  int		mbr_err;		/* Error from membership function */
  int		is_member;		/* Is this user a member? */
#else
 /*
  * Use standard POSIX APIs for checking users and groups...
  */

  struct passwd	*pw;			/* User password data */
#endif /* HAVE_MBR_UID_TO_UUID */


  cupsdLogMessage(CUPSD_LOG_DEBUG2, "check_quotas(%p[%d], %p[%s])",
                  con, con->number, p, p->name);

 /*
  * Figure out who is printing...
  */

  strlcpy(username, get_username(con), sizeof(username));

  if ((name = strchr(username, '@')) != NULL)
    *name = '\0';			/* Strip @REALM */

 /*
  * Check global active job limits for printers and users...
  */

  if (MaxJobsPerPrinter)
  {
   /*
    * Check if there are too many pending jobs on this printer...
    */

    if (cupsdGetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
    {
      cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for printer \"%s\"...",
                      p->name);
      return (-1);
    }
  }

  if (MaxJobsPerUser)
  {
   /*
    * Check if there are too many pending jobs for this user...
    */

    if (cupsdGetUserJobCount(username) >= MaxJobsPerUser)
    {
      cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for user \"%s\"...",
                      username);
      return (-1);
    }
  }

 /*
  * Check against users...
  */

  if (cupsArrayCount(p->users) == 0 && p->k_limit == 0 && p->page_limit == 0)
    return (1);

  if (cupsArrayCount(p->users))
  {
#ifdef HAVE_MBR_UID_TO_UUID
   /*
    * Get UUID for job requesting user...
    */

    if (mbr_user_name_to_uuid((char *)username, usr_uuid))
    {
     /*
      * Unknown user...
      */

      cupsdLogMessage(CUPSD_LOG_DEBUG,
		      "check_quotas: UUID lookup failed for user \"%s\"",
		      username);
      cupsdLogMessage(CUPSD_LOG_INFO,
		      "Denying user \"%s\" access to printer \"%s\" "
		      "(unknown user)...",
		      username, p->name);
      return (0);
    }
#else
   /*
    * Get UID and GID of requesting user...
    */

    pw = getpwnam(username);
    endpwent();
#endif /* HAVE_MBR_UID_TO_UUID */

    for (name = (char *)cupsArrayFirst(p->users);
         name;
	 name = (char *)cupsArrayNext(p->users))
      if (name[0] == '@')
      {
       /*
        * Check group membership...
	*/

#ifdef HAVE_MBR_UID_TO_UUID
        if (name[1] == '#')
	{
	  if (uuid_parse(name + 2, grp_uuid))
	    uuid_clear(grp_uuid);
	}
	else if ((mbr_err = mbr_group_name_to_uuid(name + 1, grp_uuid)) != 0)
	{
	 /*
	  * Invalid ACL entries are ignored for matching; just record a
	  * warning in the log...
	  */

	  cupsdLogMessage(CUPSD_LOG_DEBUG,
	                  "check_quotas: UUID lookup failed for ACL entry "
			  "\"%s\" (err=%d)", name, mbr_err);
	  cupsdLogMessage(CUPSD_LOG_WARN,
	                  "Access control entry \"%s\" not a valid group name; "
			  "entry ignored", name);
	}

	if ((mbr_err = mbr_check_membership(usr_uuid, grp_uuid,
					    &is_member)) != 0)
	{
	 /*
	  * At this point, there should be no errors, but check anyways...
	  */

	  cupsdLogMessage(CUPSD_LOG_DEBUG,
			  "check_quotas: group \"%s\" membership check "
			  "failed (err=%d)", name + 1, mbr_err);
	  is_member = 0;
	}

       /*
	* Stop if we found a match...
	*/

	if (is_member)
	  break;

#else
        if (cupsdCheckGroup(username, pw, name + 1))
	  break;
#endif /* HAVE_MBR_UID_TO_UUID */
      }
#ifdef HAVE_MBR_UID_TO_UUID
      else
      {
        if (name[0] == '#')
	{
	  if (uuid_parse(name + 1, usr2_uuid))
	    uuid_clear(usr2_uuid);
        }
        else if ((mbr_err = mbr_user_name_to_uuid(name, usr2_uuid)) != 0)
    	{
	 /*
	  * Invalid ACL entries are ignored for matching; just record a
	  * warning in the log...
	  */

          cupsdLogMessage(CUPSD_LOG_DEBUG,
	                  "check_quotas: UUID lookup failed for ACL entry "
			  "\"%s\" (err=%d)", name, mbr_err);
          cupsdLogMessage(CUPSD_LOG_WARN,
	                  "Access control entry \"%s\" not a valid user name; "
			  "entry ignored", name);
	}

	if (!uuid_compare(usr_uuid, usr2_uuid))
	  break;
      }
#else
      else if (!_cups_strcasecmp(username, name))
	break;
#endif /* HAVE_MBR_UID_TO_UUID */

    if ((name != NULL) == p->deny_users)
    {
      cupsdLogMessage(CUPSD_LOG_INFO,
                      "Denying user \"%s\" access to printer \"%s\"...",
        	      username, p->name);
      return (0);
    }
  }

 /*
  * Check quotas...
  */

  if (p->k_limit || p->page_limit)
  {
    if ((q = cupsdUpdateQuota(p, username, 0, 0)) == NULL)
    {
      cupsdLogMessage(CUPSD_LOG_ERROR,
                      "Unable to allocate quota data for user \"%s\"",
                      username);
      return (-1);
    }

    if ((q->k_count >= p->k_limit && p->k_limit) ||
        (q->page_count >= p->page_limit && p->page_limit))
    {
      cupsdLogMessage(CUPSD_LOG_INFO, "User \"%s\" is over the quota limit...",
                      username);
      return (-1);
    }
  }

 /*
  * If we have gotten this far, we're done!
  */

  return (1);
}

Project: cups
File: ipp.c

check_rss_recipient(
    const char *recipient)		/* I - Recipient URI */
{
  cupsd_subscription_t	*sub;		/* Current subscription */


  for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
       sub;
       sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
    if (sub->recipient)
    {
     /*
      * Compare the URIs up to the first ?...
      */

      const char *r1, *r2;

      for (r1 = recipient, r2 = sub->recipient;
           *r1 == *r2 && *r1 && *r1 != '?' && *r2 && *r2 != '?';
	   r1 ++, r2 ++);

      if (*r1 == *r2)
        return (0);
    }

  return (1);
}

Project: cups
File: ipp.c

close_job(cupsd_client_t  *con,		/* I - Client connection */
          ipp_attribute_t *uri)		/* I - Printer URI */
{
  cupsd_job_t		*job;		/* Job */
  ipp_attribute_t	*attr;		/* Attribute */
  char			job_uri[HTTP_MAX_URI],
					/* Job URI */
			username[256];	/* User name */


  cupsdLogMessage(CUPSD_LOG_DEBUG2, "close_job(%p[%d], %s)", con,
                  con->number, uri->values[0].string.text);

 /*
  * See if we have a job URI or a printer URI...
  */

  if (strcmp(uri->name, "printer-uri"))
  {
   /*
    * job-uri is not supported by Close-Job!
    */

    send_ipp_status(con, IPP_BAD_REQUEST,
		    _("Close-Job doesn't support the job-uri attribute."));
    return;
  }

 /*
  * Got a printer URI; see if we also have a job-id attribute...
  */

  if ((attr = ippFindAttribute(con->request, "job-id",
			       IPP_TAG_INTEGER)) == NULL)
  {
    send_ipp_status(con, IPP_BAD_REQUEST,
		    _("Got a printer-uri attribute but no job-id."));
    return;
  }

  if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
  {
   /*
    * Nope - return a "not found" error...
    */

    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
                    attr->values[0].integer);
    return;
  }

 /*
  * See if the job is owned by the requesting user...
  */

  if (!validate_user(job, con, job->username, username, sizeof(username)))
  {
    send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
                    cupsdFindDest(job->dest));
    return;
  }

 /*
  * Add any ending sheet...
  */

  if (cupsdTimeoutJob(job))
    return;

  if (job->state_value == IPP_JOB_STOPPED)
  {
    job->state->values[0].integer = IPP_JOB_PENDING;
    job->state_value              = IPP_JOB_PENDING;
  }
  else if (job->state_value == IPP_JOB_HELD)
  {
    if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
				 IPP_TAG_KEYWORD)) == NULL)
      attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);

    if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
    {
      job->state->values[0].integer = IPP_JOB_PENDING;
      job->state_value              = IPP_JOB_PENDING;
    }
  }

  job->dirty = 1;
  cupsdMarkDirty(CUPSD_DIRTY_JOBS);

 /*
  * Fill in the response info...
  */

  httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
                   con->clientname, con->clientport, "/jobs/%d", job->id);
  ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
               job_uri);

  ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);

  ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
                job->state_value);

  con->response->request.status.status_code = IPP_OK;

 /*
  * Start the job if necessary...
  */

  cupsdCheckJobs();
}

Project: cups
File: ipp.c

copy_attrs(ipp_t        *to,		/* I - Destination request */
           ipp_t        *from,		/* I - Source request */
           cups_array_t *ra,		/* I - Requested attributes */
	   ipp_tag_t    group,		/* I - Group to copy */
	   int          quickcopy,	/* I - Do a quick copy? */
	   cups_array_t *exclude)	/* I - Attributes to exclude? */
{
  ipp_attribute_t	*fromattr;	/* Source attribute */


  cupsdLogMessage(CUPSD_LOG_DEBUG2,
                  "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
		  to, from, ra, group, quickcopy);

  if (!to || !from)
    return;

  for (fromattr = from->attrs; fromattr; fromattr = fromattr->next)
  {
   /*
    * Filter attributes as needed...
    */

    if ((group != IPP_TAG_ZERO && fromattr->group_tag != group &&
         fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
      continue;

    if (!strcmp(fromattr->name, "document-password") ||
        !strcmp(fromattr->name, "job-authorization-uri") ||
        !strcmp(fromattr->name, "job-password") ||
        !strcmp(fromattr->name, "job-password-encryption") ||
        !strcmp(fromattr->name, "job-printer-uri"))
      continue;

    if (exclude &&
        (cupsArrayFind(exclude, fromattr->name) ||
	 cupsArrayFind(exclude, "all")))
    {
     /*
      * We need to exclude this attribute for security reasons; we require the
      * job-id attribute regardless of the security settings for IPP
      * conformance.
      *
      * The job-printer-uri attribute is handled by copy_job_attrs().
      *
      * Subscription attribute security is handled by copy_subscription_attrs().
      */

      if (strcmp(fromattr->name, "job-id"))
        continue;
    }

    if (!ra || cupsArrayFind(ra, fromattr->name))
    {
     /*
      * Don't send collection attributes by default to IPP/1.x clients
      * since many do not support collections.  Also don't send
      * media-col-database unless specifically requested by the client.
      */

      if (fromattr->value_tag == IPP_TAG_BEGIN_COLLECTION &&
          !ra &&
	  (to->request.status.version[0] == 1 ||
	   !strcmp(fromattr->name, "media-col-database")))
	continue;

      ippCopyAttribute(to, fromattr, quickcopy);
    }
  }
}