2 * "$Id: ipp.c,v 1.127.2.78 2004/02/17 21:32:58 mike Exp $"
4 * IPP routines for the Common UNIX Printing System (CUPS) scheduler.
6 * Copyright 1997-2003 by Easy Software Products, all rights reserved.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * ProcessIPPRequest() - Process an incoming IPP request...
27 * accept_jobs() - Accept print jobs to a printer.
28 * add_class() - Add a class to the system.
29 * add_file() - Add a file to a job.
30 * add_job_state_reasons() - Add the "job-state-reasons" attribute based
31 * upon the job and printer state...
32 * add_printer() - Add a printer to the system.
33 * add_printer_state_reasons() - Add the "printer-state-reasons" attribute
34 * based upon the printer state...
35 * add_queued_job_count() - Add the "queued-job-count" attribute for
36 * cancel_all_jobs() - Cancel all print jobs.
37 * cancel_job() - Cancel a print job.
38 * check_quotas() - Check quotas for a printer and user.
39 * copy_attribute() - Copy a single attribute.
40 * copy_attrs() - Copy attributes from one request to another.
41 * copy_banner() - Copy a banner file to the requests directory
42 * for the specified job.
43 * copy_file() - Copy a PPD file or interface script...
44 * copy_model() - Copy a PPD model file, substituting default
46 * create_job() - Print a file to a printer or class.
47 * delete_printer() - Remove a printer or class from the system.
48 * get_default() - Get the default destination.
49 * get_devices() - Get the list of available devices on the
51 * get_jobs() - Get a list of jobs for the specified printer.
52 * get_job_attrs() - Get job attributes.
53 * get_ppds() - Get the list of PPD files on the local
55 * get_printer_attrs() - Get printer attributes.
56 * get_printers() - Get a list of printers.
57 * hold_job() - Hold a print job.
58 * move_job() - Move a job to a new destination.
59 * ppd_add_default() - Add a PPD default choice.
60 * ppd_parse_line() - Parse a PPD default line.
61 * print_job() - Print a file to a printer or class.
62 * read_ps_line() - Read a line from a PS file...
63 * read_ps_job_ticket() - Reads a job ticket embedded in a PS file.
64 * reject_jobs() - Reject print jobs to a printer.
65 * release_job() - Release a held print job.
66 * restart_job() - Restart an old print job.
67 * send_document() - Send a file to a printer or class.
68 * send_ipp_error() - Send an error status back to the IPP client.
69 * set_default() - Set the default destination...
70 * set_job_attrs() - Set job attributes.
71 * start_printer() - Start a printer.
72 * stop_printer() - Stop a printer.
73 * validate_job() - Validate printer options and destination.
74 * validate_user() - Validate the user for the request.
78 * Include necessary headers...
87 #endif /* HAVE_LIBPAPER */
91 * PPD default choice structure...
96 char option
[PPD_MAX_NAME
]; /* Main keyword (option name) */
97 char choice
[PPD_MAX_NAME
]; /* Option keyword (choice name) */
106 static void accept_jobs(client_t
*con
, ipp_attribute_t
*uri
);
107 static void add_class(client_t
*con
, ipp_attribute_t
*uri
);
108 static int add_file(client_t
*con
, job_t
*job
, mime_type_t
*filetype
,
110 static void add_job_state_reasons(client_t
*con
, job_t
*job
);
111 static void add_printer(client_t
*con
, ipp_attribute_t
*uri
);
112 static void add_printer_state_reasons(client_t
*con
, printer_t
*p
);
113 static void add_queued_job_count(client_t
*con
, printer_t
*p
);
114 static void cancel_all_jobs(client_t
*con
, ipp_attribute_t
*uri
);
115 static void cancel_job(client_t
*con
, ipp_attribute_t
*uri
);
116 static int check_quotas(client_t
*con
, printer_t
*p
);
117 static ipp_attribute_t
*copy_attribute(ipp_t
*to
, ipp_attribute_t
*attr
,
119 static void copy_attrs(ipp_t
*to
, ipp_t
*from
, ipp_attribute_t
*req
,
120 ipp_tag_t group
, int quickcopy
);
121 static int copy_banner(client_t
*con
, job_t
*job
, const char *name
);
122 static int copy_file(const char *from
, const char *to
);
123 static int copy_model(const char *from
, const char *to
);
124 static void create_job(client_t
*con
, ipp_attribute_t
*uri
);
125 static void delete_printer(client_t
*con
, ipp_attribute_t
*uri
);
126 static void get_default(client_t
*con
);
127 static void get_devices(client_t
*con
);
128 static void get_jobs(client_t
*con
, ipp_attribute_t
*uri
);
129 static void get_job_attrs(client_t
*con
, ipp_attribute_t
*uri
);
130 static void get_ppds(client_t
*con
);
131 static void get_printers(client_t
*con
, int type
);
132 static void get_printer_attrs(client_t
*con
, ipp_attribute_t
*uri
);
133 static void hold_job(client_t
*con
, ipp_attribute_t
*uri
);
134 static void move_job(client_t
*con
, ipp_attribute_t
*uri
);
135 static int ppd_add_default(const char *option
, const char *choice
,
136 int num_defaults
, ppd_default_t
**defaults
);
137 static int ppd_parse_line(const char *line
, char *option
, int olen
,
138 char *choice
, int clen
);
139 static void print_job(client_t
*con
, ipp_attribute_t
*uri
);
140 static void read_ps_job_ticket(client_t
*con
);
141 static void reject_jobs(client_t
*con
, ipp_attribute_t
*uri
);
142 static void release_job(client_t
*con
, ipp_attribute_t
*uri
);
143 static void restart_job(client_t
*con
, ipp_attribute_t
*uri
);
144 static void send_document(client_t
*con
, ipp_attribute_t
*uri
);
145 static void send_ipp_error(client_t
*con
, ipp_status_t status
);
146 static void set_default(client_t
*con
, ipp_attribute_t
*uri
);
147 static void set_job_attrs(client_t
*con
, ipp_attribute_t
*uri
);
148 static void start_printer(client_t
*con
, ipp_attribute_t
*uri
);
149 static void stop_printer(client_t
*con
, ipp_attribute_t
*uri
);
150 static void validate_job(client_t
*con
, ipp_attribute_t
*uri
);
151 static int validate_user(client_t
*con
, const char *owner
, char *username
,
156 * 'ProcessIPPRequest()' - Process an incoming IPP request...
159 int /* O - 1 on success, 0 on failure */
160 ProcessIPPRequest(client_t
*con
) /* I - Client connection */
162 ipp_tag_t group
; /* Current group tag */
163 ipp_attribute_t
*attr
; /* Current attribute */
164 ipp_attribute_t
*charset
; /* Character set attribute */
165 ipp_attribute_t
*language
; /* Language attribute */
166 ipp_attribute_t
*uri
; /* Printer URI attribute */
167 ipp_attribute_t
*username
; /* requesting-user-name attr */
170 DEBUG_printf(("ProcessIPPRequest(%08x)\n", con
));
171 DEBUG_printf(("ProcessIPPRequest: operation_id = %04x\n",
172 con
->request
->request
.op
.operation_id
));
175 * First build an empty response message for this request...
178 con
->response
= ippNew();
180 con
->response
->request
.status
.version
[0] = con
->request
->request
.op
.version
[0];
181 con
->response
->request
.status
.version
[1] = con
->request
->request
.op
.version
[1];
182 con
->response
->request
.status
.request_id
= con
->request
->request
.op
.request_id
;
185 * Then validate the request header and required attributes...
188 if (con
->request
->request
.any
.version
[0] != 1)
191 * Return an error, since we only support IPP 1.x.
194 LogMessage(L_ERROR
, "ProcessIPPRequest: bad request version (%d.%d)!",
195 con
->request
->request
.any
.version
[0],
196 con
->request
->request
.any
.version
[1]);
198 send_ipp_error(con
, IPP_VERSION_NOT_SUPPORTED
);
200 else if (con
->request
->attrs
== NULL
)
202 LogMessage(L_ERROR
, "ProcessIPPRequest: no attributes in request!");
203 send_ipp_error(con
, IPP_BAD_REQUEST
);
208 * Make sure that the attributes are provided in the correct order and
209 * don't repeat groups...
212 for (attr
= con
->request
->attrs
, group
= attr
->group_tag
;
215 if (attr
->group_tag
< group
)
218 * Out of order; return an error...
221 LogMessage(L_ERROR
, "ProcessIPPRequest: attribute groups are out of order!");
222 send_ipp_error(con
, IPP_BAD_REQUEST
);
226 group
= attr
->group_tag
;
231 * Then make sure that the first three attributes are:
234 * attributes-natural-language
235 * printer-uri/job-uri
238 attr
= con
->request
->attrs
;
239 if (attr
!= NULL
&& strcmp(attr
->name
, "attributes-charset") == 0 &&
240 attr
->value_tag
== IPP_TAG_CHARSET
)
247 if (attr
!= NULL
&& strcmp(attr
->name
, "attributes-natural-language") == 0 &&
248 attr
->value_tag
== IPP_TAG_LANGUAGE
)
253 if ((attr
= ippFindAttribute(con
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
255 else if ((attr
= ippFindAttribute(con
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
261 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
262 "attributes-charset", NULL
, charset
->values
[0].string
.text
);
264 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
265 "attributes-charset", NULL
, DefaultCharset
);
268 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
269 "attributes-natural-language", NULL
,
270 language
->values
[0].string
.text
);
272 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
273 "attributes-natural-language", NULL
, DefaultLanguage
);
275 if (charset
== NULL
|| language
== NULL
||
277 con
->request
->request
.op
.operation_id
!= CUPS_GET_DEFAULT
&&
278 con
->request
->request
.op
.operation_id
!= CUPS_GET_PRINTERS
&&
279 con
->request
->request
.op
.operation_id
!= CUPS_GET_CLASSES
&&
280 con
->request
->request
.op
.operation_id
!= CUPS_GET_DEVICES
&&
281 con
->request
->request
.op
.operation_id
!= CUPS_GET_PPDS
))
284 * Return an error, since attributes-charset,
285 * attributes-natural-language, and printer-uri/job-uri are required
286 * for all operations.
290 LogMessage(L_ERROR
, "ProcessIPPRequest: missing attributes-charset attribute!");
292 if (language
== NULL
)
293 LogMessage(L_ERROR
, "ProcessIPPRequest: missing attributes-natural-language attribute!");
296 LogMessage(L_ERROR
, "ProcessIPPRequest: missing printer-uri or job-uri attribute!");
298 send_ipp_error(con
, IPP_BAD_REQUEST
);
303 * OK, all the checks pass so far; make sure requesting-user-name is
304 * not "root" from a remote host...
307 if ((username
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
310 * Check for root user...
313 if (strcmp(username
->values
[0].string
.text
, "root") == 0 &&
314 strcasecmp(con
->http
.hostname
, "localhost") != 0 &&
315 strcmp(con
->username
, "root") != 0)
318 * Remote unauthenticated user masquerading as local root...
321 free(username
->values
[0].string
.text
);
322 username
->values
[0].string
.text
= strdup(RemoteRoot
);
327 * Then try processing the operation...
330 switch (con
->request
->request
.op
.operation_id
)
336 case IPP_VALIDATE_JOB
:
337 validate_job(con
, uri
);
340 case IPP_CREATE_JOB
:
341 create_job(con
, uri
);
344 case IPP_SEND_DOCUMENT
:
345 send_document(con
, uri
);
348 case IPP_CANCEL_JOB
:
349 cancel_job(con
, uri
);
352 case IPP_GET_JOB_ATTRIBUTES
:
353 get_job_attrs(con
, uri
);
360 case IPP_GET_PRINTER_ATTRIBUTES
:
361 get_printer_attrs(con
, uri
);
368 case IPP_RELEASE_JOB
:
369 release_job(con
, uri
);
372 case IPP_RESTART_JOB
:
373 restart_job(con
, uri
);
376 case IPP_PAUSE_PRINTER
:
377 stop_printer(con
, uri
);
380 case IPP_RESUME_PRINTER
:
381 start_printer(con
, uri
);
384 case IPP_PURGE_JOBS
:
385 cancel_all_jobs(con
, uri
);
388 case IPP_SET_JOB_ATTRIBUTES
:
389 set_job_attrs(con
, uri
);
392 case CUPS_GET_DEFAULT
:
396 case CUPS_GET_PRINTERS
:
397 get_printers(con
, 0);
400 case CUPS_GET_CLASSES
:
401 get_printers(con
, CUPS_PRINTER_CLASS
);
404 case CUPS_ADD_PRINTER
:
405 add_printer(con
, uri
);
408 case CUPS_DELETE_PRINTER
:
409 delete_printer(con
, uri
);
412 case CUPS_ADD_CLASS
:
416 case CUPS_DELETE_CLASS
:
417 delete_printer(con
, uri
);
420 case CUPS_ACCEPT_JOBS
:
421 case IPP_ENABLE_PRINTER
:
422 accept_jobs(con
, uri
);
425 case CUPS_REJECT_JOBS
:
426 case IPP_DISABLE_PRINTER
:
427 reject_jobs(con
, uri
);
430 case CUPS_SET_DEFAULT
:
431 set_default(con
, uri
);
434 case CUPS_GET_DEVICES
:
447 send_ipp_error(con
, IPP_OPERATION_NOT_SUPPORTED
);
453 LogMessage(L_DEBUG
, "ProcessIPPRequest: %d status_code=%x",
454 con
->http
.fd
, con
->response
->request
.status
.status_code
);
456 if (SendHeader(con
, HTTP_OK
, "application/ipp"))
459 if (con
->http
.version
== HTTP_1_1
)
461 con
->http
.data_encoding
= HTTP_ENCODE_CHUNKED
;
463 httpPrintf(HTTP(con
), "Transfer-Encoding: chunked\r\n\r\n");
468 con
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
469 con
->http
.data_remaining
= ippLength(con
->response
);
471 httpPrintf(HTTP(con
), "Content-Length: %d\r\n\r\n",
472 con
->http
.data_remaining
);
475 LogMessage(L_DEBUG2
, "ProcessIPPRequest: Adding fd %d to OutputSet...",
478 FD_SET(con
->http
.fd
, OutputSet
);
481 * Tell the caller the response header was sent successfully...
489 * Tell the caller the response header could not be sent...
498 * 'accept_jobs()' - Accept print jobs to a printer.
502 accept_jobs(client_t
*con
, /* I - Client connection */
503 ipp_attribute_t
*uri
) /* I - Printer or class URI */
505 cups_ptype_t dtype
; /* Destination type (printer or class) */
506 char method
[HTTP_MAX_URI
],
507 /* Method portion of URI */
508 username
[HTTP_MAX_URI
],
509 /* Username portion of URI */
511 /* Host portion of URI */
512 resource
[HTTP_MAX_URI
];
513 /* Resource portion of URI */
514 int port
; /* Port portion of URI */
515 const char *name
; /* Printer name */
516 printer_t
*printer
; /* Printer data */
519 LogMessage(L_DEBUG2
, "accept_jobs(%d, %s)\n", con
->http
.fd
,
520 uri
->values
[0].string
.text
);
523 * Was this operation called from the correct URI?
526 if (strncmp(con
->uri
, "/admin/", 7) != 0)
528 LogMessage(L_ERROR
, "accept_jobs: admin request on bad resource \'%s\'!",
530 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
535 * Is the destination valid?
538 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
540 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
546 LogMessage(L_ERROR
, "accept_jobs: resource name \'%s\' no good!", resource
);
547 send_ipp_error(con
, IPP_NOT_FOUND
);
552 * Accept jobs sent to the printer...
555 if (dtype
& CUPS_PRINTER_CLASS
)
556 printer
= FindClass(name
);
558 printer
= FindPrinter(name
);
560 printer
->accepting
= 1;
561 printer
->state_message
[0] = '\0';
563 AddPrinterHistory(printer
);
565 if (dtype
& CUPS_PRINTER_CLASS
)
570 LogMessage(L_INFO
, "Printer \'%s\' now accepting jobs (\'%s\').", name
,
574 * Everything was ok, so return OK status...
577 con
->response
->request
.status
.status_code
= IPP_OK
;
582 * 'add_class()' - Add a class to the system.
586 add_class(client_t
*con
, /* I - Client connection */
587 ipp_attribute_t
*uri
) /* I - URI of class */
589 int i
; /* Looping var */
590 char method
[HTTP_MAX_URI
],
591 /* Method portion of URI */
592 username
[HTTP_MAX_URI
],
593 /* Username portion of URI */
595 /* Host portion of URI */
596 resource
[HTTP_MAX_URI
];
597 /* Resource portion of URI */
598 int port
; /* Port portion of URI */
599 printer_t
*pclass
; /* Class */
600 cups_ptype_t dtype
; /* Destination type */
601 const char *dest
; /* Printer or class name */
602 ipp_attribute_t
*attr
; /* Printer attribute */
603 int modify
; /* Non-zero if we just modified */
606 LogMessage(L_DEBUG2
, "add_class(%d, %s)\n", con
->http
.fd
,
607 uri
->values
[0].string
.text
);
610 * Was this operation called from the correct URI?
613 if (strncmp(con
->uri
, "/admin/", 7) != 0)
615 LogMessage(L_ERROR
, "add_class: admin request on bad resource \'%s\'!",
617 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
621 DEBUG_printf(("add_class(%08x, %08x)\n", con
, uri
));
624 * Do we have a valid URI?
627 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
629 if (strncmp(resource
, "/classes/", 9) != 0 || strlen(resource
) == 9)
632 * No, return an error...
635 send_ipp_error(con
, IPP_BAD_REQUEST
);
640 * See if the class already exists; if not, create a new class...
643 if ((pclass
= FindClass(resource
+ 9)) == NULL
)
646 * Class doesn't exist; see if we have a printer of the same name...
649 if ((pclass
= FindPrinter(resource
+ 9)) != NULL
&&
650 !(pclass
->type
& CUPS_PRINTER_REMOTE
))
653 * Yes, return an error...
656 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
661 * No, add the pclass...
664 pclass
= AddClass(resource
+ 9);
667 else if (pclass
->type
& CUPS_PRINTER_IMPLICIT
)
670 * Rename the implicit class to "AnyClass" or remove it...
673 if (ImplicitAnyClasses
)
675 snprintf(pclass
->name
, sizeof(pclass
->name
), "Any%s", resource
+ 9);
679 DeletePrinter(pclass
, 1);
682 * Add the class as a new local class...
685 pclass
= AddClass(resource
+ 9);
688 else if (pclass
->type
& CUPS_PRINTER_REMOTE
)
691 * Rename the remote class to "Class"...
694 DeletePrinterFilters(pclass
);
695 snprintf(pclass
->name
, sizeof(pclass
->name
), "%s@%s", resource
+ 9,
697 SetPrinterAttrs(pclass
);
701 * Add the class as a new local class...
704 pclass
= AddClass(resource
+ 9);
711 * Look for attributes and copy them over as needed...
714 if ((attr
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
715 SetString(&pclass
->location
, attr
->values
[0].string
.text
);
717 if ((attr
= ippFindAttribute(con
->request
, "printer-info", IPP_TAG_TEXT
)) != NULL
)
718 SetString(&pclass
->info
, attr
->values
[0].string
.text
);
720 if ((attr
= ippFindAttribute(con
->request
, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN
)) != NULL
)
722 LogMessage(L_INFO
, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
723 pclass
->name
, attr
->values
[0].boolean
, pclass
->accepting
);
725 pclass
->accepting
= attr
->values
[0].boolean
;
726 AddPrinterHistory(pclass
);
728 if ((attr
= ippFindAttribute(con
->request
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
730 if (attr
->values
[0].integer
!= IPP_PRINTER_IDLE
&&
731 attr
->values
[0].integer
== IPP_PRINTER_STOPPED
)
733 LogMessage(L_ERROR
, "Attempt to set %s printer-state to bad value %d!",
734 pclass
->name
, attr
->values
[0].integer
);
735 send_ipp_error(con
, IPP_BAD_REQUEST
);
739 LogMessage(L_INFO
, "Setting %s printer-state to %d (was %d.)", pclass
->name
,
740 attr
->values
[0].integer
, pclass
->state
);
742 SetPrinterState(pclass
, attr
->values
[0].integer
, 0);
744 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message", IPP_TAG_TEXT
)) != NULL
)
746 strlcpy(pclass
->state_message
, attr
->values
[0].string
.text
,
747 sizeof(pclass
->state_message
));
748 AddPrinterHistory(pclass
);
750 if ((attr
= ippFindAttribute(con
->request
, "job-sheets-default", IPP_TAG_ZERO
)) != NULL
&&
753 SetString(&pclass
->job_sheets
[0], attr
->values
[0].string
.text
);
754 if (attr
->num_values
> 1)
755 SetString(&pclass
->job_sheets
[1], attr
->values
[1].string
.text
);
757 SetString(&pclass
->job_sheets
[1], "none");
759 if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name-allowed",
760 IPP_TAG_ZERO
)) != NULL
)
762 FreePrinterUsers(pclass
);
764 pclass
->deny_users
= 0;
765 if (attr
->value_tag
== IPP_TAG_NAME
&&
766 (attr
->num_values
> 1 ||
767 strcmp(attr
->values
[0].string
.text
, "all") != 0))
768 for (i
= 0; i
< attr
->num_values
; i
++)
769 AddPrinterUser(pclass
, attr
->values
[i
].string
.text
);
771 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name-denied",
772 IPP_TAG_ZERO
)) != NULL
)
774 FreePrinterUsers(pclass
);
776 pclass
->deny_users
= 1;
777 if (attr
->value_tag
== IPP_TAG_NAME
&&
778 (attr
->num_values
> 1 ||
779 strcmp(attr
->values
[0].string
.text
, "none") != 0))
780 for (i
= 0; i
< attr
->num_values
; i
++)
781 AddPrinterUser(pclass
, attr
->values
[i
].string
.text
);
783 if ((attr
= ippFindAttribute(con
->request
, "job-quota-period",
784 IPP_TAG_INTEGER
)) != NULL
)
786 LogMessage(L_DEBUG
, "add_class: Setting job-quota-period to %d...",
787 attr
->values
[0].integer
);
789 pclass
->quota_period
= attr
->values
[0].integer
;
791 if ((attr
= ippFindAttribute(con
->request
, "job-k-limit",
792 IPP_TAG_INTEGER
)) != NULL
)
794 LogMessage(L_DEBUG
, "add_class: Setting job-k-limit to %d...",
795 attr
->values
[0].integer
);
797 pclass
->k_limit
= attr
->values
[0].integer
;
799 if ((attr
= ippFindAttribute(con
->request
, "job-page-limit",
800 IPP_TAG_INTEGER
)) != NULL
)
802 LogMessage(L_DEBUG
, "add_class: Setting job-page-limit to %d...",
803 attr
->values
[0].integer
);
805 pclass
->page_limit
= attr
->values
[0].integer
;
808 if ((attr
= ippFindAttribute(con
->request
, "member-uris", IPP_TAG_URI
)) != NULL
)
811 * Clear the printer array as needed...
814 if (pclass
->num_printers
> 0)
816 free(pclass
->printers
);
817 pclass
->num_printers
= 0;
821 * Add each printer or class that is listed...
824 for (i
= 0; i
< attr
->num_values
; i
++)
827 * Search for the printer or class URI...
830 httpSeparate(attr
->values
[i
].string
.text
, method
, username
, host
,
833 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
839 LogMessage(L_ERROR
, "add_class: resource name \'%s\' no good!", resource
);
840 send_ipp_error(con
, IPP_NOT_FOUND
);
845 * Add it to the class...
848 if (dtype
& CUPS_PRINTER_CLASS
)
849 AddPrinterToClass(pclass
, FindClass(dest
));
851 AddPrinterToClass(pclass
, FindPrinter(dest
));
856 * Update the printer class attributes and return...
859 SetPrinterAttrs(pclass
);
866 LogMessage(L_INFO
, "Class \'%s\' modified by \'%s\'.", pclass
->name
,
870 AddPrinterHistory(pclass
);
872 LogMessage(L_INFO
, "New class \'%s\' added by \'%s\'.", pclass
->name
,
876 con
->response
->request
.status
.status_code
= IPP_OK
;
881 * 'add_file()' - Add a file to a job.
884 static int /* O - 0 on success, -1 on error */
885 add_file(client_t
*con
, /* I - Connection to client */
886 job_t
*job
, /* I - Job to add to */
887 mime_type_t
*filetype
, /* I - Type of file */
888 int compression
) /* I - Compression */
890 mime_type_t
**filetypes
; /* New filetypes array... */
891 int *compressions
; /* New compressions array... */
894 LogMessage(L_DEBUG2
, "add_file(con=%p[%d], job=%d, filetype=%s/%s, compression=%d)\n",
895 con
, con
->http
.fd
, job
->id
, filetype
->super
, filetype
->type
,
899 * Add the file to the job...
902 if (job
->num_files
== 0)
904 compressions
= (int *)malloc(sizeof(int));
905 filetypes
= (mime_type_t
**)malloc(sizeof(mime_type_t
*));
909 compressions
= (int *)realloc(job
->compressions
,
910 (job
->num_files
+ 1) * sizeof(int));
911 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
912 (job
->num_files
+ 1) *
913 sizeof(mime_type_t
*));
916 if (compressions
== NULL
|| filetypes
== NULL
)
918 CancelJob(job
->id
, 1);
919 LogMessage(L_ERROR
, "add_file: unable to allocate memory for file types!");
920 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
924 job
->compressions
= compressions
;
925 job
->compressions
[job
->num_files
] = compression
;
926 job
->filetypes
= filetypes
;
927 job
->filetypes
[job
->num_files
] = filetype
;
936 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
937 * upon the job and printer state...
941 add_job_state_reasons(client_t
*con
, /* I - Client connection */
942 job_t
*job
) /* I - Job info */
944 printer_t
*dest
; /* Destination printer */
947 LogMessage(L_DEBUG2
, "add_job_state_reasons(%p[%d], %d)\n", con
, con
->http
.fd
,
950 switch (job
? job
->state
->values
[0].integer
: IPP_JOB_CANCELLED
)
952 case IPP_JOB_PENDING
:
953 if (job
->dtype
& CUPS_PRINTER_CLASS
)
954 dest
= FindClass(job
->dest
);
956 dest
= FindPrinter(job
->dest
);
958 if (dest
!= NULL
&& dest
->state
== IPP_PRINTER_STOPPED
)
959 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
960 "job-state-reasons", NULL
, "printer-stopped");
962 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
963 "job-state-reasons", NULL
, "none");
967 if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
) != NULL
||
968 ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
) != NULL
)
969 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
970 "job-state-reasons", NULL
, "job-hold-until-specified");
972 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
973 "job-state-reasons", NULL
, "job-incoming");
976 case IPP_JOB_PROCESSING
:
977 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
978 "job-state-reasons", NULL
, "job-printing");
981 case IPP_JOB_STOPPED
:
982 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
983 "job-state-reasons", NULL
, "job-stopped");
986 case IPP_JOB_CANCELLED
:
987 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
988 "job-state-reasons", NULL
, "job-canceled-by-user");
991 case IPP_JOB_ABORTED
:
992 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
993 "job-state-reasons", NULL
, "aborted-by-system");
996 case IPP_JOB_COMPLETED
:
997 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
998 "job-state-reasons", NULL
, "job-completed-successfully");
1005 * 'add_printer()' - Add a printer to the system.
1009 add_printer(client_t
*con
, /* I - Client connection */
1010 ipp_attribute_t
*uri
) /* I - URI of printer */
1012 int i
; /* Looping var */
1013 char method
[HTTP_MAX_URI
],
1014 /* Method portion of URI */
1015 username
[HTTP_MAX_URI
],
1016 /* Username portion of URI */
1018 /* Host portion of URI */
1019 resource
[HTTP_MAX_URI
];
1020 /* Resource portion of URI */
1021 int port
; /* Port portion of URI */
1022 printer_t
*printer
; /* Printer/class */
1023 ipp_attribute_t
*attr
; /* Printer attribute */
1024 cups_file_t
*fp
; /* Script/PPD file */
1025 char line
[1024]; /* Line from file... */
1026 char srcfile
[1024], /* Source Script/PPD file */
1027 dstfile
[1024]; /* Destination Script/PPD file */
1028 int modify
; /* Non-zero if we are modifying */
1031 LogMessage(L_DEBUG2
, "add_printer(%d, %s)\n", con
->http
.fd
,
1032 uri
->values
[0].string
.text
);
1035 * Was this operation called from the correct URI?
1038 if (strncmp(con
->uri
, "/admin/", 7) != 0)
1040 LogMessage(L_ERROR
, "add_printer: admin request on bad resource \'%s\'!",
1042 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1047 * Do we have a valid URI?
1050 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1052 if (strncmp(resource
, "/printers/", 10) != 0 || strlen(resource
) == 10)
1055 * No, return an error...
1058 LogMessage(L_ERROR
, "add_printer: bad printer URI \"%s\"!",
1059 uri
->values
[0].string
.text
);
1060 send_ipp_error(con
, IPP_BAD_REQUEST
);
1065 * See if the printer already exists; if not, create a new printer...
1068 if ((printer
= FindPrinter(resource
+ 10)) == NULL
)
1071 * Printer doesn't exist; see if we have a class of the same name...
1074 if ((printer
= FindClass(resource
+ 10)) != NULL
&&
1075 !(printer
->type
& CUPS_PRINTER_REMOTE
))
1078 * Yes, return an error...
1081 LogMessage(L_ERROR
, "add_printer: \"%s\" already exists as a class!",
1083 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1088 * No, add the printer...
1091 printer
= AddPrinter(resource
+ 10);
1094 else if (printer
->type
& CUPS_PRINTER_IMPLICIT
)
1097 * Rename the implicit printer to "AnyPrinter" or delete it...
1100 if (ImplicitAnyClasses
)
1102 snprintf(printer
->name
, sizeof(printer
->name
), "Any%s", resource
+ 10);
1106 DeletePrinter(printer
, 1);
1109 * Add the printer as a new local printer...
1112 printer
= AddPrinter(resource
+ 10);
1115 else if (printer
->type
& CUPS_PRINTER_REMOTE
)
1118 * Rename the remote printer to "Printer@server"...
1121 DeletePrinterFilters(printer
);
1122 snprintf(printer
->name
, sizeof(printer
->name
), "%s@%s", resource
+ 10,
1124 SetPrinterAttrs(printer
);
1128 * Add the printer as a new local printer...
1131 printer
= AddPrinter(resource
+ 10);
1138 * Look for attributes and copy them over as needed...
1141 if ((attr
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
1142 SetString(&printer
->location
, attr
->values
[0].string
.text
);
1144 if ((attr
= ippFindAttribute(con
->request
, "printer-info", IPP_TAG_TEXT
)) != NULL
)
1145 SetString(&printer
->info
, attr
->values
[0].string
.text
);
1147 if ((attr
= ippFindAttribute(con
->request
, "device-uri", IPP_TAG_URI
)) != NULL
)
1149 ipp_attribute_t
*device
; /* Current device */
1150 int methodlen
; /* Length of method string */
1154 * Do we have a valid device URI?
1157 httpSeparate(attr
->values
[0].string
.text
, method
, username
, host
,
1159 methodlen
= strlen(method
);
1161 if (strcmp(method
, "file") == 0)
1164 * See if the administrator has enabled file devices...
1167 if (!FileDevice
&& strcmp(resource
, "/dev/null"))
1170 * File devices are disabled and the URL is not file:/dev/null...
1173 LogMessage(L_ERROR
, "add_printer: File device URIs have been disabled! "
1174 "To enable, see the FileDevice directive in cupsd.conf.");
1175 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1182 * See if the backend is listed as a device...
1185 for (device
= ippFindAttribute(Devices
, "device-uri", IPP_TAG_URI
);
1187 device
= ippFindNextAttribute(Devices
, "device-uri", IPP_TAG_URI
))
1188 if (strncmp(method
, device
->values
[0].string
.text
, methodlen
) == 0 &&
1189 (device
->values
[0].string
.text
[methodlen
] == ':' ||
1190 device
->values
[0].string
.text
[methodlen
] == '\0'))
1196 * Could not find device in list!
1199 LogMessage(L_ERROR
, "add_printer: bad device-uri attribute \'%s\'!",
1200 attr
->values
[0].string
.text
);
1201 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1206 LogMessage(L_INFO
, "Setting %s device-uri to \"%s\" (was \"%s\".)",
1207 printer
->name
, attr
->values
[0].string
.text
, printer
->device_uri
);
1209 SetString(&printer
->device_uri
, attr
->values
[0].string
.text
);
1212 if ((attr
= ippFindAttribute(con
->request
, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN
)) != NULL
)
1214 LogMessage(L_INFO
, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
1215 printer
->name
, attr
->values
[0].boolean
, printer
->accepting
);
1217 printer
->accepting
= attr
->values
[0].boolean
;
1218 AddPrinterHistory(printer
);
1220 if ((attr
= ippFindAttribute(con
->request
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
1222 if (attr
->values
[0].integer
!= IPP_PRINTER_IDLE
&&
1223 attr
->values
[0].integer
== IPP_PRINTER_STOPPED
)
1225 LogMessage(L_ERROR
, "Attempt to set %s printer-state to bad value %d!",
1226 printer
->name
, attr
->values
[0].integer
);
1227 send_ipp_error(con
, IPP_BAD_REQUEST
);
1231 LogMessage(L_INFO
, "Setting %s printer-state to %d (was %d.)", printer
->name
,
1232 attr
->values
[0].integer
, printer
->state
);
1234 SetPrinterState(printer
, attr
->values
[0].integer
, 0);
1236 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message", IPP_TAG_TEXT
)) != NULL
)
1238 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
1239 sizeof(printer
->state_message
));
1240 AddPrinterHistory(printer
);
1242 if ((attr
= ippFindAttribute(con
->request
, "job-sheets-default", IPP_TAG_ZERO
)) != NULL
&&
1245 SetString(&printer
->job_sheets
[0], attr
->values
[0].string
.text
);
1246 if (attr
->num_values
> 1)
1247 SetString(&printer
->job_sheets
[1], attr
->values
[1].string
.text
);
1249 SetString(&printer
->job_sheets
[1], "none");
1251 if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name-allowed",
1252 IPP_TAG_ZERO
)) != NULL
)
1254 FreePrinterUsers(printer
);
1256 printer
->deny_users
= 0;
1257 if (attr
->value_tag
== IPP_TAG_NAME
&&
1258 (attr
->num_values
> 1 ||
1259 strcmp(attr
->values
[0].string
.text
, "all") != 0))
1260 for (i
= 0; i
< attr
->num_values
; i
++)
1261 AddPrinterUser(printer
, attr
->values
[i
].string
.text
);
1263 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name-denied",
1264 IPP_TAG_ZERO
)) != NULL
)
1266 FreePrinterUsers(printer
);
1268 printer
->deny_users
= 1;
1269 if (attr
->value_tag
== IPP_TAG_NAME
&&
1270 (attr
->num_values
> 1 ||
1271 strcmp(attr
->values
[0].string
.text
, "none") != 0))
1272 for (i
= 0; i
< attr
->num_values
; i
++)
1273 AddPrinterUser(printer
, attr
->values
[i
].string
.text
);
1275 if ((attr
= ippFindAttribute(con
->request
, "job-quota-period",
1276 IPP_TAG_INTEGER
)) != NULL
)
1278 LogMessage(L_DEBUG
, "add_printer: Setting job-quota-period to %d...",
1279 attr
->values
[0].integer
);
1280 FreeQuotas(printer
);
1281 printer
->quota_period
= attr
->values
[0].integer
;
1283 if ((attr
= ippFindAttribute(con
->request
, "job-k-limit",
1284 IPP_TAG_INTEGER
)) != NULL
)
1286 LogMessage(L_DEBUG
, "add_printer: Setting job-k-limit to %d...",
1287 attr
->values
[0].integer
);
1288 FreeQuotas(printer
);
1289 printer
->k_limit
= attr
->values
[0].integer
;
1291 if ((attr
= ippFindAttribute(con
->request
, "job-page-limit",
1292 IPP_TAG_INTEGER
)) != NULL
)
1294 LogMessage(L_DEBUG
, "add_printer: Setting job-page-limit to %d...",
1295 attr
->values
[0].integer
);
1296 FreeQuotas(printer
);
1297 printer
->page_limit
= attr
->values
[0].integer
;
1301 * See if we have all required attributes...
1304 if (!printer
->device_uri
)
1305 SetString(&printer
->device_uri
, "file:/dev/null");
1308 * See if we have an interface script or PPD file attached to the request...
1313 strlcpy(srcfile
, con
->filename
, sizeof(srcfile
));
1315 if ((fp
= cupsFileOpen(srcfile
, "rb")) != NULL
)
1318 * Yes; get the first line from it...
1322 cupsFileGets(fp
, line
, sizeof(line
));
1326 * Then see what kind of file it is...
1329 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
1332 if (strncmp(line
, "*PPD-Adobe", 10) == 0)
1335 * The new file is a PPD file, so remove any old interface script
1336 * that might be lying around...
1344 * This must be an interface script, so move the file over to the
1345 * interfaces directory and make it executable...
1348 if (copy_file(srcfile
, dstfile
))
1350 LogMessage(L_ERROR
, "add_printer: Unable to copy interface script from %s to %s - %s!",
1351 srcfile
, dstfile
, strerror(errno
));
1352 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
1357 LogMessage(L_DEBUG
, "add_printer: Copied interface script successfully!");
1358 chmod(dstfile
, 0755);
1362 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
1365 if (strncmp(line
, "*PPD-Adobe", 10) == 0)
1368 * The new file is a PPD file, so move the file over to the
1369 * ppd directory and make it readable by all...
1372 if (copy_file(srcfile
, dstfile
))
1374 LogMessage(L_ERROR
, "add_printer: Unable to copy PPD file from %s to %s - %s!",
1375 srcfile
, dstfile
, strerror(errno
));
1376 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
1381 LogMessage(L_DEBUG
, "add_printer: Copied PPD file successfully!");
1382 chmod(dstfile
, 0644);
1388 * This must be an interface script, so remove any old PPD file that
1389 * may be lying around...
1396 else if ((attr
= ippFindAttribute(con
->request
, "ppd-name", IPP_TAG_NAME
)) != NULL
)
1398 if (strcmp(attr
->values
[0].string
.text
, "raw") == 0)
1401 * Raw driver, remove any existing PPD or interface script files.
1404 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
1408 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
1418 snprintf(srcfile
, sizeof(srcfile
), "%s/model/%s", DataDir
,
1419 attr
->values
[0].string
.text
);
1421 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
1425 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
1428 if (copy_model(srcfile
, dstfile
))
1430 LogMessage(L_ERROR
, "add_printer: Unable to copy PPD file from %s to %s - %s!",
1431 srcfile
, dstfile
, strerror(errno
));
1432 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
1437 LogMessage(L_DEBUG
, "add_printer: Copied PPD file successfully!");
1438 chmod(dstfile
, 0644);
1444 * Make this printer the default if there is none...
1447 if (DefaultPrinter
== NULL
)
1448 DefaultPrinter
= printer
;
1451 * Update the printer attributes and return...
1454 SetPrinterAttrs(printer
);
1457 if (printer
->job
!= NULL
)
1462 * Stop the current job and then restart it below...
1465 job
= (job_t
*)printer
->job
;
1467 StopJob(job
->id
, 1);
1468 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1476 LogMessage(L_INFO
, "Printer \'%s\' modified by \'%s\'.", printer
->name
,
1480 AddPrinterHistory(printer
);
1482 LogMessage(L_INFO
, "New printer \'%s\' added by \'%s\'.", printer
->name
,
1486 con
->response
->request
.status
.status_code
= IPP_OK
;
1491 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
1492 * based upon the printer state...
1496 add_printer_state_reasons(client_t
*con
, /* I - Client connection */
1497 printer_t
*p
) /* I - Printer info */
1499 LogMessage(L_DEBUG2
, "add_printer_state_reasons(%d, %s)\n", con
->http
.fd
,
1502 if (p
->num_reasons
== 0)
1503 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1504 "printer-state-reasons", NULL
,
1505 p
->state
== IPP_PRINTER_STOPPED
? "paused" : "none");
1507 ippAddStrings(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1508 "printer-state-reasons", p
->num_reasons
, NULL
,
1509 (const char * const *)p
->reasons
);
1514 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
1515 * the specified printer or class.
1519 add_queued_job_count(client_t
*con
, /* I - Client connection */
1520 printer_t
*p
) /* I - Printer or class */
1522 int count
; /* Number of jobs on destination */
1525 LogMessage(L_DEBUG2
, "add_queued_job_count(%d, %s)\n", con
->http
.fd
,
1528 count
= GetPrinterJobCount(p
->name
);
1530 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1531 "queued-job-count", count
);
1536 * 'cancel_all_jobs()' - Cancel all print jobs.
1540 cancel_all_jobs(client_t
*con
, /* I - Client connection */
1541 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
1543 const char *dest
; /* Destination */
1544 cups_ptype_t dtype
; /* Destination type */
1545 char method
[HTTP_MAX_URI
],
1546 /* Method portion of URI */
1547 userpass
[HTTP_MAX_URI
],
1548 /* Username portion of URI */
1550 /* Host portion of URI */
1551 resource
[HTTP_MAX_URI
];
1552 /* Resource portion of URI */
1553 int port
; /* Port portion of URI */
1554 ipp_attribute_t
*attr
; /* Attribute in request */
1555 const char *username
; /* Username */
1556 int purge
; /* Purge? */
1559 LogMessage(L_DEBUG2
, "cancel_all_jobs(%d, %s)\n", con
->http
.fd
,
1560 uri
->values
[0].string
.text
);
1563 * Was this operation called from the correct URI?
1566 if (strncmp(con
->uri
, "/admin/", 7) != 0)
1568 LogMessage(L_ERROR
, "cancel_all_jobs: admin request on bad resource \'%s\'!",
1570 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1575 * See if we have a printer URI...
1578 if (strcmp(uri
->name
, "printer-uri") != 0)
1580 LogMessage(L_ERROR
, "cancel_all_jobs: bad %s attribute \'%s\'!",
1581 uri
->name
, uri
->values
[0].string
.text
);
1582 send_ipp_error(con
, IPP_BAD_REQUEST
);
1587 * Get the username (if any) for the jobs we want to cancel (only if
1588 * "my-jobs" is specified...
1591 if ((attr
= ippFindAttribute(con
->request
, "my-jobs", IPP_TAG_BOOLEAN
)) != NULL
&&
1592 attr
->values
[0].boolean
)
1594 if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1595 username
= attr
->values
[0].string
.text
;
1598 LogMessage(L_ERROR
, "cancel_all_jobs: missing requesting-user-name attribute!");
1599 send_ipp_error(con
, IPP_BAD_REQUEST
);
1607 * Look for the "purge-jobs" attribute...
1610 if ((attr
= ippFindAttribute(con
->request
, "purge-jobs", IPP_TAG_BOOLEAN
)) != NULL
)
1611 purge
= attr
->values
[0].boolean
;
1616 * And if the destination is valid...
1619 httpSeparate(uri
->values
[0].string
.text
, method
, userpass
, host
, &port
,
1622 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
1628 if (strcmp(resource
, "/printers/") != 0)
1630 LogMessage(L_ERROR
, "cancel_all_jobs: resource name \'%s\' no good!", resource
);
1631 send_ipp_error(con
, IPP_NOT_FOUND
);
1636 * Cancel all jobs on all printers...
1639 CancelJobs(NULL
, username
, purge
);
1641 LogMessage(L_INFO
, "All jobs were %s by \'%s\'.",
1642 purge
? "purged" : "cancelled", con
->username
);
1647 * Cancel all of the jobs on the named printer...
1650 CancelJobs(dest
, username
, purge
);
1652 LogMessage(L_INFO
, "All jobs on \'%s\' were %s by \'%s\'.", dest
,
1653 purge
? "purged" : "cancelled", con
->username
);
1656 con
->response
->request
.status
.status_code
= IPP_OK
;
1661 * 'cancel_job()' - Cancel a print job.
1665 cancel_job(client_t
*con
, /* I - Client connection */
1666 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
1668 ipp_attribute_t
*attr
; /* Current attribute */
1669 int jobid
; /* Job ID */
1670 char method
[HTTP_MAX_URI
],
1671 /* Method portion of URI */
1672 username
[HTTP_MAX_URI
],
1673 /* Username portion of URI */
1675 /* Host portion of URI */
1676 resource
[HTTP_MAX_URI
];
1677 /* Resource portion of URI */
1678 int port
; /* Port portion of URI */
1679 job_t
*job
; /* Job information */
1680 const char *dest
; /* Destination */
1681 cups_ptype_t dtype
; /* Destination type (printer or class) */
1682 printer_t
*printer
; /* Printer data */
1685 LogMessage(L_DEBUG2
, "cancel_job(%d, %s)\n", con
->http
.fd
,
1686 uri
->values
[0].string
.text
);
1689 * Verify that the POST operation was done to a valid URI.
1692 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
1693 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
1694 strncmp(con
->uri
, "/printers/", 10) != 0)
1696 LogMessage(L_ERROR
, "cancel_job: cancel request on bad resource \'%s\'!",
1698 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1703 * See if we have a job URI or a printer URI...
1706 if (strcmp(uri
->name
, "printer-uri") == 0)
1709 * Got a printer URI; see if we also have a job-id attribute...
1712 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
1714 LogMessage(L_ERROR
, "cancel_job: got a printer-uri attribute but no job-id!");
1715 send_ipp_error(con
, IPP_BAD_REQUEST
);
1719 if ((jobid
= attr
->values
[0].integer
) == 0)
1722 * Find the current job on the specified printer...
1725 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1727 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
1733 LogMessage(L_ERROR
, "cancel_job: resource name \'%s\' no good!", resource
);
1734 send_ipp_error(con
, IPP_NOT_FOUND
);
1738 if (dtype
& CUPS_PRINTER_CLASS
)
1739 printer
= FindClass(dest
);
1741 printer
= FindPrinter(dest
);
1744 * See if the printer is currently printing a job...
1748 jobid
= ((job_t
*)printer
->job
)->id
;
1752 * No, see if there are any pending jobs...
1755 for (job
= Jobs
; job
!= NULL
; job
= job
->next
)
1756 if (job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
&&
1757 strcasecmp(job
->dest
, dest
) == 0)
1764 LogMessage(L_ERROR
, "cancel_job: No active jobs on %s!", dest
);
1765 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1774 * Got a job URI; parse it to get the job ID...
1777 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1779 if (strncmp(resource
, "/jobs/", 6) != 0)
1785 LogMessage(L_ERROR
, "cancel_job: bad job-uri attribute \'%s\'!",
1786 uri
->values
[0].string
.text
);
1787 send_ipp_error(con
, IPP_BAD_REQUEST
);
1791 jobid
= atoi(resource
+ 6);
1795 * See if the job exists...
1798 if ((job
= FindJob(jobid
)) == NULL
)
1801 * Nope - return a "not found" error...
1804 LogMessage(L_ERROR
, "cancel_job: job #%d doesn't exist!", jobid
);
1805 send_ipp_error(con
, IPP_NOT_FOUND
);
1810 * See if the job is owned by the requesting user...
1813 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
1815 LogMessage(L_ERROR
, "cancel_job: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
1816 username
, jobid
, job
->username
);
1817 send_ipp_error(con
, IPP_FORBIDDEN
);
1822 * See if the job is already completed, cancelled, or aborted; if so,
1823 * we can't cancel...
1826 if (job
->state
->values
[0].integer
>= IPP_JOB_CANCELLED
)
1828 LogMessage(L_ERROR
, "cancel_job: job id %d is %s - can't cancel!",
1830 job
->state
->values
[0].integer
== IPP_JOB_CANCELLED
? "cancelled" :
1831 job
->state
->values
[0].integer
== IPP_JOB_ABORTED
? "aborted" :
1833 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1838 * Cancel the job and return...
1841 CancelJob(jobid
, 0);
1844 LogMessage(L_INFO
, "Job %d was cancelled by \'%s\'.", jobid
, username
);
1846 con
->response
->request
.status
.status_code
= IPP_OK
;
1851 * 'check_quotas()' - Check quotas for a printer and user.
1854 static int /* O - 1 if OK, 0 if not */
1855 check_quotas(client_t
*con
, /* I - Client connection */
1856 printer_t
*p
) /* I - Printer or class */
1858 int i
, j
; /* Looping vars */
1859 ipp_attribute_t
*attr
; /* Current attribute */
1860 char username
[33]; /* Username */
1861 quota_t
*q
; /* Quota data */
1862 struct passwd
*pw
; /* User password data */
1863 struct group
*grp
; /* Group data */
1866 LogMessage(L_DEBUG2
, "check_quotas(%d, %s)\n", con
->http
.fd
, p
->name
);
1872 if (con
== NULL
|| p
== NULL
)
1876 * Figure out who is printing...
1879 attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
);
1881 if (con
->username
[0])
1882 strlcpy(username
, con
->username
, sizeof(username
));
1883 else if (attr
!= NULL
)
1885 LogMessage(L_DEBUG
, "check_quotas: requesting-user-name = \'%s\'",
1886 attr
->values
[0].string
.text
);
1888 strlcpy(username
, attr
->values
[0].string
.text
, sizeof(username
));
1891 strcpy(username
, "anonymous");
1894 * Check global active job limits for printers and users...
1897 if (MaxJobsPerPrinter
)
1900 * Check if there are too many pending jobs on this printer...
1903 if (GetPrinterJobCount(p
->name
) >= MaxJobsPerPrinter
)
1905 LogMessage(L_INFO
, "Too many jobs for printer \"%s\"...", p
->name
);
1913 * Check if there are too many pending jobs for this user...
1916 if (GetUserJobCount(username
) >= MaxJobsPerUser
)
1918 LogMessage(L_INFO
, "Too many jobs for user \"%s\"...", username
);
1924 * Check against users...
1927 if (p
->num_users
== 0 && p
->k_limit
== 0 && p
->page_limit
== 0)
1932 pw
= getpwnam(username
);
1935 for (i
= 0; i
< p
->num_users
; i
++)
1936 if (p
->users
[i
][0] == '@')
1939 * Check group membership...
1942 grp
= getgrnam(p
->users
[i
] + 1);
1948 * Check primary group...
1951 if (pw
&& grp
->gr_gid
== pw
->pw_gid
)
1955 * Check usernames in group...
1958 for (j
= 0; grp
->gr_mem
[j
]; j
++)
1959 if (!strcmp(username
, grp
->gr_mem
[j
]))
1966 else if (!strcasecmp(username
, p
->users
[i
]))
1969 if ((i
< p
->num_users
) == p
->deny_users
)
1971 LogMessage(L_INFO
, "Denying user \"%s\" access to printer \"%s\"...",
1981 if (p
->k_limit
|| p
->page_limit
)
1983 if ((q
= UpdateQuota(p
, username
, 0, 0)) == NULL
)
1985 LogMessage(L_ERROR
, "Unable to allocate quota data for user \"%s\"!",
1990 if ((q
->k_count
>= p
->k_limit
&& p
->k_limit
) ||
1991 (q
->page_count
>= p
->page_limit
&& p
->page_limit
))
1993 LogMessage(L_INFO
, "User \"%s\" is over the quota limit...",
2000 * If we have gotten this far, we're done!
2008 * 'copy_attribute()' - Copy a single attribute.
2011 static ipp_attribute_t
* /* O - New attribute */
2012 copy_attribute(ipp_t
*to
, /* O - Destination request/response */
2013 ipp_attribute_t
*attr
, /* I - Attribute to copy */
2014 int quickcopy
)/* I - Do a quick copy? */
2016 int i
; /* Looping var */
2017 ipp_attribute_t
*toattr
; /* Destination attribute */
2020 LogMessage(L_DEBUG2
, "copy_attribute(%p, %s)\n", to
,
2021 attr
->name
? attr
->name
: "(null)");
2023 switch (attr
->value_tag
& ~IPP_TAG_COPY
)
2026 toattr
= ippAddSeparator(to
);
2029 case IPP_TAG_INTEGER
:
2031 toattr
= ippAddIntegers(to
, attr
->group_tag
, attr
->value_tag
,
2032 attr
->name
, attr
->num_values
, NULL
);
2034 for (i
= 0; i
< attr
->num_values
; i
++)
2035 toattr
->values
[i
].integer
= attr
->values
[i
].integer
;
2038 case IPP_TAG_BOOLEAN
:
2039 toattr
= ippAddBooleans(to
, attr
->group_tag
, attr
->name
,
2040 attr
->num_values
, NULL
);
2042 for (i
= 0; i
< attr
->num_values
; i
++)
2043 toattr
->values
[i
].boolean
= attr
->values
[i
].boolean
;
2046 case IPP_TAG_STRING
:
2049 case IPP_TAG_KEYWORD
:
2051 case IPP_TAG_URISCHEME
:
2052 case IPP_TAG_CHARSET
:
2053 case IPP_TAG_LANGUAGE
:
2054 case IPP_TAG_MIMETYPE
:
2055 toattr
= ippAddStrings(to
, attr
->group_tag
,
2056 (ipp_tag_t
)(attr
->value_tag
| quickcopy
),
2057 attr
->name
, attr
->num_values
, NULL
, NULL
);
2061 for (i
= 0; i
< attr
->num_values
; i
++)
2062 toattr
->values
[i
].string
.text
= attr
->values
[i
].string
.text
;
2066 for (i
= 0; i
< attr
->num_values
; i
++)
2067 toattr
->values
[i
].string
.text
= strdup(attr
->values
[i
].string
.text
);
2072 toattr
= ippAddDate(to
, attr
->group_tag
, attr
->name
,
2073 attr
->values
[0].date
);
2076 case IPP_TAG_RESOLUTION
:
2077 toattr
= ippAddResolutions(to
, attr
->group_tag
, attr
->name
,
2078 attr
->num_values
, IPP_RES_PER_INCH
,
2081 for (i
= 0; i
< attr
->num_values
; i
++)
2083 toattr
->values
[i
].resolution
.xres
= attr
->values
[i
].resolution
.xres
;
2084 toattr
->values
[i
].resolution
.yres
= attr
->values
[i
].resolution
.yres
;
2085 toattr
->values
[i
].resolution
.units
= attr
->values
[i
].resolution
.units
;
2089 case IPP_TAG_RANGE
:
2090 toattr
= ippAddRanges(to
, attr
->group_tag
, attr
->name
,
2091 attr
->num_values
, NULL
, NULL
);
2093 for (i
= 0; i
< attr
->num_values
; i
++)
2095 toattr
->values
[i
].range
.lower
= attr
->values
[i
].range
.lower
;
2096 toattr
->values
[i
].range
.upper
= attr
->values
[i
].range
.upper
;
2100 case IPP_TAG_TEXTLANG
:
2101 case IPP_TAG_NAMELANG
:
2102 toattr
= ippAddStrings(to
, attr
->group_tag
,
2103 (ipp_tag_t
)(attr
->value_tag
| quickcopy
),
2104 attr
->name
, attr
->num_values
, NULL
, NULL
);
2108 for (i
= 0; i
< attr
->num_values
; i
++)
2110 toattr
->values
[i
].string
.charset
= attr
->values
[i
].string
.charset
;
2111 toattr
->values
[i
].string
.text
= attr
->values
[i
].string
.text
;
2116 for (i
= 0; i
< attr
->num_values
; i
++)
2119 toattr
->values
[i
].string
.charset
=
2120 strdup(attr
->values
[i
].string
.charset
);
2122 toattr
->values
[i
].string
.charset
=
2123 toattr
->values
[0].string
.charset
;
2125 toattr
->values
[i
].string
.text
= strdup(attr
->values
[i
].string
.text
);
2130 case IPP_TAG_BEGIN_COLLECTION
:
2131 toattr
= ippAddCollections(to
, attr
->group_tag
, attr
->name
,
2132 attr
->num_values
, NULL
);
2134 for (i
= 0; i
< attr
->num_values
; i
++)
2136 toattr
->values
[i
].collection
= ippNew();
2137 copy_attrs(toattr
->values
[i
].collection
, attr
->values
[i
].collection
,
2138 NULL
, IPP_TAG_ZERO
, 0);
2143 toattr
= ippAddIntegers(to
, attr
->group_tag
, attr
->value_tag
,
2144 attr
->name
, attr
->num_values
, NULL
);
2146 for (i
= 0; i
< attr
->num_values
; i
++)
2148 toattr
->values
[i
].unknown
.length
= attr
->values
[i
].unknown
.length
;
2150 if (toattr
->values
[i
].unknown
.length
> 0)
2152 if ((toattr
->values
[i
].unknown
.data
= malloc(toattr
->values
[i
].unknown
.length
)) == NULL
)
2153 toattr
->values
[i
].unknown
.length
= 0;
2155 memcpy(toattr
->values
[i
].unknown
.data
,
2156 attr
->values
[i
].unknown
.data
,
2157 toattr
->values
[i
].unknown
.length
);
2160 break; /* anti-compiler-warning-code */
2168 * 'copy_attrs()' - Copy attributes from one request to another.
2172 copy_attrs(ipp_t
*to
, /* I - Destination request */
2173 ipp_t
*from
, /* I - Source request */
2174 ipp_attribute_t
*req
, /* I - Requested attributes */
2175 ipp_tag_t group
, /* I - Group to copy */
2176 int quickcopy
) /* I - Do a quick copy? */
2178 int i
; /* Looping var */
2179 ipp_attribute_t
*fromattr
; /* Source attribute */
2182 LogMessage(L_DEBUG2
, "copy_attrs(%p, %p, %p, %x)\n", to
, from
, req
, group
);
2184 if (to
== NULL
|| from
== NULL
)
2187 if (req
!= NULL
&& strcmp(req
->values
[0].string
.text
, "all") == 0)
2188 req
= NULL
; /* "all" means no filter... */
2190 for (fromattr
= from
->attrs
; fromattr
!= NULL
; fromattr
= fromattr
->next
)
2193 * Filter attributes as needed...
2196 if (group
!= IPP_TAG_ZERO
&& fromattr
->group_tag
!= group
&&
2197 fromattr
->group_tag
!= IPP_TAG_ZERO
)
2200 if (req
!= NULL
&& fromattr
->name
!= NULL
)
2202 for (i
= 0; i
< req
->num_values
; i
++)
2203 if (strcmp(fromattr
->name
, req
->values
[i
].string
.text
) == 0)
2206 if (i
== req
->num_values
)
2210 copy_attribute(to
, fromattr
, quickcopy
);
2216 * 'copy_banner()' - Copy a banner file to the requests directory for the
2220 static int /* O - Size of banner file in kbytes */
2221 copy_banner(client_t
*con
, /* I - Client connection */
2222 job_t
*job
, /* I - Job information */
2223 const char *name
) /* I - Name of banner */
2225 int i
; /* Looping var */
2226 int kbytes
; /* Size of banner file in kbytes */
2227 char filename
[1024]; /* Job filename */
2228 banner_t
*banner
; /* Pointer to banner */
2229 cups_file_t
*in
; /* Input file */
2230 cups_file_t
*out
; /* Output file */
2231 int ch
; /* Character from file */
2232 char attrname
[255], /* Name of attribute */
2233 *s
; /* Pointer into name */
2234 ipp_attribute_t
*attr
; /* Attribute */
2237 LogMessage(L_DEBUG2
, "copy_banner(%d, %d, %s)\n", con
->http
.fd
, job
->id
,
2241 * Find the banner; return if not found or "none"...
2244 LogMessage(L_DEBUG
, "copy_banner(%p, %d, \"%s\")", con
, job
->id
,
2245 name
? name
: "(null)");
2248 strcmp(name
, "none") == 0 ||
2249 (banner
= FindBanner(name
)) == NULL
)
2253 * Open the banner and job files...
2256 if (add_file(con
, job
, banner
->filetype
, 0))
2259 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
2261 if ((out
= cupsFileOpen(filename
, "w")) == NULL
)
2263 LogMessage(L_ERROR
, "copy_banner: Unable to create banner job file %s - %s",
2264 filename
, strerror(errno
));
2269 fchmod(cupsFileNumber(out
), 0640);
2270 fchown(cupsFileNumber(out
), getuid(), Group
);
2275 * Try the localized banner file under the subdirectory...
2278 snprintf(filename
, sizeof(filename
), "%s/banners/%s/%s", DataDir
,
2279 con
->language
->language
, name
);
2281 if (access(filename
, 0) && con
->language
->language
[2])
2284 * Wasn't able to find "ll_CC" locale file; try the non-national
2285 * localization banner directory.
2288 attrname
[0] = con
->language
->language
[0];
2289 attrname
[1] = con
->language
->language
[1];
2292 snprintf(filename
, sizeof(filename
), "%s/banners/%s/%s", DataDir
,
2296 if (access(filename
, 0))
2299 * Use the non-localized banner file.
2302 snprintf(filename
, sizeof(filename
), "%s/banners/%s", DataDir
, name
);
2308 * Use the non-localized banner file.
2311 snprintf(filename
, sizeof(filename
), "%s/banners/%s", DataDir
, name
);
2314 if ((in
= cupsFileOpen(filename
, "r")) == NULL
)
2318 LogMessage(L_ERROR
, "copy_banner: Unable to open banner template file %s - %s",
2319 filename
, strerror(errno
));
2325 * Parse the file to the end...
2328 while ((ch
= cupsFileGetChar(in
)) != EOF
)
2332 * Get an attribute name...
2335 for (s
= attrname
; (ch
= cupsFileGetChar(in
)) != EOF
;)
2336 if (!isalpha(ch
) && ch
!= '-' && ch
!= '?')
2338 else if (s
< (attrname
+ sizeof(attrname
) - 1))
2348 * Ignore { followed by stuff that is not an attribute name...
2351 cupsFilePrintf(out
, "{%s%c", attrname
, ch
);
2356 * See if it is defined...
2359 if (attrname
[0] == '?')
2364 if (strcmp(s
, "printer-name") == 0)
2366 cupsFilePuts(out
, job
->dest
);
2369 else if ((attr
= ippFindAttribute(job
->attrs
, s
, IPP_TAG_ZERO
)) == NULL
)
2372 * See if we have a leading question mark...
2375 if (attrname
[0] != '?')
2378 * Nope, write to file as-is; probably a PostScript procedure...
2381 cupsFilePrintf(out
, "{%s}", attrname
);
2388 * Output value(s)...
2391 for (i
= 0; i
< attr
->num_values
; i
++)
2394 cupsFilePutChar(out
, ',');
2396 switch (attr
->value_tag
)
2398 case IPP_TAG_INTEGER
:
2400 if (strncmp(s
, "time-at-", 8) == 0)
2401 cupsFilePuts(out
, GetDateTime(attr
->values
[i
].integer
));
2403 cupsFilePrintf(out
, "%d", attr
->values
[i
].integer
);
2406 case IPP_TAG_BOOLEAN
:
2407 cupsFilePrintf(out
, "%d", attr
->values
[i
].boolean
);
2410 case IPP_TAG_NOVALUE
:
2411 cupsFilePuts(out
, "novalue");
2414 case IPP_TAG_RANGE
:
2415 cupsFilePrintf(out
, "%d-%d", attr
->values
[i
].range
.lower
,
2416 attr
->values
[i
].range
.upper
);
2419 case IPP_TAG_RESOLUTION
:
2420 cupsFilePrintf(out
, "%dx%d%s", attr
->values
[i
].resolution
.xres
,
2421 attr
->values
[i
].resolution
.yres
,
2422 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
2427 case IPP_TAG_STRING
:
2430 case IPP_TAG_KEYWORD
:
2431 case IPP_TAG_CHARSET
:
2432 case IPP_TAG_LANGUAGE
:
2433 if (strcasecmp(banner
->filetype
->type
, "postscript") == 0)
2436 * Need to quote strings for PS banners...
2441 for (p
= attr
->values
[i
].string
.text
; *p
; p
++)
2443 if (*p
== '(' || *p
== ')' || *p
== '\\')
2445 cupsFilePutChar(out
, '\\');
2446 cupsFilePutChar(out
, *p
);
2448 else if (*p
< 32 || *p
> 126)
2449 cupsFilePrintf(out
, "\\%03o", *p
& 255);
2451 cupsFilePutChar(out
, *p
);
2455 cupsFilePuts(out
, attr
->values
[i
].string
.text
);
2459 break; /* anti-compiler-warning-code */
2463 else if (ch
== '\\') /* Quoted char */
2465 ch
= cupsFileGetChar(in
);
2467 if (ch
!= '{') /* Only do special handling for \{ */
2468 cupsFilePutChar(out
, '\\');
2470 cupsFilePutChar(out
, ch
);
2473 cupsFilePutChar(out
, ch
);
2477 kbytes
= (cupsFileTell(out
) + 1023) / 1024;
2479 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
2480 attr
->values
[0].integer
+= kbytes
;
2489 * 'copy_file()' - Copy a PPD file or interface script...
2492 static int /* O - 0 = success, -1 = error */
2493 copy_file(const char *from
, /* I - Source file */
2494 const char *to
) /* I - Destination file */
2496 cups_file_t
*src
, /* Source file */
2497 *dst
; /* Destination file */
2498 int bytes
; /* Bytes to read/write */
2499 char buffer
[2048]; /* Copy buffer */
2502 LogMessage(L_DEBUG2
, "copy_file(\"%s\", \"%s\")\n", from
, to
);
2505 * Open the source and destination file for a copy...
2508 if ((src
= cupsFileOpen(from
, "rb")) == NULL
)
2511 if ((dst
= cupsFileOpen(to
, "wb")) == NULL
)
2518 * Copy the source file to the destination...
2521 while ((bytes
= cupsFileRead(src
, buffer
, sizeof(buffer
))) > 0)
2522 if (cupsFileWrite(dst
, buffer
, bytes
) < bytes
)
2530 * Close both files and return...
2535 return (cupsFileClose(dst
));
2540 * 'copy_model()' - Copy a PPD model file, substituting default values
2544 static int /* O - 0 = success, -1 = error */
2545 copy_model(const char *from
, /* I - Source file */
2546 const char *to
) /* I - Destination file */
2548 cups_file_t
*src
, /* Source file */
2549 *dst
; /* Destination file */
2550 char buffer
[2048]; /* Copy buffer */
2551 int i
; /* Looping var */
2552 char option
[PPD_MAX_NAME
], /* Option name */
2553 choice
[PPD_MAX_NAME
]; /* Choice name */
2554 int num_defaults
; /* Number of default options */
2555 ppd_default_t
*defaults
; /* Default options */
2556 char cups_protocol
[PPD_MAX_LINE
];
2557 /* cupsProtocol attribute */
2558 #ifdef HAVE_LIBPAPER
2559 char *paper_result
; /* Paper size name from libpaper */
2560 char system_paper
[64]; /* Paper size name buffer */
2561 #endif /* HAVE_LIBPAPER */
2564 LogMessage(L_DEBUG2
, "copy_model(\"%s\", \"%s\")\n", from
, to
);
2567 * Open the destination (if possible) and set the default options...
2572 cups_protocol
[0] = '\0';
2574 if ((dst
= cupsFileOpen(to
, "rb")) != NULL
)
2577 * Read all of the default lines from the old PPD...
2580 while (cupsFileGets(dst
, buffer
, sizeof(buffer
)) != NULL
)
2581 if (!strncmp(buffer
, "*Default", 8))
2584 * Add the default option...
2587 if (!ppd_parse_line(buffer
, option
, sizeof(option
),
2588 choice
, sizeof(choice
)))
2589 num_defaults
= ppd_add_default(option
, choice
, num_defaults
,
2592 else if (!strncmp(buffer
, "*cupsProtocol:", 14))
2593 strlcpy(cups_protocol
, buffer
, sizeof(cups_protocol
));
2597 #ifdef HAVE_LIBPAPER
2598 else if ((paper_result
= systempapername()) != NULL
)
2601 * Set the default media sizes from the systemwide default...
2604 strlcpy(system_paper
, paper_result
, sizeof(system_paper
));
2605 system_paper
[0] = toupper(system_paper
[0]);
2607 num_defaults
= ppd_add_default("PageSize", system_paper
,
2608 num_defaults
, &defaults
);
2609 num_defaults
= ppd_add_default("PageRegion", system_paper
,
2610 num_defaults
, &defaults
);
2611 num_defaults
= ppd_add_default("PaperDimension", system_paper
,
2612 num_defaults
, &defaults
);
2613 num_defaults
= ppd_add_default("ImageableArea", system_paper
,
2614 num_defaults
, &defaults
);
2616 #endif /* HAVE_LIBPAPER */
2620 * Add the default media sizes...
2622 * Note: These values are generally not valid for large-format devices
2623 * like plotters, however it is probably safe to say that those
2624 * users will configure the media size after initially adding
2625 * the device anyways...
2628 if (!DefaultLanguage
||
2629 !strcasecmp(DefaultLanguage
, "C") ||
2630 !strcasecmp(DefaultLanguage
, "POSIX") ||
2631 !strcasecmp(DefaultLanguage
, "en") ||
2632 !strncasecmp(DefaultLanguage
, "en_US", 5) ||
2633 !strncasecmp(DefaultLanguage
, "en_CA", 5) ||
2634 !strncasecmp(DefaultLanguage
, "fr_CA", 5))
2637 * These are the only locales that will default to "letter" size...
2640 num_defaults
= ppd_add_default("PageSize", "Letter", num_defaults
,
2642 num_defaults
= ppd_add_default("PageRegion", "Letter", num_defaults
,
2644 num_defaults
= ppd_add_default("PaperDimension", "Letter", num_defaults
,
2646 num_defaults
= ppd_add_default("ImageableArea", "Letter", num_defaults
,
2652 * The rest default to "a4" size...
2655 num_defaults
= ppd_add_default("PageSize", "A4", num_defaults
,
2657 num_defaults
= ppd_add_default("PageRegion", "A4", num_defaults
,
2659 num_defaults
= ppd_add_default("PaperDimension", "A4", num_defaults
,
2661 num_defaults
= ppd_add_default("ImageableArea", "A4", num_defaults
,
2667 * Open the source and destination file for a copy...
2670 if ((src
= cupsFileOpen(from
, "rb")) == NULL
)
2672 if (num_defaults
> 0)
2678 if ((dst
= cupsFileOpen(to
, "wb")) == NULL
)
2680 if (num_defaults
> 0)
2688 * Copy the source file to the destination...
2691 while (cupsFileGets(src
, buffer
, sizeof(buffer
)) != NULL
)
2693 if (!strncmp(buffer
, "*Default", 8))
2696 * Check for an previous default option choice...
2699 if (!ppd_parse_line(buffer
, option
, sizeof(option
),
2700 choice
, sizeof(choice
)))
2702 for (i
= 0; i
< num_defaults
; i
++)
2703 if (!strcmp(option
, defaults
[i
].option
))
2706 * Substitute the previous choice...
2709 snprintf(buffer
, sizeof(buffer
), "*Default%s: %s", option
,
2710 defaults
[i
].choice
);
2716 cupsFilePrintf(dst
, "%s\n", buffer
);
2719 if (cups_protocol
[0])
2720 cupsFilePrintf(dst
, "%s\n", cups_protocol
);
2722 if (num_defaults
> 0)
2726 * Close both files and return...
2731 return (cupsFileClose(dst
));
2736 * 'create_job()' - Print a file to a printer or class.
2740 create_job(client_t
*con
, /* I - Client connection */
2741 ipp_attribute_t
*uri
) /* I - Printer URI */
2743 ipp_attribute_t
*attr
; /* Current attribute */
2744 const char *dest
; /* Destination */
2745 cups_ptype_t dtype
; /* Destination type (printer or class) */
2746 int priority
; /* Job priority */
2747 char *title
; /* Job name/title */
2748 job_t
*job
; /* Current job */
2749 char job_uri
[HTTP_MAX_URI
],
2751 printer_uri
[HTTP_MAX_URI
],
2753 method
[HTTP_MAX_URI
],
2754 /* Method portion of URI */
2755 username
[HTTP_MAX_URI
],
2756 /* Username portion of URI */
2758 /* Host portion of URI */
2759 resource
[HTTP_MAX_URI
];
2760 /* Resource portion of URI */
2761 int port
; /* Port portion of URI */
2762 printer_t
*printer
; /* Printer data */
2763 int kbytes
; /* Size of print file */
2764 int i
; /* Looping var */
2765 int lowerpagerange
; /* Page range bound */
2768 LogMessage(L_DEBUG2
, "create_job(%d, %s)\n", con
->http
.fd
,
2769 uri
->values
[0].string
.text
);
2772 * Verify that the POST operation was done to a valid URI.
2775 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
2776 strncmp(con
->uri
, "/printers/", 10) != 0)
2778 LogMessage(L_ERROR
, "create_job: cancel request on bad resource \'%s\'!",
2780 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
2785 * Is the destination valid?
2788 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
2790 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
2796 LogMessage(L_ERROR
, "create_job: resource name \'%s\' no good!", resource
);
2797 send_ipp_error(con
, IPP_NOT_FOUND
);
2802 * See if the printer is accepting jobs...
2805 if (dtype
& CUPS_PRINTER_CLASS
)
2807 printer
= FindClass(dest
);
2810 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
2811 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/classes/%s",
2812 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), dest
);
2814 #endif /* AF_INET6 */
2815 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/classes/%s",
2816 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), dest
);
2820 printer
= FindPrinter(dest
);
2823 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
2824 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/printers/%s",
2825 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), dest
);
2827 #endif /* AF_INET6 */
2828 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/printers/%s",
2829 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), dest
);
2832 if (!printer
->accepting
)
2834 LogMessage(L_INFO
, "create_job: destination \'%s\' is not accepting jobs.",
2836 send_ipp_error(con
, IPP_NOT_ACCEPTING
);
2841 * Validate job template attributes; for now just copies and page-ranges...
2844 if ((attr
= ippFindAttribute(con
->request
, "copies", IPP_TAG_INTEGER
)) != NULL
)
2846 if (attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> MaxCopies
)
2848 LogMessage(L_INFO
, "create_job: bad copies value %d.",
2849 attr
->values
[0].integer
);
2850 send_ipp_error(con
, IPP_BAD_REQUEST
);
2855 if ((attr
= ippFindAttribute(con
->request
, "page-ranges", IPP_TAG_RANGE
)) != NULL
)
2857 for (i
= 0, lowerpagerange
= 1; i
< attr
->num_values
; i
++)
2859 if (attr
->values
[i
].range
.lower
< lowerpagerange
||
2860 attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
2862 LogMessage(L_ERROR
, "create_job: bad page-ranges values %d-%d.",
2863 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
2864 send_ipp_error(con
, IPP_BAD_REQUEST
);
2868 lowerpagerange
= attr
->values
[i
].range
.upper
+ 1;
2873 * Make sure we aren't over our limit...
2876 if (NumJobs
>= MaxJobs
&& MaxJobs
)
2879 if (NumJobs
>= MaxJobs
&& MaxJobs
)
2881 LogMessage(L_INFO
, "create_job: too many jobs.");
2882 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
2886 if (!check_quotas(con
, printer
))
2888 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
2893 * Set all but the first two attributes to the job attributes group...
2896 for (attr
= con
->request
->attrs
->next
->next
; attr
; attr
= attr
->next
)
2897 attr
->group_tag
= IPP_TAG_JOB
;
2900 * Create the job and set things up...
2903 if ((attr
= ippFindAttribute(con
->request
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
2904 priority
= attr
->values
[0].integer
;
2906 ippAddInteger(con
->request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
2909 if ((attr
= ippFindAttribute(con
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
2910 title
= attr
->values
[0].string
.text
;
2912 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
2913 title
= "Untitled");
2915 if ((job
= AddJob(priority
, printer
->name
)) == NULL
)
2917 LogMessage(L_ERROR
, "create_job: unable to add job for destination \'%s\'!",
2919 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
2924 job
->attrs
= con
->request
;
2925 con
->request
= NULL
;
2927 attr
= ippFindAttribute(job
->attrs
, "requesting-user-name", IPP_TAG_NAME
);
2929 if (con
->username
[0])
2930 SetString(&job
->username
, con
->username
);
2931 else if (attr
!= NULL
)
2933 LogMessage(L_DEBUG
, "create_job: requesting-user-name = \'%s\'",
2934 attr
->values
[0].string
.text
);
2936 SetString(&job
->username
, attr
->values
[0].string
.text
);
2939 SetString(&job
->username
, "anonymous");
2942 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name",
2943 NULL
, job
->username
);
2946 attr
->group_tag
= IPP_TAG_JOB
;
2947 SetString(&attr
->name
, "job-originating-user-name");
2950 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-host-name",
2951 IPP_TAG_ZERO
)) != NULL
)
2954 * Request contains a job-originating-host-name attribute; validate it...
2957 if (attr
->value_tag
!= IPP_TAG_NAME
||
2958 attr
->num_values
!= 1 ||
2959 strcmp(con
->http
.hostname
, "localhost") != 0)
2962 * Can't override the value if we aren't connected via localhost.
2963 * Also, we can only have 1 value and it must be a name value.
2966 int i
; /* Looping var */
2968 switch (attr
->value_tag
)
2970 case IPP_TAG_STRING
:
2971 case IPP_TAG_TEXTLANG
:
2972 case IPP_TAG_NAMELANG
:
2975 case IPP_TAG_KEYWORD
:
2977 case IPP_TAG_URISCHEME
:
2978 case IPP_TAG_CHARSET
:
2979 case IPP_TAG_LANGUAGE
:
2980 case IPP_TAG_MIMETYPE
:
2982 * Free old strings...
2985 for (i
= 0; i
< attr
->num_values
; i
++)
2987 free(attr
->values
[i
].string
.text
);
2988 attr
->values
[i
].string
.text
= NULL
;
2989 if (attr
->values
[i
].string
.charset
)
2991 free(attr
->values
[i
].string
.charset
);
2992 attr
->values
[i
].string
.charset
= NULL
;
3001 * Use the default connection hostname instead...
3004 attr
->value_tag
= IPP_TAG_NAME
;
3005 attr
->num_values
= 1;
3006 attr
->values
[0].string
.text
= strdup(con
->http
.hostname
);
3009 attr
->group_tag
= IPP_TAG_JOB
;
3014 * No job-originating-host-name attribute, so use the hostname from
3018 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
,
3019 "job-originating-host-name", NULL
, con
->http
.hostname
);
3022 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
3024 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3025 "time-at-processing", 0);
3026 attr
->value_tag
= IPP_TAG_NOVALUE
;
3027 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3028 "time-at-completed", 0);
3029 attr
->value_tag
= IPP_TAG_NOVALUE
;
3032 * Add remaining job attributes...
3035 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
3036 job
->state
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
3037 "job-state", IPP_JOB_STOPPED
);
3038 job
->sheets
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3039 "job-media-sheets-completed", 0);
3040 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
3042 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
3045 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
3046 attr
->values
[0].integer
= 0;
3048 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3051 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
3052 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
3054 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
3055 "job-hold-until", NULL
, "no-hold");
3056 if (attr
!= NULL
&& strcmp(attr
->values
[0].string
.text
, "no-hold") != 0 &&
3057 !(printer
->type
& CUPS_PRINTER_REMOTE
))
3060 * Hold job until specified time...
3063 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
3066 job
->hold_until
= time(NULL
) + 60;
3068 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
3070 if (!(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)) ||
3074 * Add job sheets options...
3077 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) == NULL
)
3079 LogMessage(L_DEBUG
, "Adding default job-sheets values \"%s,%s\"...",
3080 printer
->job_sheets
[0], printer
->job_sheets
[1]);
3082 attr
= ippAddStrings(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-sheets",
3084 attr
->values
[0].string
.text
= strdup(printer
->job_sheets
[0]);
3085 attr
->values
[1].string
.text
= strdup(printer
->job_sheets
[1]);
3088 job
->job_sheets
= attr
;
3091 * Enforce classification level if set...
3096 if (ClassifyOverride
)
3098 if (strcmp(attr
->values
[0].string
.text
, "none") == 0 &&
3099 (attr
->num_values
== 1 ||
3100 strcmp(attr
->values
[1].string
.text
, "none") == 0))
3103 * Force the leading banner to have the classification on it...
3106 SetString(&attr
->values
[0].string
.text
, Classification
);
3108 LogMessage(L_NOTICE
, "[Job %d] CLASSIFICATION FORCED "
3109 "job-sheets=\"%s,none\", "
3110 "job-originating-user-name=\"%s\"",
3111 job
->id
, Classification
,
3114 else if (attr
->num_values
== 2 &&
3115 strcmp(attr
->values
[0].string
.text
, attr
->values
[1].string
.text
) != 0 &&
3116 strcmp(attr
->values
[0].string
.text
, "none") != 0 &&
3117 strcmp(attr
->values
[1].string
.text
, "none") != 0)
3120 * Can't put two different security markings on the same document!
3123 SetString(&attr
->values
[1].string
.text
, attr
->values
[0].string
.text
);
3125 LogMessage(L_NOTICE
, "[Job %d] CLASSIFICATION FORCED "
3126 "job-sheets=\"%s,%s\", "
3127 "job-originating-user-name=\"%s\"",
3128 job
->id
, attr
->values
[0].string
.text
,
3129 attr
->values
[1].string
.text
,
3132 else if (strcmp(attr
->values
[0].string
.text
, Classification
) &&
3133 strcmp(attr
->values
[0].string
.text
, "none") &&
3134 (attr
->num_values
== 1 ||
3135 (strcmp(attr
->values
[1].string
.text
, Classification
) &&
3136 strcmp(attr
->values
[1].string
.text
, "none"))))
3138 if (attr
->num_values
== 1)
3139 LogMessage(L_NOTICE
, "[Job %d] CLASSIFICATION OVERRIDDEN "
3140 "job-sheets=\"%s\", "
3141 "job-originating-user-name=\"%s\"",
3142 job
->id
, attr
->values
[0].string
.text
,
3145 LogMessage(L_NOTICE
, "[Job %d] CLASSIFICATION OVERRIDDEN "
3146 "job-sheets=\"%s,%s\", "
3147 "job-originating-user-name=\"%s\"",
3148 job
->id
, attr
->values
[0].string
.text
,
3149 attr
->values
[1].string
.text
,
3153 else if (strcmp(attr
->values
[0].string
.text
, Classification
) != 0 &&
3154 (attr
->num_values
== 1 ||
3155 strcmp(attr
->values
[1].string
.text
, Classification
) != 0))
3158 * Force the banner to have the classification on it...
3161 if (attr
->num_values
== 1 || strcmp(attr
->values
[0].string
.text
, "none"))
3162 SetString(&attr
->values
[0].string
.text
, Classification
);
3164 if (attr
->num_values
> 1 && strcmp(attr
->values
[1].string
.text
, "none"))
3165 SetString(&attr
->values
[1].string
.text
, Classification
);
3167 if (attr
->num_values
> 1)
3168 LogMessage(L_NOTICE
, "[Job %d] CLASSIFICATION FORCED "
3169 "job-sheets=\"%s,%s\", "
3170 "job-originating-user-name=\"%s\"",
3171 job
->id
, attr
->values
[0].string
.text
,
3172 attr
->values
[1].string
.text
,
3175 LogMessage(L_NOTICE
, "[Job %d] CLASSIFICATION FORCED "
3176 "job-sheets=\"%s\", "
3177 "job-originating-user-name=\"%s\"",
3178 job
->id
, Classification
,
3184 * See if we need to add the starting sheet...
3187 if (!(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)))
3189 LogMessage(L_INFO
, "Adding start banner page \"%s\" to job %d.",
3190 attr
->values
[0].string
.text
, job
->id
);
3192 kbytes
= copy_banner(con
, job
, attr
->values
[0].string
.text
);
3194 UpdateQuota(printer
, job
->username
, 0, kbytes
);
3197 else if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
3201 * Save and log the job...
3206 LogMessage(L_INFO
, "Job %d created on \'%s\' by \'%s\'.", job
->id
,
3207 job
->dest
, job
->username
);
3210 * Fill in the response info...
3214 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
3215 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
3216 ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), job
->id
);
3218 #endif /* AF_INET6 */
3219 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
3220 ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), job
->id
);
3222 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, job_uri
);
3224 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
3226 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
3227 job
->state
->values
[0].integer
);
3229 con
->response
->request
.status
.status_code
= IPP_OK
;
3234 * 'delete_printer()' - Remove a printer or class from the system.
3238 delete_printer(client_t
*con
, /* I - Client connection */
3239 ipp_attribute_t
*uri
) /* I - URI of printer or class */
3241 const char *dest
; /* Destination */
3242 cups_ptype_t dtype
; /* Destination type (printer or class) */
3243 char method
[HTTP_MAX_URI
],
3244 /* Method portion of URI */
3245 username
[HTTP_MAX_URI
],
3246 /* Username portion of URI */
3248 /* Host portion of URI */
3249 resource
[HTTP_MAX_URI
];
3250 /* Resource portion of URI */
3251 int port
; /* Port portion of URI */
3252 printer_t
*printer
; /* Printer/class */
3253 char filename
[1024]; /* Script/PPD filename */
3256 LogMessage(L_DEBUG2
, "delete_printer(%d, %s)\n", con
->http
.fd
,
3257 uri
->values
[0].string
.text
);
3260 * Was this operation called from the correct URI?
3263 if (strncmp(con
->uri
, "/admin/", 7) != 0)
3265 LogMessage(L_ERROR
, "delete_printer: admin request on bad resource \'%s\'!",
3267 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
3271 DEBUG_printf(("delete_printer(%08x, %08x)\n", con
, uri
));
3274 * Do we have a valid URI?
3277 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3279 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
3285 LogMessage(L_ERROR
, "delete_printer: resource name \'%s\' no good!", resource
);
3286 send_ipp_error(con
, IPP_NOT_FOUND
);
3291 * Find the printer or class and delete it...
3294 if (dtype
& CUPS_PRINTER_CLASS
)
3295 printer
= FindClass(dest
);
3297 printer
= FindPrinter(dest
);
3300 * Remove old jobs...
3303 CancelJobs(dest
, NULL
, 1);
3306 * Remove any old PPD or script files...
3309 snprintf(filename
, sizeof(filename
), "%s/interfaces/%s", ServerRoot
, dest
);
3312 snprintf(filename
, sizeof(filename
), "%s/ppd/%s.ppd", ServerRoot
, dest
);
3315 if (dtype
& CUPS_PRINTER_CLASS
)
3317 LogMessage(L_INFO
, "Class \'%s\' deleted by \'%s\'.", dest
,
3320 DeletePrinter(printer
, 0);
3325 LogMessage(L_INFO
, "Printer \'%s\' deleted by \'%s\'.", dest
,
3328 DeletePrinter(printer
, 0);
3333 * Return with no errors...
3336 con
->response
->request
.status
.status_code
= IPP_OK
;
3341 * 'get_default()' - Get the default destination.
3345 get_default(client_t
*con
) /* I - Client connection */
3347 int i
; /* Looping var */
3348 ipp_attribute_t
*requested
, /* requested-attributes */
3349 *history
; /* History collection */
3350 int need_history
; /* Need to send history collection? */
3353 LogMessage(L_DEBUG2
, "get_default(%p[%d])\n", con
, con
->http
.fd
);
3355 if (DefaultPrinter
!= NULL
)
3357 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3360 copy_attrs(con
->response
, DefaultPrinter
->attrs
, requested
, IPP_TAG_ZERO
, 0);
3361 copy_attrs(con
->response
, CommonData
, requested
, IPP_TAG_ZERO
, IPP_TAG_COPY
);
3365 if (MaxPrinterHistory
> 0 && DefaultPrinter
->num_history
> 0 && requested
)
3367 for (i
= 0; i
< requested
->num_values
; i
++)
3368 if (!strcmp(requested
->values
[i
].string
.text
, "all") ||
3369 !strcmp(requested
->values
[i
].string
.text
, "printer-state-history"))
3378 history
= ippAddCollections(con
->response
, IPP_TAG_PRINTER
,
3379 "printer-state-history",
3380 DefaultPrinter
->num_history
, NULL
);
3382 for (i
= 0; i
< DefaultPrinter
->num_history
; i
++)
3383 copy_attrs(history
->values
[i
].collection
= ippNew(),
3384 DefaultPrinter
->history
[i
],
3385 NULL
, IPP_TAG_ZERO
, 0);
3388 con
->response
->request
.status
.status_code
= requested
? IPP_OK_SUBST
: IPP_OK
;
3391 con
->response
->request
.status
.status_code
= IPP_NOT_FOUND
;
3396 * 'get_devices()' - Get the list of available devices on the local system.
3400 get_devices(client_t
*con
) /* I - Client connection */
3402 LogMessage(L_DEBUG2
, "get_devices(%d)\n", con
->http
.fd
);
3405 * Copy the device attributes to the response using the requested-attributes
3406 * attribute that may be provided by the client.
3409 copy_attrs(con
->response
, Devices
,
3410 ippFindAttribute(con
->request
, "requested-attributes",
3411 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
, IPP_TAG_COPY
);
3413 con
->response
->request
.status
.status_code
= IPP_OK
;
3418 * 'get_jobs()' - Get a list of jobs for the specified printer.
3422 get_jobs(client_t
*con
, /* I - Client connection */
3423 ipp_attribute_t
*uri
) /* I - Printer URI */
3425 ipp_attribute_t
*attr
, /* Current attribute */
3426 *requested
; /* Requested attributes */
3427 const char *dest
; /* Destination */
3428 cups_ptype_t dtype
; /* Destination type (printer or class) */
3429 cups_ptype_t dmask
; /* Destination type mask */
3430 char method
[HTTP_MAX_URI
],
3431 /* Method portion of URI */
3432 username
[HTTP_MAX_URI
],
3433 /* Username portion of URI */
3435 /* Host portion of URI */
3436 resource
[HTTP_MAX_URI
];
3437 /* Resource portion of URI */
3438 int port
; /* Port portion of URI */
3439 int completed
; /* Completed jobs? */
3440 int limit
; /* Maximum number of jobs to return */
3441 int count
; /* Number of jobs that match */
3442 job_t
*job
; /* Current job pointer */
3443 char job_uri
[HTTP_MAX_URI
];
3447 LogMessage(L_DEBUG2
, "get_jobs(%d, %s)\n", con
->http
.fd
,
3448 uri
->values
[0].string
.text
);
3451 * Is the destination valid?
3454 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3456 if (strcmp(resource
, "/") == 0 ||
3457 (strncmp(resource
, "/jobs", 5) == 0 && strlen(resource
) <= 6))
3460 dtype
= (cups_ptype_t
)0;
3461 dmask
= (cups_ptype_t
)0;
3463 else if (strncmp(resource
, "/printers", 9) == 0 && strlen(resource
) <= 10)
3466 dtype
= (cups_ptype_t
)0;
3467 dmask
= CUPS_PRINTER_CLASS
;
3469 else if (strncmp(resource
, "/classes", 8) == 0 && strlen(resource
) <= 9)
3472 dtype
= CUPS_PRINTER_CLASS
;
3473 dmask
= CUPS_PRINTER_CLASS
;
3475 else if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
3481 LogMessage(L_ERROR
, "get_jobs: resource name \'%s\' no good!", resource
);
3482 send_ipp_error(con
, IPP_NOT_FOUND
);
3486 dmask
= CUPS_PRINTER_CLASS
;
3489 * See if the "which-jobs" attribute have been specified...
3492 if ((attr
= ippFindAttribute(con
->request
, "which-jobs", IPP_TAG_KEYWORD
)) != NULL
&&
3493 strcmp(attr
->values
[0].string
.text
, "completed") == 0)
3499 * See if they want to limit the number of jobs reported...
3502 if ((attr
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
)) != NULL
)
3503 limit
= attr
->values
[0].integer
;
3508 * See if we only want to see jobs for a specific user...
3511 if ((attr
= ippFindAttribute(con
->request
, "my-jobs", IPP_TAG_BOOLEAN
)) != NULL
&&
3512 attr
->values
[0].boolean
)
3514 if (con
->username
[0])
3515 strlcpy(username
, con
->username
, sizeof(username
));
3516 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
3517 strlcpy(username
, attr
->values
[0].string
.text
, sizeof(username
));
3519 strcpy(username
, "anonymous");
3524 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3528 * OK, build a list of jobs for this printer...
3531 for (count
= 0, job
= Jobs
; count
< limit
&& job
!= NULL
; job
= job
->next
)
3534 * Filter out jobs that don't match...
3537 DEBUG_printf(("get_jobs: job->id = %d\n", job
->id
));
3539 if ((dest
!= NULL
&& strcmp(job
->dest
, dest
) != 0) &&
3540 (job
->printer
== NULL
|| dest
== NULL
||
3541 strcmp(job
->printer
->name
, dest
) != 0))
3543 if ((job
->dtype
& dmask
) != dtype
&&
3544 (job
->printer
== NULL
|| (job
->printer
->type
& dmask
) != dtype
))
3546 if (username
[0] != '\0' && strcmp(username
, job
->username
) != 0)
3549 if (completed
&& job
->state
->values
[0].integer
<= IPP_JOB_STOPPED
)
3551 if (!completed
&& job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
3556 DEBUG_printf(("get_jobs: count = %d\n", count
));
3559 * Send the requested attributes for each job...
3563 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
3564 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
3565 ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), job
->id
);
3567 #endif /* AF_INET6 */
3568 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
3569 ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), job
->id
);
3571 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
3572 "job-more-info", NULL
, job_uri
);
3574 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
3575 "job-uri", NULL
, job_uri
);
3577 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3578 "job-printer-up-time", time(NULL
));
3581 * Copy the job attributes to the response using the requested-attributes
3582 * attribute that may be provided by the client.
3585 copy_attrs(con
->response
, job
->attrs
, requested
, IPP_TAG_JOB
, 0);
3587 add_job_state_reasons(con
, job
);
3589 ippAddSeparator(con
->response
);
3592 if (requested
!= NULL
)
3593 con
->response
->request
.status
.status_code
= IPP_OK_SUBST
;
3595 con
->response
->request
.status
.status_code
= IPP_OK
;
3600 * 'get_job_attrs()' - Get job attributes.
3604 get_job_attrs(client_t
*con
, /* I - Client connection */
3605 ipp_attribute_t
*uri
) /* I - Job URI */
3607 ipp_attribute_t
*attr
, /* Current attribute */
3608 *requested
; /* Requested attributes */
3609 int jobid
; /* Job ID */
3610 job_t
*job
; /* Current job */
3611 char method
[HTTP_MAX_URI
],
3612 /* Method portion of URI */
3613 username
[HTTP_MAX_URI
],
3614 /* Username portion of URI */
3616 /* Host portion of URI */
3617 resource
[HTTP_MAX_URI
];
3618 /* Resource portion of URI */
3619 int port
; /* Port portion of URI */
3620 char job_uri
[HTTP_MAX_URI
];
3624 LogMessage(L_DEBUG2
, "get_job_attrs(%d, %s)\n", con
->http
.fd
,
3625 uri
->values
[0].string
.text
);
3628 * See if we have a job URI or a printer URI...
3631 if (strcmp(uri
->name
, "printer-uri") == 0)
3634 * Got a printer URI; see if we also have a job-id attribute...
3637 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
3639 LogMessage(L_ERROR
, "get_job_attrs: got a printer-uri attribute but no job-id!");
3640 send_ipp_error(con
, IPP_BAD_REQUEST
);
3644 jobid
= attr
->values
[0].integer
;
3649 * Got a job URI; parse it to get the job ID...
3652 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3654 if (strncmp(resource
, "/jobs/", 6) != 0)
3660 LogMessage(L_ERROR
, "get_job_attrs: bad job-uri attribute \'%s\'!\n",
3661 uri
->values
[0].string
.text
);
3662 send_ipp_error(con
, IPP_BAD_REQUEST
);
3666 jobid
= atoi(resource
+ 6);
3670 * See if the job exists...
3673 if ((job
= FindJob(jobid
)) == NULL
)
3676 * Nope - return a "not found" error...
3679 LogMessage(L_ERROR
, "get_job_attrs: job #%d doesn't exist!", jobid
);
3680 send_ipp_error(con
, IPP_NOT_FOUND
);
3685 * Put out the standard attributes...
3689 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
3690 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d",
3691 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
),
3694 #endif /* AF_INET6 */
3695 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d",
3696 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
),
3699 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
3701 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
3702 "job-more-info", NULL
, job_uri
);
3704 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
3705 "job-uri", NULL
, job_uri
);
3707 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3708 "job-printer-up-time", time(NULL
));
3711 * Copy the job attributes to the response using the requested-attributes
3712 * attribute that may be provided by the client.
3715 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3718 copy_attrs(con
->response
, job
->attrs
, requested
, IPP_TAG_JOB
, 0);
3720 add_job_state_reasons(con
, job
);
3722 if (requested
!= NULL
)
3723 con
->response
->request
.status
.status_code
= IPP_OK_SUBST
;
3725 con
->response
->request
.status
.status_code
= IPP_OK
;
3730 * 'get_ppds()' - Get the list of PPD files on the local system.
3734 get_ppds(client_t
*con
) /* I - Client connection */
3736 LogMessage(L_DEBUG2
, "get_ppds(%d)\n", con
->http
.fd
);
3739 * Copy the PPD attributes to the response using the requested-attributes
3740 * attribute that may be provided by the client.
3743 copy_attrs(con
->response
, PPDs
,
3744 ippFindAttribute(con
->request
, "requested-attributes",
3745 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
, IPP_TAG_COPY
);
3747 con
->response
->request
.status
.status_code
= IPP_OK
;
3752 * 'get_printer_attrs()' - Get printer attributes.
3756 get_printer_attrs(client_t
*con
, /* I - Client connection */
3757 ipp_attribute_t
*uri
) /* I - Printer URI */
3759 const char *dest
; /* Destination */
3760 cups_ptype_t dtype
; /* Destination type (printer or class) */
3761 char method
[HTTP_MAX_URI
],
3762 /* Method portion of URI */
3763 username
[HTTP_MAX_URI
],
3764 /* Username portion of URI */
3766 /* Host portion of URI */
3767 resource
[HTTP_MAX_URI
];
3768 /* Resource portion of URI */
3769 int port
; /* Port portion of URI */
3770 printer_t
*printer
; /* Printer/class */
3771 time_t curtime
; /* Current time */
3772 int i
; /* Looping var */
3773 ipp_attribute_t
*requested
, /* requested-attributes */
3774 *history
; /* History collection */
3775 int need_history
; /* Need to send history collection? */
3778 LogMessage(L_DEBUG2
, "get_printer_attrs(%d, %s)\n", con
->http
.fd
,
3779 uri
->values
[0].string
.text
);
3782 * Is the destination valid?
3785 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3787 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
3793 LogMessage(L_ERROR
, "get_printer_attrs: resource name \'%s\' no good!", resource
);
3794 send_ipp_error(con
, IPP_NOT_FOUND
);
3798 if (dtype
& CUPS_PRINTER_CLASS
)
3799 printer
= FindClass(dest
);
3801 printer
= FindPrinter(dest
);
3803 curtime
= time(NULL
);
3806 * Copy the printer attributes to the response using requested-attributes
3807 * and document-format attributes that may be provided by the client.
3810 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
3813 add_printer_state_reasons(con
, printer
);
3815 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
3816 "printer-state-message", NULL
, printer
->state_message
);
3818 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
3819 printer
->accepting
);
3821 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3822 "printer-up-time", curtime
);
3823 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3824 "printer-state-time", printer
->state_time
);
3825 ippAddDate(con
->response
, IPP_TAG_PRINTER
, "printer-current-time",
3826 ippTimeToDate(curtime
));
3828 add_queued_job_count(con
, printer
);
3830 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3833 copy_attrs(con
->response
, printer
->attrs
, requested
, IPP_TAG_ZERO
, 0);
3834 copy_attrs(con
->response
, CommonData
, requested
, IPP_TAG_ZERO
, IPP_TAG_COPY
);
3838 if (MaxPrinterHistory
> 0 && printer
->num_history
> 0 && requested
)
3840 for (i
= 0; i
< requested
->num_values
; i
++)
3841 if (!strcmp(requested
->values
[i
].string
.text
, "all") ||
3842 !strcmp(requested
->values
[i
].string
.text
, "printer-state-history"))
3851 history
= ippAddCollections(con
->response
, IPP_TAG_PRINTER
,
3852 "printer-state-history",
3853 printer
->num_history
, NULL
);
3855 for (i
= 0; i
< printer
->num_history
; i
++)
3856 copy_attrs(history
->values
[i
].collection
= ippNew(), printer
->history
[i
],
3857 NULL
, IPP_TAG_ZERO
, 0);
3860 con
->response
->request
.status
.status_code
= requested
? IPP_OK_SUBST
: IPP_OK
;
3865 * 'get_printers()' - Get a list of printers or classes.
3869 get_printers(client_t
*con
, /* I - Client connection */
3870 int type
) /* I - 0 or CUPS_PRINTER_CLASS */
3872 int i
; /* Looping var */
3873 ipp_attribute_t
*requested
, /* requested-attributes */
3874 *history
, /* History collection */
3875 *attr
; /* Current attribute */
3876 int need_history
; /* Need to send history collection? */
3877 int limit
; /* Maximum number of printers to return */
3878 int count
; /* Number of printers that match */
3879 printer_t
*printer
; /* Current printer pointer */
3880 time_t curtime
; /* Current time */
3881 int printer_type
, /* printer-type attribute */
3882 printer_mask
; /* printer-type-mask attribute */
3883 char *location
; /* Location string */
3884 char name
[IPP_MAX_NAME
],
3886 *nameptr
; /* Pointer into name */
3887 printer_t
*iclass
; /* Implicit class */
3890 LogMessage(L_DEBUG2
, "get_printers(%d, %x)\n", con
->http
.fd
, type
);
3893 * See if they want to limit the number of printers reported...
3896 if ((attr
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
)) != NULL
)
3897 limit
= attr
->values
[0].integer
;
3902 * Support filtering...
3905 if ((attr
= ippFindAttribute(con
->request
, "printer-type", IPP_TAG_ENUM
)) != NULL
)
3906 printer_type
= attr
->values
[0].integer
;
3910 if ((attr
= ippFindAttribute(con
->request
, "printer-type-mask", IPP_TAG_ENUM
)) != NULL
)
3911 printer_mask
= attr
->values
[0].integer
;
3915 if ((attr
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
3916 location
= attr
->values
[0].string
.text
;
3920 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3925 if (MaxPrinterHistory
> 0 && requested
)
3927 for (i
= 0; i
< requested
->num_values
; i
++)
3928 if (!strcmp(requested
->values
[i
].string
.text
, "all") ||
3929 !strcmp(requested
->values
[i
].string
.text
, "printer-state-history"))
3937 * OK, build a list of printers for this printer...
3940 curtime
= time(NULL
);
3942 for (count
= 0, printer
= Printers
;
3943 count
< limit
&& printer
!= NULL
;
3944 printer
= printer
->next
)
3945 if ((printer
->type
& CUPS_PRINTER_CLASS
) == type
&&
3946 (printer
->type
& printer_mask
) == printer_type
&&
3947 (location
== NULL
|| printer
->location
== NULL
||
3948 strcasecmp(printer
->location
, location
) == 0))
3951 * If HideImplicitMembers is enabled, see if this printer or class
3952 * is a member of an implicit class...
3955 if (ImplicitClasses
&& HideImplicitMembers
&&
3956 (printer
->type
& CUPS_PRINTER_REMOTE
))
3959 * Make a copy of the printer name...
3962 strlcpy(name
, printer
->name
, sizeof(name
));
3964 if ((nameptr
= strchr(name
, '@')) != NULL
)
3967 * Strip trailing @server...
3973 * Find the core printer, if any...
3976 if ((iclass
= FindPrinter(name
)) != NULL
&&
3977 (iclass
->type
& CUPS_PRINTER_IMPLICIT
))
3983 * Add the group separator as needed...
3987 ippAddSeparator(con
->response
);
3992 * Send the following attributes for each printer:
3995 * printer-state-message
3996 * printer-is-accepting-jobs
3997 * + all printer attributes
4000 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
4001 "printer-state", printer
->state
);
4003 add_printer_state_reasons(con
, printer
);
4005 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
4006 "printer-state-message", NULL
, printer
->state_message
);
4008 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
4009 printer
->accepting
);
4011 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
4012 "printer-up-time", curtime
);
4013 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
4014 "printer-state-time", printer
->state_time
);
4015 ippAddDate(con
->response
, IPP_TAG_PRINTER
, "printer-current-time",
4016 ippTimeToDate(curtime
));
4018 add_queued_job_count(con
, printer
);
4020 copy_attrs(con
->response
, printer
->attrs
, requested
, IPP_TAG_ZERO
, 0);
4022 copy_attrs(con
->response
, CommonData
, requested
, IPP_TAG_ZERO
,
4025 if (need_history
&& printer
->num_history
> 0)
4027 history
= ippAddCollections(con
->response
, IPP_TAG_PRINTER
,
4028 "printer-state-history",
4029 printer
->num_history
, NULL
);
4031 for (i
= 0; i
< printer
->num_history
; i
++)
4032 copy_attrs(history
->values
[i
].collection
= ippNew(),
4033 printer
->history
[i
], NULL
, IPP_TAG_ZERO
, 0);
4037 con
->response
->request
.status
.status_code
= requested
? IPP_OK_SUBST
: IPP_OK
;
4042 * 'hold_job()' - Hold a print job.
4046 hold_job(client_t
*con
, /* I - Client connection */
4047 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
4049 ipp_attribute_t
*attr
, /* Current job-hold-until */
4050 *newattr
; /* New job-hold-until */
4051 int jobid
; /* Job ID */
4052 char method
[HTTP_MAX_URI
],
4053 /* Method portion of URI */
4054 username
[HTTP_MAX_URI
],
4055 /* Username portion of URI */
4057 /* Host portion of URI */
4058 resource
[HTTP_MAX_URI
];
4059 /* Resource portion of URI */
4060 int port
; /* Port portion of URI */
4061 job_t
*job
; /* Job information */
4064 LogMessage(L_DEBUG2
, "hold_job(%d, %s)\n", con
->http
.fd
,
4065 uri
->values
[0].string
.text
);
4068 * Verify that the POST operation was done to a valid URI.
4071 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
4072 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
4073 strncmp(con
->uri
, "/printers/", 10) != 0)
4075 LogMessage(L_ERROR
, "hold_job: hold request on bad resource \'%s\'!",
4077 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
4082 * See if we have a job URI or a printer URI...
4085 if (strcmp(uri
->name
, "printer-uri") == 0)
4088 * Got a printer URI; see if we also have a job-id attribute...
4091 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
4093 LogMessage(L_ERROR
, "hold_job: got a printer-uri attribute but no job-id!");
4094 send_ipp_error(con
, IPP_BAD_REQUEST
);
4098 jobid
= attr
->values
[0].integer
;
4103 * Got a job URI; parse it to get the job ID...
4106 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
4108 if (strncmp(resource
, "/jobs/", 6) != 0)
4114 LogMessage(L_ERROR
, "hold_job: bad job-uri attribute \'%s\'!",
4115 uri
->values
[0].string
.text
);
4116 send_ipp_error(con
, IPP_BAD_REQUEST
);
4120 jobid
= atoi(resource
+ 6);
4124 * See if the job exists...
4127 if ((job
= FindJob(jobid
)) == NULL
)
4130 * Nope - return a "not found" error...
4133 LogMessage(L_ERROR
, "hold_job: job #%d doesn't exist!", jobid
);
4134 send_ipp_error(con
, IPP_NOT_FOUND
);
4139 * See if the job is owned by the requesting user...
4142 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
4144 LogMessage(L_ERROR
, "hold_job: \"%s\" not authorized to hold job id %d owned by \"%s\"!",
4145 username
, jobid
, job
->username
);
4146 send_ipp_error(con
, IPP_FORBIDDEN
);
4151 * Hold the job and return...
4156 if ((newattr
= ippFindAttribute(con
->request
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
4157 newattr
= ippFindAttribute(con
->request
, "job-hold-until", IPP_TAG_NAME
);
4159 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
4160 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
4165 * Free the old hold value and copy the new one over...
4168 free(attr
->values
[0].string
.text
);
4170 if (newattr
!= NULL
)
4172 attr
->value_tag
= newattr
->value_tag
;
4173 attr
->values
[0].string
.text
= strdup(newattr
->values
[0].string
.text
);
4177 attr
->value_tag
= IPP_TAG_KEYWORD
;
4178 attr
->values
[0].string
.text
= strdup("indefinite");
4182 * Hold job until specified time...
4185 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
4188 LogMessage(L_INFO
, "Job %d was held by \'%s\'.", jobid
, username
);
4190 con
->response
->request
.status
.status_code
= IPP_OK
;
4195 * 'move_job()' - Move a job to a new destination.
4199 move_job(client_t
*con
, /* I - Client connection */
4200 ipp_attribute_t
*uri
) /* I - Job URI */
4202 ipp_attribute_t
*attr
; /* Current attribute */
4203 int jobid
; /* Job ID */
4204 job_t
*job
; /* Current job */
4205 const char *dest
; /* Destination */
4206 cups_ptype_t dtype
; /* Destination type (printer or class) */
4207 char method
[HTTP_MAX_URI
],
4208 /* Method portion of URI */
4209 username
[HTTP_MAX_URI
],
4210 /* Username portion of URI */
4212 /* Host portion of URI */
4213 resource
[HTTP_MAX_URI
];
4214 /* Resource portion of URI */
4215 int port
; /* Port portion of URI */
4218 LogMessage(L_DEBUG2
, "move_job(%d, %s)\n", con
->http
.fd
,
4219 uri
->values
[0].string
.text
);
4222 * See if we have a job URI or a printer URI...
4225 if (strcmp(uri
->name
, "printer-uri") == 0)
4228 * Got a printer URI; see if we also have a job-id attribute...
4231 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
4233 LogMessage(L_ERROR
, "move_job: got a printer-uri attribute but no job-id!");
4234 send_ipp_error(con
, IPP_BAD_REQUEST
);
4238 jobid
= attr
->values
[0].integer
;
4243 * Got a job URI; parse it to get the job ID...
4246 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
4248 if (strncmp(resource
, "/jobs/", 6) != 0)
4254 LogMessage(L_ERROR
, "move_job: bad job-uri attribute \'%s\'!\n",
4255 uri
->values
[0].string
.text
);
4256 send_ipp_error(con
, IPP_BAD_REQUEST
);
4260 jobid
= atoi(resource
+ 6);
4264 * See if the job exists...
4267 if ((job
= FindJob(jobid
)) == NULL
)
4270 * Nope - return a "not found" error...
4273 LogMessage(L_ERROR
, "move_job: job #%d doesn't exist!", jobid
);
4274 send_ipp_error(con
, IPP_NOT_FOUND
);
4279 * See if the job has been completed...
4282 if (job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
4285 * Return a "not-possible" error...
4288 LogMessage(L_ERROR
, "move_job: job #%d is finished and cannot be altered!", jobid
);
4289 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
4294 * See if the job is owned by the requesting user...
4297 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
4299 LogMessage(L_ERROR
, "move_job: \"%s\" not authorized to move job id %d owned by \"%s\"!",
4300 username
, jobid
, job
->username
);
4301 send_ipp_error(con
, IPP_FORBIDDEN
);
4305 if ((attr
= ippFindAttribute(con
->request
, "job-printer-uri", IPP_TAG_URI
)) == NULL
)
4308 * Need job-printer-uri...
4311 LogMessage(L_ERROR
, "move_job: job-printer-uri attribute missing!");
4312 send_ipp_error(con
, IPP_BAD_REQUEST
);
4317 * Move the job to a different printer or class...
4320 httpSeparate(attr
->values
[0].string
.text
, method
, username
, host
, &port
,
4322 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
4328 LogMessage(L_ERROR
, "move_job: resource name \'%s\' no good!", resource
);
4329 send_ipp_error(con
, IPP_NOT_FOUND
);
4333 MoveJob(jobid
, dest
);
4336 * Start jobs if possible...
4342 * Return with "everything is OK" status...
4345 con
->response
->request
.status
.status_code
= IPP_OK
;
4350 * 'ppd_add_default()' - Add a PPD default choice.
4353 static int /* O - Number of defaults */
4354 ppd_add_default(const char *option
, /* I - Option name */
4355 const char *choice
, /* I - Choice name */
4357 /* I - Number of defaults */
4358 ppd_default_t
**defaults
)
4361 int i
; /* Looping var */
4362 ppd_default_t
*temp
; /* Temporary defaults array */
4366 * First check if the option already has a default value; the PPD spec
4367 * says that the first one is used...
4370 for (i
= 0, temp
= *defaults
; i
< num_defaults
; i
++)
4371 if (!strcmp(option
, temp
[i
].option
))
4372 return (num_defaults
);
4375 * Now add the option...
4378 if (num_defaults
== 0)
4379 temp
= malloc(sizeof(ppd_default_t
));
4381 temp
= realloc(*defaults
, (num_defaults
+ 1) * sizeof(ppd_default_t
));
4385 LogMessage(L_ERROR
, "ppd_add_default: Unable to add default value for \"%s\" - %s",
4386 option
, strerror(errno
));
4387 return (num_defaults
);
4391 temp
+= num_defaults
;
4393 strlcpy(temp
->option
, option
, sizeof(temp
->option
));
4394 strlcpy(temp
->choice
, choice
, sizeof(temp
->choice
));
4396 return (num_defaults
+ 1);
4401 * 'ppd_parse_line()' - Parse a PPD default line.
4404 static int /* O - 0 on success, -1 on failure */
4405 ppd_parse_line(const char *line
, /* I - Line */
4406 char *option
, /* O - Option name */
4407 int olen
, /* I - Size of option name */
4408 char *choice
, /* O - Choice name */
4409 int clen
) /* I - Size of choice name */
4412 * Verify this is a default option line...
4415 if (strncmp(line
, "*Default", 8))
4419 * Read the option name...
4422 for (line
+= 8, olen
--; isalnum(*line
); line
++)
4432 * Skip everything else up to the colon (:)...
4435 while (*line
&& *line
!= ':')
4444 * Now grab the option choice, skipping leading whitespace...
4447 while (isspace(*line
))
4450 for (clen
--; isalnum(*line
); line
++)
4460 * Return with no errors...
4468 * 'print_job()' - Print a file to a printer or class.
4472 print_job(client_t
*con
, /* I - Client connection */
4473 ipp_attribute_t
*uri
) /* I - Printer URI */
4475 ipp_attribute_t
*attr
; /* Current attribute */
4476 ipp_attribute_t
*format
; /* Document-format attribute */
4477 const char *dest
; /* Destination */
4478 cups_ptype_t dtype
; /* Destination type (printer or class) */
4479 int priority
; /* Job priority */
4480 char *title
; /* Job name/title */
4481 job_t
*job
; /* Current job */
4482 int jobid
; /* Job ID number */
4483 char job_uri
[HTTP_MAX_URI
],
4485 printer_uri
[HTTP_MAX_URI
],
4487 method
[HTTP_MAX_URI
],
4488 /* Method portion of URI */
4489 username
[HTTP_MAX_URI
],
4490 /* Username portion of URI */
4492 /* Host portion of URI */
4493 resource
[HTTP_MAX_URI
],
4494 /* Resource portion of URI */
4495 filename
[1024]; /* Job filename */
4496 int port
; /* Port portion of URI */
4497 mime_type_t
*filetype
; /* Type of file */
4498 char super
[MIME_MAX_SUPER
],
4499 /* Supertype of file */
4500 type
[MIME_MAX_TYPE
],
4501 /* Subtype of file */
4502 mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
4503 /* Textual name of mime type */
4504 printer_t
*printer
; /* Printer data */
4505 struct stat fileinfo
; /* File information */
4506 int kbytes
; /* Size of file */
4507 int i
; /* Looping var */
4508 int lowerpagerange
; /* Page range bound */
4509 int compression
; /* Document compression */
4512 LogMessage(L_DEBUG2
, "print_job(%d, %s)\n", con
->http
.fd
,
4513 uri
->values
[0].string
.text
);
4516 * Verify that the POST operation was done to a valid URI.
4519 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
4520 strncmp(con
->uri
, "/printers/", 10) != 0)
4522 LogMessage(L_ERROR
, "print_job: cancel request on bad resource \'%s\'!",
4524 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
4529 * Validate job template attributes; for now just copies and page-ranges...
4532 if ((attr
= ippFindAttribute(con
->request
, "copies", IPP_TAG_INTEGER
)) != NULL
)
4534 if (attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> MaxCopies
)
4536 LogMessage(L_INFO
, "print_job: bad copies value %d.",
4537 attr
->values
[0].integer
);
4538 send_ipp_error(con
, IPP_BAD_REQUEST
);
4543 if ((attr
= ippFindAttribute(con
->request
, "page-ranges", IPP_TAG_RANGE
)) != NULL
)
4545 for (i
= 0, lowerpagerange
= 1; i
< attr
->num_values
; i
++)
4547 if (attr
->values
[i
].range
.lower
< lowerpagerange
||
4548 attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4550 LogMessage(L_ERROR
, "print_job: bad page-ranges values %d-%d.",
4551 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
4552 send_ipp_error(con
, IPP_BAD_REQUEST
);
4556 lowerpagerange
= attr
->values
[i
].range
.upper
+ 1;
4561 * OK, see if the client is sending the document compressed - CUPS
4562 * only supports "none" and "gzip".
4565 compression
= CUPS_FILE_NONE
;
4567 if ((attr
= ippFindAttribute(con
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
)
4569 if (strcmp(attr
->values
[0].string
.text
, "none")
4571 && strcmp(attr
->values
[0].string
.text
, "gzip")
4572 #endif /* HAVE_LIBZ */
4575 LogMessage(L_ERROR
, "print_job: Unsupported compression \"%s\"!",
4576 attr
->values
[0].string
.text
);
4577 send_ipp_error(con
, IPP_ATTRIBUTES
);
4578 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
4579 "compression", NULL
, attr
->values
[0].string
.text
);
4584 if (!strcmp(attr
->values
[0].string
.text
, "gzip"))
4585 compression
= CUPS_FILE_GZIP
;
4586 #endif /* HAVE_LIBZ */
4590 * Do we have a file to print?
4595 LogMessage(L_ERROR
, "print_job: No file!?!");
4596 send_ipp_error(con
, IPP_BAD_REQUEST
);
4601 * Is it a format we support?
4604 if ((format
= ippFindAttribute(con
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
4607 * Grab format from client...
4610 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
, type
) != 2)
4612 LogMessage(L_ERROR
, "print_job: could not scan type \'%s\'!",
4613 format
->values
[0].string
.text
);
4614 send_ipp_error(con
, IPP_BAD_REQUEST
);
4621 * No document format attribute? Auto-type it!
4624 strcpy(super
, "application");
4625 strcpy(type
, "octet-stream");
4628 if (strcmp(super
, "application") == 0 &&
4629 strcmp(type
, "octet-stream") == 0)
4632 * Auto-type the file...
4635 LogMessage(L_DEBUG
, "print_job: auto-typing file...");
4637 filetype
= mimeFileType(MimeDatabase
, con
->filename
, &compression
);
4639 if (filetype
!= NULL
)
4642 * Replace the document-format attribute value with the auto-typed one.
4645 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
4650 free(format
->values
[0].string
.text
);
4651 format
->values
[0].string
.text
= strdup(mimetype
);
4654 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
4655 "document-format", NULL
, mimetype
);
4658 filetype
= mimeType(MimeDatabase
, super
, type
);
4661 filetype
= mimeType(MimeDatabase
, super
, type
);
4663 if (filetype
== NULL
)
4665 LogMessage(L_ERROR
, "print_job: Unsupported format \'%s/%s\'!",
4667 LogMessage(L_INFO
, "Hint: Do you have the raw file printing rules enabled?");
4668 send_ipp_error(con
, IPP_DOCUMENT_FORMAT
);
4671 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
4672 "document-format", NULL
, format
->values
[0].string
.text
);
4677 LogMessage(L_DEBUG
, "print_job: request file type is %s/%s.",
4678 filetype
->super
, filetype
->type
);
4681 * Read any embedded job ticket info from PS files...
4684 if (strcasecmp(filetype
->super
, "application") == 0 &&
4685 strcasecmp(filetype
->type
, "postscript") == 0)
4686 read_ps_job_ticket(con
);
4689 * Is the destination valid?
4692 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
4694 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
4700 LogMessage(L_ERROR
, "print_job: resource name \'%s\' no good!", resource
);
4701 send_ipp_error(con
, IPP_NOT_FOUND
);
4706 * See if the printer is accepting jobs...
4709 if (dtype
& CUPS_PRINTER_CLASS
)
4711 printer
= FindClass(dest
);
4713 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
4714 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/classes/%s",
4715 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), dest
);
4717 #endif /* AF_INET6 */
4718 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/classes/%s",
4719 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), dest
);
4723 printer
= FindPrinter(dest
);
4726 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
4727 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/printers/%s",
4728 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), dest
);
4730 #endif /* AF_INET6 */
4731 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/printers/%s",
4732 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), dest
);
4735 if (!printer
->accepting
)
4737 LogMessage(L_INFO
, "print_job: destination \'%s\' is not accepting jobs.",
4739 send_ipp_error(con
, IPP_NOT_ACCEPTING
);
4744 * Make sure we aren't over our limit...
4747 if (NumJobs
>= MaxJobs
&& MaxJobs
)
4750 if (NumJobs
>= MaxJobs
&& MaxJobs
)
4752 LogMessage(L_INFO
, "print_job: too many jobs - %d jobs, max jobs is %d.",
4754 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
4758 if (!check_quotas(con
, printer
))
4760 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
4765 * Set all but the first two attributes to the job attributes group...
4768 for (attr
= con
->request
->attrs
->next
->next
; attr
; attr
= attr
->next
)
4769 attr
->group_tag
= IPP_TAG_JOB
;
4772 * Create the job and set things up...
4775 if ((attr
= ippFindAttribute(con
->request
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
4776 priority
= attr
->values
[0].integer
;
4778 ippAddInteger(con
->request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
4781 if ((attr
= ippFindAttribute(con
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
4782 title
= attr
->values
[0].string
.text
;
4784 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
4785 title
= "Untitled");
4787 if ((job
= AddJob(priority
, printer
->name
)) == NULL
)
4789 LogMessage(L_ERROR
, "print_job: unable to add job for destination \'%s\'!",
4791 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
4796 job
->attrs
= con
->request
;
4797 con
->request
= NULL
;
4800 * Copy the rest of the job info...
4803 attr
= ippFindAttribute(job
->attrs
, "requesting-user-name", IPP_TAG_NAME
);
4805 if (con
->username
[0])
4806 SetString(&job
->username
, con
->username
);
4807 else if (attr
!= NULL
)
4809 LogMessage(L_DEBUG
, "print_job: requesting-user-name = \'%s\'",
4810 attr
->values
[0].string
.text
);
4812 SetString(&job
->username
, attr
->values
[0].string
.text
);
4815 SetString(&job
->username
, "anonymous");
4818 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name",
4819 NULL
, job
->username
);
4822 attr
->group_tag
= IPP_TAG_JOB
;
4823 SetString(&attr
->name
, "job-originating-user-name");
4827 * Add remaining job attributes...
4830 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-host-name",
4831 IPP_TAG_ZERO
)) != NULL
)
4834 * Request contains a job-originating-host-name attribute; validate it...
4837 if (attr
->value_tag
!= IPP_TAG_NAME
||
4838 attr
->num_values
!= 1 ||
4839 strcmp(con
->http
.hostname
, "localhost") != 0)
4842 * Can't override the value if we aren't connected via localhost.
4843 * Also, we can only have 1 value and it must be a name value.
4846 int i
; /* Looping var */
4848 switch (attr
->value_tag
)
4850 case IPP_TAG_STRING
:
4851 case IPP_TAG_TEXTLANG
:
4852 case IPP_TAG_NAMELANG
:
4855 case IPP_TAG_KEYWORD
:
4857 case IPP_TAG_URISCHEME
:
4858 case IPP_TAG_CHARSET
:
4859 case IPP_TAG_LANGUAGE
:
4860 case IPP_TAG_MIMETYPE
:
4862 * Free old strings...
4865 for (i
= 0; i
< attr
->num_values
; i
++)
4867 free(attr
->values
[i
].string
.text
);
4868 attr
->values
[i
].string
.text
= NULL
;
4869 if (attr
->values
[i
].string
.charset
)
4871 free(attr
->values
[i
].string
.charset
);
4872 attr
->values
[i
].string
.charset
= NULL
;
4881 * Use the default connection hostname instead...
4884 attr
->value_tag
= IPP_TAG_NAME
;
4885 attr
->num_values
= 1;
4886 attr
->values
[0].string
.text
= strdup(con
->http
.hostname
);
4889 attr
->group_tag
= IPP_TAG_JOB
;
4894 * No job-originating-host-name attribute, so use the hostname from
4898 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
,
4899 "job-originating-host-name", NULL
, con
->http
.hostname
);
4902 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
4903 job
->state
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
4904 "job-state", IPP_JOB_PENDING
);
4905 job
->sheets
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4906 "job-media-sheets-completed", 0);
4907 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
4909 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
4912 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) == NULL
)
4913 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4916 if (stat(con
->filename
, &fileinfo
))
4919 kbytes
= (fileinfo
.st_size
+ 1023) / 1024;
4921 UpdateQuota(printer
, job
->username
, 0, kbytes
);
4922 attr
->values
[0].integer
+= kbytes
;
4924 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
4926 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4927 "time-at-processing", 0);
4928 attr
->value_tag
= IPP_TAG_NOVALUE
;
4929 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4930 "time-at-completed", 0);
4931 attr
->value_tag
= IPP_TAG_NOVALUE
;
4933 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
4934 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
4936 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
4937 "job-hold-until", NULL
, "no-hold");
4939 if (attr
!= NULL
&& strcmp(attr
->values
[0].string
.text
, "no-hold") != 0 &&
4940 !(printer
->type
& CUPS_PRINTER_REMOTE
))
4943 * Hold job until specified time...
4946 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
4947 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
4950 if (!(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)) ||
4954 * Add job sheets options...
4957 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) == NULL
)
4959 LogMessage(L_DEBUG
, "Adding default job-sheets values \"%s,%s\"...",
4960 printer
->job_sheets
[0], printer
->job_sheets
[1]);
4962 attr
= ippAddStrings(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-sheets",
4964 attr
->values
[0].string
.text
= strdup(printer
->job_sheets
[0]);
4965 attr
->values
[1].string
.text
= strdup(printer
->job_sheets
[1]);
4968 job
->job_sheets
= attr
;
4971 * Enforce classification level if set...
4976 if (ClassifyOverride
)
4978 if (strcmp(attr
->values
[0].string
.text
, "none") == 0 &&
4979 (attr
->num_values
== 1 ||
4980 strcmp(attr
->values
[1].string
.text
, "none") == 0))
4983 * Force the leading banner to have the classification on it...
4986 SetString(&attr
->values
[0].string
.text
, Classification
);
4988 LogMessage(L_NOTICE
, "[Job %d] CLASSIFICATION FORCED "
4989 "job-sheets=\"%s,none\", "
4990 "job-originating-user-name=\"%s\"",
4991 job
->id
, Classification
,
4994 else if (attr
->num_values
== 2 &&
4995 strcmp(attr
->values
[0].string
.text
, attr
->values
[1].string
.text
) != 0 &&
4996 strcmp(attr
->values
[0].string
.text
, "none") != 0 &&
4997 strcmp(attr
->values
[1].string
.text
, "none") != 0)
5000 * Can't put two different security markings on the same document!
5003 SetString(&attr
->values
[1].string
.text
, attr
->values
[0].string
.text
);
5005 LogMessage(L_NOTICE
, "[Job %d] CLASSIFICATION FORCED "
5006 "job-sheets=\"%s,%s\", "
5007 "job-originating-user-name=\"%s\"",
5008 job
->id
, attr
->values
[0].string
.text
,
5009 attr
->values
[1].string
.text
,
5012 else if (strcmp(attr
->values
[0].string
.text
, Classification
) &&
5013 strcmp(attr
->values
[0].string
.text
, "none") &&
5014 (attr
->num_values
== 1 ||
5015 (strcmp(attr
->values
[1].string
.text
, Classification
) &&
5016 strcmp(attr
->values
[1].string
.text
, "none"))))
5018 if (attr
->num_values
== 1)
5019 LogMessage(L_NOTICE
, "[Job %d] CLASSIFICATION OVERRIDDEN "
5020 "job-sheets=\"%s\", "
5021 "job-originating-user-name=\"%s\"",
5022 job
->id
, attr
->values
[0].string
.text
,
5025 LogMessage(L_NOTICE
, "[Job %d] CLASSIFICATION OVERRIDDEN "
5026 "job-sheets=\"%s,%s\", "
5027 "job-originating-user-name=\"%s\"",
5028 job
->id
, attr
->values
[0].string
.text
,
5029 attr
->values
[1].string
.text
,
5033 else if (strcmp(attr
->values
[0].string
.text
, Classification
) != 0 &&
5034 (attr
->num_values
== 1 ||
5035 strcmp(attr
->values
[1].string
.text
, Classification
) != 0))
5038 * Force the banner to have the classification on it...
5041 if (attr
->num_values
== 1 || strcmp(attr
->values
[0].string
.text
, "none"))
5042 SetString(&attr
->values
[0].string
.text
, Classification
);
5044 if (attr
->num_values
> 1)
5045 LogMessage(L_NOTICE
, "[Job %d] CLASSIFICATION FORCED "
5046 "job-sheets=\"%s,%s\", "
5047 "job-originating-user-name=\"%s\"",
5048 job
->id
, attr
->values
[0].string
.text
,
5049 attr
->values
[1].string
.text
,
5052 LogMessage(L_NOTICE
, "[Job %d] CLASSIFICATION FORCED "
5053 "job-sheets=\"%s\", "
5054 "job-originating-user-name=\"%s\"",
5055 job
->id
, Classification
,
5061 * Add the starting sheet...
5064 if (!(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)))
5066 LogMessage(L_INFO
, "Adding start banner page \"%s\" to job %d.",
5067 attr
->values
[0].string
.text
, job
->id
);
5069 kbytes
= copy_banner(con
, job
, attr
->values
[0].string
.text
);
5071 UpdateQuota(printer
, job
->username
, 0, kbytes
);
5074 else if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
5078 * Add the job file...
5081 if (add_file(con
, job
, filetype
, compression
))
5084 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
5086 rename(con
->filename
, filename
);
5087 ClearString(&con
->filename
);
5090 * See if we need to add the ending sheet...
5093 if (!(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)) &&
5094 attr
->num_values
> 1)
5100 LogMessage(L_INFO
, "Adding end banner page \"%s\" to job %d.",
5101 attr
->values
[1].string
.text
, job
->id
);
5103 kbytes
= copy_banner(con
, job
, attr
->values
[1].string
.text
);
5105 UpdateQuota(printer
, job
->username
, 0, kbytes
);
5109 * Log and save the job...
5112 LogMessage(L_INFO
, "Job %d queued on \'%s\' by \'%s\'.", job
->id
,
5113 job
->dest
, job
->username
);
5114 LogMessage(L_DEBUG
, "Job %d hold_until = %d", job
->id
, (int)job
->hold_until
);
5119 * Start the job if possible... Since CheckJobs() can cancel a job if it
5120 * doesn't print, we need to re-find the job afterwards...
5127 job
= FindJob(jobid
);
5130 * Fill in the response info...
5134 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
5135 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
5136 ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), jobid
);
5138 #endif /* AF_INET6 */
5139 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
5140 ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), jobid
);
5142 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, job_uri
);
5144 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", jobid
);
5146 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
5147 job
? job
->state
->values
[0].integer
: IPP_JOB_CANCELLED
);
5148 add_job_state_reasons(con
, job
);
5150 con
->response
->request
.status
.status_code
= IPP_OK
;
5155 * 'read_ps_job_ticket()' - Reads a job ticket embedded in a PS file.
5157 * This function only gets called when printing a single PostScript
5158 * file using the Print-Job operation. It doesn't work for Create-Job +
5159 * Send-File, since the job attributes need to be set at job creation
5160 * time for banners to work. The embedded PS job ticket stuff is here
5161 * only to allow the Windows printer driver for CUPS to pass in JCL
5162 * options and IPP attributes which otherwise would be lost.
5164 * The format of a PS job ticket is simple:
5166 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
5168 * %cupsJobTicket: attr1=value1
5169 * %cupsJobTicket: attr2=value2
5171 * %cupsJobTicket: attrN=valueN
5173 * Job ticket lines must appear immediately after the first line that
5174 * specifies PostScript format (%!PS-Adobe-3.0), and CUPS will stop
5175 * looking for job ticket info when it finds a line that does not begin
5176 * with "%cupsJobTicket:".
5178 * The maximum length of a job ticket line, including the prefix, is
5179 * 255 characters to conform with the Adobe DSC.
5181 * Read-only attributes are rejected with a notice to the error log in
5182 * case a malicious user tries anything. Since the job ticket is read
5183 * prior to attribute validation in print_job(), job ticket attributes
5184 * will go through the same validation as IPP attributes...
5188 read_ps_job_ticket(client_t
*con
) /* I - Client connection */
5190 cups_file_t
*fp
; /* File to read from */
5191 char line
[256]; /* Line data */
5192 int num_options
; /* Number of options */
5193 cups_option_t
*options
; /* Options */
5194 ipp_t
*ticket
; /* New attributes */
5195 ipp_attribute_t
*attr
, /* Current attribute */
5196 *attr2
, /* Job attribute */
5197 *prev2
; /* Previous job attribute */
5201 * First open the print file...
5204 if ((fp
= cupsFileOpen(con
->filename
, "rb")) == NULL
)
5206 LogMessage(L_ERROR
, "read_ps_job_ticket: Unable to open PostScript print file - %s",
5212 * Skip the first line...
5215 if (cupsFileGets(fp
, line
, sizeof(line
)) == NULL
)
5217 LogMessage(L_ERROR
, "read_ps_job_ticket: Unable to read from PostScript print file - %s",
5223 if (strncmp(line
, "%!PS-Adobe-", 11) != 0)
5226 * Not a DSC-compliant file, so no job ticket info will be available...
5234 * Read job ticket info from the file...
5240 while (cupsFileGets(fp
, line
, sizeof(line
)) != NULL
)
5243 * Stop at the first non-ticket line...
5246 if (strncmp(line
, "%cupsJobTicket:", 15) != 0)
5250 * Add the options to the option array...
5253 num_options
= cupsParseOptions(line
+ 15, num_options
, &options
);
5257 * Done with the file; see if we have any options...
5262 if (num_options
== 0)
5266 * OK, convert the options to an attribute list, and apply them to
5271 cupsEncodeOptions(ticket
, num_options
, options
);
5274 * See what the user wants to change.
5277 for (attr
= ticket
->attrs
; attr
!= NULL
; attr
= attr
->next
)
5279 if (attr
->group_tag
!= IPP_TAG_JOB
|| !attr
->name
)
5282 if (strcmp(attr
->name
, "job-originating-host-name") == 0 ||
5283 strcmp(attr
->name
, "job-originating-user-name") == 0 ||
5284 strcmp(attr
->name
, "job-media-sheets-completed") == 0 ||
5285 strcmp(attr
->name
, "job-k-octets") == 0 ||
5286 strcmp(attr
->name
, "job-id") == 0 ||
5287 strncmp(attr
->name
, "job-state", 9) == 0 ||
5288 strncmp(attr
->name
, "time-at-", 8) == 0)
5289 continue; /* Read-only attrs */
5291 if ((attr2
= ippFindAttribute(con
->request
, attr
->name
, IPP_TAG_ZERO
)) != NULL
)
5294 * Some other value; first free the old value...
5297 if (con
->request
->attrs
== attr2
)
5299 con
->request
->attrs
= attr2
->next
;
5304 for (prev2
= con
->request
->attrs
; prev2
!= NULL
; prev2
= prev2
->next
)
5305 if (prev2
->next
== attr2
)
5307 prev2
->next
= attr2
->next
;
5312 if (con
->request
->last
== attr2
)
5313 con
->request
->last
= prev2
;
5315 _ipp_free_attr(attr2
);
5319 * Add new option by copying it...
5322 copy_attribute(con
->request
, attr
, 0);
5326 * Then free the attribute list and option array...
5330 cupsFreeOptions(num_options
, options
);
5335 * 'reject_jobs()' - Reject print jobs to a printer.
5339 reject_jobs(client_t
*con
, /* I - Client connection */
5340 ipp_attribute_t
*uri
) /* I - Printer or class URI */
5342 cups_ptype_t dtype
; /* Destination type (printer or class) */
5343 char method
[HTTP_MAX_URI
],
5344 /* Method portion of URI */
5345 username
[HTTP_MAX_URI
],
5346 /* Username portion of URI */
5348 /* Host portion of URI */
5349 resource
[HTTP_MAX_URI
];
5350 /* Resource portion of URI */
5351 int port
; /* Port portion of URI */
5352 const char *name
; /* Printer name */
5353 printer_t
*printer
; /* Printer data */
5354 ipp_attribute_t
*attr
; /* printer-state-message text */
5357 LogMessage(L_DEBUG2
, "reject_jobs(%d, %s)\n", con
->http
.fd
,
5358 uri
->values
[0].string
.text
);
5361 * Was this operation called from the correct URI?
5364 if (strncmp(con
->uri
, "/admin/", 7) != 0)
5366 LogMessage(L_ERROR
, "reject_jobs: admin request on bad resource \'%s\'!",
5368 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5373 * Is the destination valid?
5376 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5378 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
5384 LogMessage(L_ERROR
, "reject_jobs: resource name \'%s\' no good!", resource
);
5385 send_ipp_error(con
, IPP_NOT_FOUND
);
5390 * Reject jobs sent to the printer...
5393 if (dtype
& CUPS_PRINTER_CLASS
)
5394 printer
= FindClass(name
);
5396 printer
= FindPrinter(name
);
5398 printer
->accepting
= 0;
5400 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
5401 IPP_TAG_TEXT
)) == NULL
)
5402 strcpy(printer
->state_message
, "Rejecting Jobs");
5404 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
5405 sizeof(printer
->state_message
));
5407 AddPrinterHistory(printer
);
5409 if (dtype
& CUPS_PRINTER_CLASS
)
5413 LogMessage(L_INFO
, "Class \'%s\' rejecting jobs (\'%s\').", name
,
5420 LogMessage(L_INFO
, "Printer \'%s\' rejecting jobs (\'%s\').", name
,
5425 * Everything was ok, so return OK status...
5428 con
->response
->request
.status
.status_code
= IPP_OK
;
5433 * 'release_job()' - Release a held print job.
5437 release_job(client_t
*con
, /* I - Client connection */
5438 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
5440 ipp_attribute_t
*attr
; /* Current attribute */
5441 int jobid
; /* Job ID */
5442 char method
[HTTP_MAX_URI
],
5443 /* Method portion of URI */
5444 username
[HTTP_MAX_URI
],
5445 /* Username portion of URI */
5447 /* Host portion of URI */
5448 resource
[HTTP_MAX_URI
];
5449 /* Resource portion of URI */
5450 int port
; /* Port portion of URI */
5451 job_t
*job
; /* Job information */
5454 LogMessage(L_DEBUG2
, "release_job(%d, %s)\n", con
->http
.fd
,
5455 uri
->values
[0].string
.text
);
5458 * Verify that the POST operation was done to a valid URI.
5461 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
5462 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
5463 strncmp(con
->uri
, "/printers/", 10) != 0)
5465 LogMessage(L_ERROR
, "release_job: release request on bad resource \'%s\'!",
5467 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5472 * See if we have a job URI or a printer URI...
5475 if (strcmp(uri
->name
, "printer-uri") == 0)
5478 * Got a printer URI; see if we also have a job-id attribute...
5481 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
5483 LogMessage(L_ERROR
, "release_job: got a printer-uri attribute but no job-id!");
5484 send_ipp_error(con
, IPP_BAD_REQUEST
);
5488 jobid
= attr
->values
[0].integer
;
5493 * Got a job URI; parse it to get the job ID...
5496 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5498 if (strncmp(resource
, "/jobs/", 6) != 0)
5504 LogMessage(L_ERROR
, "release_job: bad job-uri attribute \'%s\'!",
5505 uri
->values
[0].string
.text
);
5506 send_ipp_error(con
, IPP_BAD_REQUEST
);
5510 jobid
= atoi(resource
+ 6);
5514 * See if the job exists...
5517 if ((job
= FindJob(jobid
)) == NULL
)
5520 * Nope - return a "not found" error...
5523 LogMessage(L_ERROR
, "release_job: job #%d doesn't exist!", jobid
);
5524 send_ipp_error(con
, IPP_NOT_FOUND
);
5529 * See if job is "held"...
5532 if (job
->state
->values
[0].integer
!= IPP_JOB_HELD
)
5535 * Nope - return a "not possible" error...
5538 LogMessage(L_ERROR
, "release_job: job #%d is not held!", jobid
);
5539 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
5544 * See if the job is owned by the requesting user...
5547 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
5549 LogMessage(L_ERROR
, "release_job: \"%s\" not authorized to release job id %d owned by \"%s\"!",
5550 username
, jobid
, job
->username
);
5551 send_ipp_error(con
, IPP_FORBIDDEN
);
5556 * Reset the job-hold-until value to "no-hold"...
5559 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
5560 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
5564 free(attr
->values
[0].string
.text
);
5565 attr
->value_tag
= IPP_TAG_KEYWORD
;
5566 attr
->values
[0].string
.text
= strdup("no-hold");
5570 * Release the job and return...
5575 LogMessage(L_INFO
, "Job %d was released by \'%s\'.", jobid
, username
);
5577 con
->response
->request
.status
.status_code
= IPP_OK
;
5582 * 'restart_job()' - Restart an old print job.
5586 restart_job(client_t
*con
, /* I - Client connection */
5587 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
5589 ipp_attribute_t
*attr
; /* Current attribute */
5590 int jobid
; /* Job ID */
5591 char method
[HTTP_MAX_URI
],
5592 /* Method portion of URI */
5593 username
[HTTP_MAX_URI
],
5594 /* Username portion of URI */
5596 /* Host portion of URI */
5597 resource
[HTTP_MAX_URI
];
5598 /* Resource portion of URI */
5599 int port
; /* Port portion of URI */
5600 job_t
*job
; /* Job information */
5603 LogMessage(L_DEBUG2
, "restart_job(%d, %s)\n", con
->http
.fd
,
5604 uri
->values
[0].string
.text
);
5607 * Verify that the POST operation was done to a valid URI.
5610 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
5611 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
5612 strncmp(con
->uri
, "/printers/", 10) != 0)
5614 LogMessage(L_ERROR
, "restart_job: restart request on bad resource \'%s\'!",
5616 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5621 * See if we have a job URI or a printer URI...
5624 if (strcmp(uri
->name
, "printer-uri") == 0)
5627 * Got a printer URI; see if we also have a job-id attribute...
5630 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
5632 LogMessage(L_ERROR
, "restart_job: got a printer-uri attribute but no job-id!");
5633 send_ipp_error(con
, IPP_BAD_REQUEST
);
5637 jobid
= attr
->values
[0].integer
;
5642 * Got a job URI; parse it to get the job ID...
5645 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5647 if (strncmp(resource
, "/jobs/", 6) != 0)
5653 LogMessage(L_ERROR
, "restart_job: bad job-uri attribute \'%s\'!",
5654 uri
->values
[0].string
.text
);
5655 send_ipp_error(con
, IPP_BAD_REQUEST
);
5659 jobid
= atoi(resource
+ 6);
5663 * See if the job exists...
5666 if ((job
= FindJob(jobid
)) == NULL
)
5669 * Nope - return a "not found" error...
5672 LogMessage(L_ERROR
, "restart_job: job #%d doesn't exist!", jobid
);
5673 send_ipp_error(con
, IPP_NOT_FOUND
);
5678 * See if job is in any of the "completed" states...
5681 if (job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
)
5684 * Nope - return a "not possible" error...
5687 LogMessage(L_ERROR
, "restart_job: job #%d is not complete!", jobid
);
5688 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
5693 * See if we have retained the job files...
5696 if (!JobFiles
&& job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
5699 * Nope - return a "not possible" error...
5702 LogMessage(L_ERROR
, "restart_job: job #%d cannot be restarted - no files!", jobid
);
5703 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
5708 * See if the job is owned by the requesting user...
5711 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
5713 LogMessage(L_ERROR
, "restart_job: \"%s\" not authorized to restart job id %d owned by \"%s\"!",
5714 username
, jobid
, job
->username
);
5715 send_ipp_error(con
, IPP_FORBIDDEN
);
5720 * Restart the job and return...
5725 LogMessage(L_INFO
, "Job %d was restarted by \'%s\'.", jobid
, username
);
5727 con
->response
->request
.status
.status_code
= IPP_OK
;
5732 * 'send_document()' - Send a file to a printer or class.
5736 send_document(client_t
*con
, /* I - Client connection */
5737 ipp_attribute_t
*uri
) /* I - Printer URI */
5739 ipp_attribute_t
*attr
; /* Current attribute */
5740 ipp_attribute_t
*format
; /* Document-format attribute */
5741 int jobid
; /* Job ID number */
5742 job_t
*job
; /* Current job */
5743 char job_uri
[HTTP_MAX_URI
],
5745 method
[HTTP_MAX_URI
],
5746 /* Method portion of URI */
5747 username
[HTTP_MAX_URI
],
5748 /* Username portion of URI */
5750 /* Host portion of URI */
5751 resource
[HTTP_MAX_URI
];
5752 /* Resource portion of URI */
5753 int port
; /* Port portion of URI */
5754 mime_type_t
*filetype
; /* Type of file */
5755 char super
[MIME_MAX_SUPER
],
5756 /* Supertype of file */
5757 type
[MIME_MAX_TYPE
],
5758 /* Subtype of file */
5759 mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
5760 /* Textual name of mime type */
5761 char filename
[1024]; /* Job filename */
5762 printer_t
*printer
; /* Current printer */
5763 struct stat fileinfo
; /* File information */
5764 int kbytes
; /* Size of file */
5765 int compression
; /* Type of compression */
5768 LogMessage(L_DEBUG2
, "send_document(%d, %s)\n", con
->http
.fd
,
5769 uri
->values
[0].string
.text
);
5772 * Verify that the POST operation was done to a valid URI.
5775 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
5776 strncmp(con
->uri
, "/jobs/", 6) != 0 &&
5777 strncmp(con
->uri
, "/printers/", 10) != 0)
5779 LogMessage(L_ERROR
, "send_document: print request on bad resource \'%s\'!",
5781 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5786 * See if we have a job URI or a printer URI...
5789 if (strcmp(uri
->name
, "printer-uri") == 0)
5792 * Got a printer URI; see if we also have a job-id attribute...
5795 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
5797 LogMessage(L_ERROR
, "send_document: got a printer-uri attribute but no job-id!");
5798 send_ipp_error(con
, IPP_BAD_REQUEST
);
5802 jobid
= attr
->values
[0].integer
;
5807 * Got a job URI; parse it to get the job ID...
5810 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5812 if (strncmp(resource
, "/jobs/", 6) != 0)
5818 LogMessage(L_ERROR
, "send_document: bad job-uri attribute \'%s\'!",
5819 uri
->values
[0].string
.text
);
5820 send_ipp_error(con
, IPP_BAD_REQUEST
);
5824 jobid
= atoi(resource
+ 6);
5828 * See if the job exists...
5831 if ((job
= FindJob(jobid
)) == NULL
)
5834 * Nope - return a "not found" error...
5837 LogMessage(L_ERROR
, "send_document: job #%d doesn't exist!", jobid
);
5838 send_ipp_error(con
, IPP_NOT_FOUND
);
5843 * See if the job is owned by the requesting user...
5846 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
5848 LogMessage(L_ERROR
, "send_document: \"%s\" not authorized to send document for job id %d owned by \"%s\"!",
5849 username
, jobid
, job
->username
);
5850 send_ipp_error(con
, IPP_FORBIDDEN
);
5855 * OK, see if the client is sending the document compressed - CUPS
5856 * only supports "none" and "gzip".
5859 compression
= CUPS_FILE_NONE
;
5861 if ((attr
= ippFindAttribute(con
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
)
5863 if (strcmp(attr
->values
[0].string
.text
, "none")
5865 && strcmp(attr
->values
[0].string
.text
, "gzip")
5866 #endif /* HAVE_LIBZ */
5869 LogMessage(L_ERROR
, "print_job: Unsupported compression \"%s\"!",
5870 attr
->values
[0].string
.text
);
5871 send_ipp_error(con
, IPP_ATTRIBUTES
);
5872 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
5873 "compression", NULL
, attr
->values
[0].string
.text
);
5878 if (!strcmp(attr
->values
[0].string
.text
, "gzip"))
5879 compression
= CUPS_FILE_GZIP
;
5880 #endif /* HAVE_LIBZ */
5884 * Do we have a file to print?
5889 LogMessage(L_ERROR
, "send_document: No file!?!");
5890 send_ipp_error(con
, IPP_BAD_REQUEST
);
5895 * Is it a format we support?
5898 if ((format
= ippFindAttribute(con
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
5901 * Grab format from client...
5904 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
, type
) != 2)
5906 LogMessage(L_ERROR
, "send_document: could not scan type \'%s\'!",
5907 format
->values
[0].string
.text
);
5908 send_ipp_error(con
, IPP_BAD_REQUEST
);
5915 * No document format attribute? Auto-type it!
5918 strcpy(super
, "application");
5919 strcpy(type
, "octet-stream");
5922 if (strcmp(super
, "application") == 0 &&
5923 strcmp(type
, "octet-stream") == 0)
5926 * Auto-type the file...
5929 LogMessage(L_DEBUG
, "send_document: auto-typing file...");
5931 filetype
= mimeFileType(MimeDatabase
, con
->filename
, &compression
);
5933 if (filetype
!= NULL
)
5936 * Replace the document-format attribute value with the auto-typed one.
5939 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
5944 free(format
->values
[0].string
.text
);
5945 format
->values
[0].string
.text
= strdup(mimetype
);
5948 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
5949 "document-format", NULL
, mimetype
);
5952 filetype
= mimeType(MimeDatabase
, super
, type
);
5955 filetype
= mimeType(MimeDatabase
, super
, type
);
5957 if (filetype
== NULL
)
5959 LogMessage(L_ERROR
, "send_document: Unsupported format \'%s/%s\'!",
5961 LogMessage(L_INFO
, "Hint: Do you have the raw file printing rules enabled?");
5962 send_ipp_error(con
, IPP_DOCUMENT_FORMAT
);
5965 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
5966 "document-format", NULL
, format
->values
[0].string
.text
);
5971 LogMessage(L_DEBUG
, "send_document: request file type is %s/%s.",
5972 filetype
->super
, filetype
->type
);
5975 * Add the file to the job...
5978 if (add_file(con
, job
, filetype
, compression
))
5981 if (job
->dtype
& CUPS_PRINTER_CLASS
)
5982 printer
= FindClass(job
->dest
);
5984 printer
= FindPrinter(job
->dest
);
5986 if (stat(con
->filename
, &fileinfo
))
5989 kbytes
= (fileinfo
.st_size
+ 1023) / 1024;
5991 UpdateQuota(printer
, job
->username
, 0, kbytes
);
5993 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
5994 attr
->values
[0].integer
+= kbytes
;
5996 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
5998 rename(con
->filename
, filename
);
6000 ClearString(&con
->filename
);
6002 LogMessage(L_INFO
, "File of type %s/%s queued in job #%d by \'%s\'.",
6003 filetype
->super
, filetype
->type
, job
->id
, job
->username
);
6006 * Start the job if this is the last document...
6009 if ((attr
= ippFindAttribute(con
->request
, "last-document", IPP_TAG_BOOLEAN
)) != NULL
&&
6010 attr
->values
[0].boolean
)
6013 * See if we need to add the ending sheet...
6016 if (printer
!= NULL
&&
6017 !(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)) &&
6018 (attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
&&
6019 attr
->num_values
> 1)
6025 LogMessage(L_INFO
, "Adding end banner page \"%s\" to job %d.",
6026 attr
->values
[1].string
.text
, job
->id
);
6028 kbytes
= copy_banner(con
, job
, attr
->values
[1].string
.text
);
6030 UpdateQuota(printer
, job
->username
, 0, kbytes
);
6033 if (job
->state
->values
[0].integer
== IPP_JOB_STOPPED
)
6034 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
6035 else if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
6037 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
6038 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
6040 if (attr
== NULL
|| strcmp(attr
->values
[0].string
.text
, "no-hold") == 0)
6041 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
6047 * Start the job if possible... Since CheckJobs() can cancel a job if it
6048 * doesn't print, we need to re-find the job afterwards...
6055 job
= FindJob(jobid
);
6059 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
6060 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
6062 if (attr
== NULL
|| strcmp(attr
->values
[0].string
.text
, "no-hold") == 0)
6064 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
6065 job
->hold_until
= time(NULL
) + 60;
6071 * Fill in the response info...
6075 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
6076 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
6077 ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), jobid
);
6079 #endif /* AF_INET6 */
6080 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
6081 ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), jobid
);
6083 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
,
6086 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", jobid
);
6088 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
6089 job
? job
->state
->values
[0].integer
: IPP_JOB_CANCELLED
);
6090 add_job_state_reasons(con
, job
);
6092 con
->response
->request
.status
.status_code
= IPP_OK
;
6097 * 'send_ipp_error()' - Send an error status back to the IPP client.
6101 send_ipp_error(client_t
*con
, /* I - Client connection */
6102 ipp_status_t status
) /* I - IPP status code */
6104 LogMessage(L_DEBUG2
, "send_ipp_error(%d, %x)\n", con
->http
.fd
, status
);
6106 LogMessage(L_DEBUG
, "Sending error: %s", ippErrorString(status
));
6108 con
->response
->request
.status
.status_code
= status
;
6110 if (ippFindAttribute(con
->response
, "attributes-charset", IPP_TAG_ZERO
) == NULL
)
6111 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
6112 "attributes-charset", NULL
, DefaultCharset
);
6114 if (ippFindAttribute(con
->response
, "attributes-natural-language",
6115 IPP_TAG_ZERO
) == NULL
)
6116 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
6117 "attributes-natural-language", NULL
, DefaultLanguage
);
6122 * 'set_default()' - Set the default destination...
6126 set_default(client_t
*con
, /* I - Client connection */
6127 ipp_attribute_t
*uri
) /* I - Printer URI */
6129 cups_ptype_t dtype
; /* Destination type (printer or class) */
6130 char method
[HTTP_MAX_URI
],
6131 /* Method portion of URI */
6132 username
[HTTP_MAX_URI
],
6133 /* Username portion of URI */
6135 /* Host portion of URI */
6136 resource
[HTTP_MAX_URI
];
6137 /* Resource portion of URI */
6138 int port
; /* Port portion of URI */
6139 const char *name
; /* Printer name */
6142 LogMessage(L_DEBUG2
, "set_default(%d, %s)\n", con
->http
.fd
,
6143 uri
->values
[0].string
.text
);
6146 * Was this operation called from the correct URI?
6149 if (strncmp(con
->uri
, "/admin/", 7) != 0)
6151 LogMessage(L_ERROR
, "set_default: admin request on bad resource \'%s\'!",
6153 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
6158 * Is the destination valid?
6161 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6163 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
6169 LogMessage(L_ERROR
, "set_default: resource name \'%s\' no good!", resource
);
6170 send_ipp_error(con
, IPP_NOT_FOUND
);
6175 * Set it as the default...
6178 if (dtype
& CUPS_PRINTER_CLASS
)
6179 DefaultPrinter
= FindClass(name
);
6181 DefaultPrinter
= FindPrinter(name
);
6186 LogMessage(L_INFO
, "Default destination set to \'%s\' by \'%s\'.", name
,
6190 * Everything was ok, so return OK status...
6193 con
->response
->request
.status
.status_code
= IPP_OK
;
6198 * 'set_job_attrs()' - Set job attributes.
6202 set_job_attrs(client_t
*con
, /* I - Client connection */
6203 ipp_attribute_t
*uri
) /* I - Job URI */
6205 ipp_attribute_t
*attr
, /* Current attribute */
6206 *attr2
, /* Job attribute */
6207 *prev2
; /* Previous job attribute */
6208 int jobid
; /* Job ID */
6209 job_t
*job
; /* Current job */
6210 char method
[HTTP_MAX_URI
],
6211 /* Method portion of URI */
6212 username
[HTTP_MAX_URI
],
6213 /* Username portion of URI */
6215 /* Host portion of URI */
6216 resource
[HTTP_MAX_URI
];
6217 /* Resource portion of URI */
6218 int port
; /* Port portion of URI */
6221 LogMessage(L_DEBUG2
, "set_job_attrs(%d, %s)\n", con
->http
.fd
,
6222 uri
->values
[0].string
.text
);
6225 * Start with "everything is OK" status...
6228 con
->response
->request
.status
.status_code
= IPP_OK
;
6231 * See if we have a job URI or a printer URI...
6234 if (strcmp(uri
->name
, "printer-uri") == 0)
6237 * Got a printer URI; see if we also have a job-id attribute...
6240 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
6242 LogMessage(L_ERROR
, "set_job_attrs: got a printer-uri attribute but no job-id!");
6243 send_ipp_error(con
, IPP_BAD_REQUEST
);
6247 jobid
= attr
->values
[0].integer
;
6252 * Got a job URI; parse it to get the job ID...
6255 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6257 if (strncmp(resource
, "/jobs/", 6) != 0)
6263 LogMessage(L_ERROR
, "set_job_attrs: bad job-uri attribute \'%s\'!\n",
6264 uri
->values
[0].string
.text
);
6265 send_ipp_error(con
, IPP_BAD_REQUEST
);
6269 jobid
= atoi(resource
+ 6);
6273 * See if the job exists...
6276 if ((job
= FindJob(jobid
)) == NULL
)
6279 * Nope - return a "not found" error...
6282 LogMessage(L_ERROR
, "set_job_attrs: job #%d doesn't exist!", jobid
);
6283 send_ipp_error(con
, IPP_NOT_FOUND
);
6288 * See if the job has been completed...
6291 if (job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
6294 * Return a "not-possible" error...
6297 LogMessage(L_ERROR
, "set_job_attrs: job #%d is finished and cannot be altered!", jobid
);
6298 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
6303 * See if the job is owned by the requesting user...
6306 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
6308 LogMessage(L_ERROR
, "set_job_attrs: \"%s\" not authorized to alter job id %d owned by \"%s\"!",
6309 username
, jobid
, job
->username
);
6310 send_ipp_error(con
, IPP_FORBIDDEN
);
6315 * See what the user wants to change.
6318 for (attr
= con
->request
->attrs
; attr
!= NULL
; attr
= attr
->next
)
6320 if (attr
->group_tag
!= IPP_TAG_JOB
|| !attr
->name
)
6323 if (!strcmp(attr
->name
, "attributes-charset") ||
6324 !strcmp(attr
->name
, "attributes-natural-language") ||
6325 !strcmp(attr
->name
, "document-compression") ||
6326 !strcmp(attr
->name
, "document-format") ||
6327 !strcmp(attr
->name
, "job-detailed-status-messages") ||
6328 !strcmp(attr
->name
, "job-document-access-errors") ||
6329 !strcmp(attr
->name
, "job-id") ||
6330 !strcmp(attr
->name
, "job-k-octets") ||
6331 !strcmp(attr
->name
, "job-originating-host-name") ||
6332 !strcmp(attr
->name
, "job-originating-user-name") ||
6333 !strcmp(attr
->name
, "job-printer-up-time") ||
6334 !strcmp(attr
->name
, "job-printer-uri") ||
6335 !strcmp(attr
->name
, "job-sheets") ||
6336 !strcmp(attr
->name
, "job-state-message") ||
6337 !strcmp(attr
->name
, "job-state-reasons") ||
6338 !strcmp(attr
->name
, "job-uri") ||
6339 !strcmp(attr
->name
, "number-of-documents") ||
6340 !strcmp(attr
->name
, "number-of-intervening-jobs") ||
6341 !strcmp(attr
->name
, "output-device-assigned") ||
6342 !strncmp(attr
->name
, "date-time-at-", 13) ||
6343 !strncmp(attr
->name
, "job-impressions", 15) ||
6344 !strncmp(attr
->name
, "job-k-octets", 12) ||
6345 !strncmp(attr
->name
, "job-media-sheets", 16) ||
6346 !strncmp(attr
->name
, "time-at-", 8))
6352 send_ipp_error(con
, IPP_ATTRIBUTES_NOT_SETTABLE
);
6354 if ((attr2
= copy_attribute(con
->response
, attr
, 0)) != NULL
)
6355 attr2
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
6360 if (!strcmp(attr
->name
, "job-priority"))
6363 * Change the job priority...
6366 if (attr
->value_tag
!= IPP_TAG_INTEGER
)
6368 send_ipp_error(con
, IPP_REQUEST_VALUE
);
6370 if ((attr2
= copy_attribute(con
->response
, attr
, 0)) != NULL
)
6371 attr2
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
6373 else if (job
->state
->values
[0].integer
>= IPP_JOB_PROCESSING
)
6375 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
6378 else if (con
->response
->request
.status
.status_code
== IPP_OK
)
6379 SetJobPriority(jobid
, attr
->values
[0].integer
);
6381 else if (!strcmp(attr
->name
, "job-state"))
6384 * Change the job state...
6387 if (attr
->value_tag
!= IPP_TAG_ENUM
)
6389 send_ipp_error(con
, IPP_REQUEST_VALUE
);
6391 if ((attr2
= copy_attribute(con
->response
, attr
, 0)) != NULL
)
6392 attr2
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
6396 switch (attr
->values
[0].integer
)
6398 case IPP_JOB_PENDING
:
6400 if (job
->state
->values
[0].integer
> IPP_JOB_HELD
)
6402 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
6405 else if (con
->response
->request
.status
.status_code
== IPP_OK
)
6406 job
->state
->values
[0].integer
= attr
->values
[0].integer
;
6409 case IPP_JOB_PROCESSING
:
6410 case IPP_JOB_STOPPED
:
6411 if (job
->state
->values
[0].integer
!= attr
->values
[0].integer
)
6413 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
6418 case IPP_JOB_CANCELLED
:
6419 case IPP_JOB_ABORTED
:
6420 case IPP_JOB_COMPLETED
:
6421 if (job
->state
->values
[0].integer
> IPP_JOB_PROCESSING
)
6423 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
6426 else if (con
->response
->request
.status
.status_code
== IPP_OK
)
6428 CancelJob(job
->id
, 0);
6432 job
->state
->values
[0].integer
= attr
->values
[0].integer
;
6440 else if (con
->response
->request
.status
.status_code
!= IPP_OK
)
6442 else if ((attr2
= ippFindAttribute(job
->attrs
, attr
->name
, IPP_TAG_ZERO
)) != NULL
)
6445 * Some other value; first free the old value...
6448 if (job
->attrs
->attrs
== attr2
)
6450 job
->attrs
->attrs
= attr2
->next
;
6455 for (prev2
= job
->attrs
->attrs
; prev2
!= NULL
; prev2
= prev2
->next
)
6456 if (prev2
->next
== attr2
)
6458 prev2
->next
= attr2
->next
;
6463 if (job
->attrs
->last
== attr2
)
6464 job
->attrs
->last
= prev2
;
6466 _ipp_free_attr(attr2
);
6469 * Then copy the attribute...
6472 copy_attribute(job
->attrs
, attr
, 0);
6475 * See if the job-name or job-hold-until is being changed.
6478 if (strcmp(attr
->name
, "job-hold-until") == 0)
6480 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
6482 if (strcmp(attr
->values
[0].string
.text
, "no-hold") == 0)
6483 ReleaseJob(job
->id
);
6488 else if (attr
->value_tag
== IPP_TAG_DELETEATTR
)
6491 * Delete the attribute...
6494 for (attr2
= job
->attrs
->attrs
, prev2
= NULL
;
6496 prev2
= attr2
, attr2
= attr2
->next
)
6497 if (attr2
->name
&& strcmp(attr2
->name
, attr
->name
) == 0)
6503 prev2
->next
= attr2
->next
;
6505 job
->attrs
->attrs
= attr2
->next
;
6507 if (attr2
== job
->attrs
->last
)
6508 job
->attrs
->last
= prev2
;
6510 _ipp_free_attr(attr2
);
6516 * Add new option by copying it...
6519 copy_attribute(job
->attrs
, attr
, 0);
6530 * Start jobs if possible...
6538 * 'start_printer()' - Start a printer.
6542 start_printer(client_t
*con
, /* I - Client connection */
6543 ipp_attribute_t
*uri
) /* I - Printer URI */
6545 cups_ptype_t dtype
; /* Destination type (printer or class) */
6546 char method
[HTTP_MAX_URI
],
6547 /* Method portion of URI */
6548 username
[HTTP_MAX_URI
],
6549 /* Username portion of URI */
6551 /* Host portion of URI */
6552 resource
[HTTP_MAX_URI
];
6553 /* Resource portion of URI */
6554 int port
; /* Port portion of URI */
6555 const char *name
; /* Printer name */
6556 printer_t
*printer
; /* Printer data */
6559 LogMessage(L_DEBUG2
, "start_printer(%d, %s)\n", con
->http
.fd
,
6560 uri
->values
[0].string
.text
);
6563 * Was this operation called from the correct URI?
6566 if (strncmp(con
->uri
, "/admin/", 7) != 0)
6568 LogMessage(L_ERROR
, "start_printer: admin request on bad resource \'%s\'!",
6570 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
6575 * Is the destination valid?
6578 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6580 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
6586 LogMessage(L_ERROR
, "start_printer: resource name \'%s\' no good!", resource
);
6587 send_ipp_error(con
, IPP_NOT_FOUND
);
6592 * Start the printer...
6595 if (dtype
& CUPS_PRINTER_CLASS
)
6596 printer
= FindClass(name
);
6598 printer
= FindPrinter(name
);
6600 printer
->state_message
[0] = '\0';
6602 StartPrinter(printer
, 1);
6604 if (dtype
& CUPS_PRINTER_CLASS
)
6605 LogMessage(L_INFO
, "Class \'%s\' started by \'%s\'.", name
,
6607 LogMessage(L_INFO
, "Printer \'%s\' started by \'%s\'.", name
,
6613 * Everything was ok, so return OK status...
6616 con
->response
->request
.status
.status_code
= IPP_OK
;
6621 * 'stop_printer()' - Stop a printer.
6625 stop_printer(client_t
*con
, /* I - Client connection */
6626 ipp_attribute_t
*uri
) /* I - Printer URI */
6628 cups_ptype_t dtype
; /* Destination type (printer or class) */
6629 char method
[HTTP_MAX_URI
],
6630 /* Method portion of URI */
6631 username
[HTTP_MAX_URI
],
6632 /* Username portion of URI */
6634 /* Host portion of URI */
6635 resource
[HTTP_MAX_URI
];
6636 /* Resource portion of URI */
6637 int port
; /* Port portion of URI */
6638 const char *name
; /* Printer name */
6639 printer_t
*printer
; /* Printer data */
6640 ipp_attribute_t
*attr
; /* printer-state-message attribute */
6643 LogMessage(L_DEBUG2
, "stop_printer(%d, %s)\n", con
->http
.fd
,
6644 uri
->values
[0].string
.text
);
6647 * Was this operation called from the correct URI?
6650 if (strncmp(con
->uri
, "/admin/", 7) != 0)
6652 LogMessage(L_ERROR
, "stop_printer: admin request on bad resource \'%s\'!",
6654 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
6659 * Is the destination valid?
6662 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6664 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
6670 LogMessage(L_ERROR
, "stop_printer: resource name \'%s\' no good!", resource
);
6671 send_ipp_error(con
, IPP_NOT_FOUND
);
6676 * Stop the printer...
6679 if (dtype
& CUPS_PRINTER_CLASS
)
6680 printer
= FindClass(name
);
6682 printer
= FindPrinter(name
);
6684 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
6685 IPP_TAG_TEXT
)) == NULL
)
6686 strcpy(printer
->state_message
, "Paused");
6689 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
6690 sizeof(printer
->state_message
));
6693 StopPrinter(printer
, 1);
6695 if (dtype
& CUPS_PRINTER_CLASS
)
6696 LogMessage(L_INFO
, "Class \'%s\' stopped by \'%s\'.", name
,
6699 LogMessage(L_INFO
, "Printer \'%s\' stopped by \'%s\'.", name
,
6703 * Everything was ok, so return OK status...
6706 con
->response
->request
.status
.status_code
= IPP_OK
;
6711 * 'validate_job()' - Validate printer options and destination.
6715 validate_job(client_t
*con
, /* I - Client connection */
6716 ipp_attribute_t
*uri
) /* I - Printer URI */
6718 ipp_attribute_t
*attr
; /* Current attribute */
6719 ipp_attribute_t
*format
; /* Document-format attribute */
6720 cups_ptype_t dtype
; /* Destination type (printer or class) */
6721 char method
[HTTP_MAX_URI
],
6722 /* Method portion of URI */
6723 username
[HTTP_MAX_URI
],
6724 /* Username portion of URI */
6726 /* Host portion of URI */
6727 resource
[HTTP_MAX_URI
];
6728 /* Resource portion of URI */
6729 int port
; /* Port portion of URI */
6730 char super
[MIME_MAX_SUPER
],
6731 /* Supertype of file */
6732 type
[MIME_MAX_TYPE
];
6733 /* Subtype of file */
6736 LogMessage(L_DEBUG2
, "validate_job(%d, %s)\n", con
->http
.fd
,
6737 uri
->values
[0].string
.text
);
6740 * Verify that the POST operation was done to a valid URI.
6743 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
6744 strncmp(con
->uri
, "/printers/", 10) != 0)
6746 LogMessage(L_ERROR
, "validate_job: request on bad resource \'%s\'!",
6748 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
6753 * OK, see if the client is sending the document compressed - CUPS
6754 * doesn't support compression yet...
6757 if ((attr
= ippFindAttribute(con
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
&&
6758 strcmp(attr
->values
[0].string
.text
, "none") == 0)
6760 LogMessage(L_ERROR
, "validate_job: Unsupported compression attribute %s!",
6761 attr
->values
[0].string
.text
);
6762 send_ipp_error(con
, IPP_ATTRIBUTES
);
6763 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
6764 "compression", NULL
, attr
->values
[0].string
.text
);
6769 * Is it a format we support?
6772 if ((format
= ippFindAttribute(con
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
6774 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
, type
) != 2)
6776 LogMessage(L_ERROR
, "validate_job: could not scan type \'%s\'!\n",
6777 format
->values
[0].string
.text
);
6778 send_ipp_error(con
, IPP_BAD_REQUEST
);
6782 if ((strcmp(super
, "application") != 0 ||
6783 strcmp(type
, "octet-stream") != 0) &&
6784 mimeType(MimeDatabase
, super
, type
) == NULL
)
6786 LogMessage(L_ERROR
, "validate_job: Unsupported format \'%s\'!\n",
6787 format
->values
[0].string
.text
);
6788 LogMessage(L_INFO
, "Hint: Do you have the raw file printing rules enabled?");
6789 send_ipp_error(con
, IPP_DOCUMENT_FORMAT
);
6790 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
6791 "document-format", NULL
, format
->values
[0].string
.text
);
6797 * Is the destination valid?
6800 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6802 if (ValidateDest(host
, resource
, &dtype
) == NULL
)
6808 LogMessage(L_ERROR
, "validate_job: resource name \'%s\' no good!", resource
);
6809 send_ipp_error(con
, IPP_NOT_FOUND
);
6814 * Everything was ok, so return OK status...
6817 con
->response
->request
.status
.status_code
= IPP_OK
;
6822 * 'validate_user()' - Validate the user for the request.
6825 static int /* O - 1 if permitted, 0 otherwise */
6826 validate_user(client_t
*con
, /* I - Client connection */
6827 const char *owner
, /* I - Owner of job/resource */
6828 char *username
, /* O - Authenticated username */
6829 int userlen
) /* I - Length of username */
6831 int i
, j
; /* Looping vars */
6832 ipp_attribute_t
*attr
; /* requesting-user-name attribute */
6833 struct passwd
*user
; /* User info */
6834 struct group
*group
; /* System group info */
6835 char junk
[33]; /* MD5 password (not used) */
6838 LogMessage(L_DEBUG2
, "validate_user(%d, %s, %s, %d)\n", con
->http
.fd
,
6839 owner
, username
, userlen
);
6845 if (con
== NULL
|| owner
== NULL
|| username
== NULL
|| userlen
<= 0)
6849 * Get the best authenticated username that is available.
6852 if (con
->username
[0])
6853 strlcpy(username
, con
->username
, userlen
);
6854 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
6855 strlcpy(username
, attr
->values
[0].string
.text
, userlen
);
6857 strlcpy(username
, "anonymous", userlen
);
6860 * Check the username against the owner...
6863 if (strcasecmp(username
, owner
) != 0 && strcasecmp(username
, "root") != 0)
6866 * Not the owner or root; check to see if the user is a member of the
6870 user
= getpwnam(username
);
6873 for (i
= 0, j
= 0, group
= NULL
; i
< NumSystemGroups
; i
++)
6875 group
= getgrnam(SystemGroups
[i
]);
6880 for (j
= 0; group
->gr_mem
[j
]; j
++)
6881 if (strcasecmp(username
, group
->gr_mem
[j
]) == 0)
6884 if (group
->gr_mem
[j
])
6891 if (user
== NULL
|| group
== NULL
||
6892 (group
->gr_mem
[j
] == NULL
&& group
->gr_gid
!= user
->pw_gid
))
6895 * Username not found, group not found, or user is not part of the
6896 * system group... Check for a user and group in the MD5 password
6900 for (i
= 0; i
< NumSystemGroups
; i
++)
6901 if (GetMD5Passwd(username
, SystemGroups
[i
], junk
) != NULL
)
6905 * Nope, not an MD5 user, either. Return 0 indicating no-go...
6917 * End of "$Id: ipp.c,v 1.127.2.78 2004/02/17 21:32:58 mike Exp $".