2 * "$Id: ipp.c,v 1.127.2.58 2003/04/08 03:48:08 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 * PPD default choice structure...
92 char option
[PPD_MAX_NAME
]; /* Main keyword (option name) */
93 char choice
[PPD_MAX_NAME
]; /* Option keyword (choice name) */
102 static void accept_jobs(client_t
*con
, ipp_attribute_t
*uri
);
103 static void add_class(client_t
*con
, ipp_attribute_t
*uri
);
104 static int add_file(client_t
*con
, job_t
*job
, mime_type_t
*filetype
,
106 static void add_job_state_reasons(client_t
*con
, job_t
*job
);
107 static void add_printer(client_t
*con
, ipp_attribute_t
*uri
);
108 static void add_printer_state_reasons(client_t
*con
, printer_t
*p
);
109 static void add_queued_job_count(client_t
*con
, printer_t
*p
);
110 static void cancel_all_jobs(client_t
*con
, ipp_attribute_t
*uri
);
111 static void cancel_job(client_t
*con
, ipp_attribute_t
*uri
);
112 static int check_quotas(client_t
*con
, printer_t
*p
);
113 static void copy_attribute(ipp_t
*to
, ipp_attribute_t
*attr
,
115 static void copy_attrs(ipp_t
*to
, ipp_t
*from
, ipp_attribute_t
*req
,
116 ipp_tag_t group
, int quickcopy
);
117 static int copy_banner(client_t
*con
, job_t
*job
, const char *name
);
118 static int copy_file(const char *from
, const char *to
);
119 static int copy_model(const char *from
, const char *to
);
120 static void create_job(client_t
*con
, ipp_attribute_t
*uri
);
121 static void delete_printer(client_t
*con
, ipp_attribute_t
*uri
);
122 static void get_default(client_t
*con
);
123 static void get_devices(client_t
*con
);
124 static void get_jobs(client_t
*con
, ipp_attribute_t
*uri
);
125 static void get_job_attrs(client_t
*con
, ipp_attribute_t
*uri
);
126 static void get_ppds(client_t
*con
);
127 static void get_printers(client_t
*con
, int type
);
128 static void get_printer_attrs(client_t
*con
, ipp_attribute_t
*uri
);
129 static void hold_job(client_t
*con
, ipp_attribute_t
*uri
);
130 static void move_job(client_t
*con
, ipp_attribute_t
*uri
);
131 static int ppd_add_default(const char *option
, const char *choice
,
132 int num_defaults
, ppd_default_t
**defaults
);
133 static int ppd_parse_line(const char *line
, char *option
, int olen
,
134 char *choice
, int clen
);
135 static void print_job(client_t
*con
, ipp_attribute_t
*uri
);
136 static void read_ps_job_ticket(client_t
*con
);
137 static void reject_jobs(client_t
*con
, ipp_attribute_t
*uri
);
138 static void release_job(client_t
*con
, ipp_attribute_t
*uri
);
139 static void restart_job(client_t
*con
, ipp_attribute_t
*uri
);
140 static void send_document(client_t
*con
, ipp_attribute_t
*uri
);
141 static void send_ipp_error(client_t
*con
, ipp_status_t status
);
142 static void set_default(client_t
*con
, ipp_attribute_t
*uri
);
143 static void set_job_attrs(client_t
*con
, ipp_attribute_t
*uri
);
144 static void start_printer(client_t
*con
, ipp_attribute_t
*uri
);
145 static void stop_printer(client_t
*con
, ipp_attribute_t
*uri
);
146 static void validate_job(client_t
*con
, ipp_attribute_t
*uri
);
147 static int validate_user(client_t
*con
, const char *owner
, char *username
,
152 * 'ProcessIPPRequest()' - Process an incoming IPP request...
155 int /* O - 1 on success, 0 on failure */
156 ProcessIPPRequest(client_t
*con
) /* I - Client connection */
158 ipp_tag_t group
; /* Current group tag */
159 ipp_attribute_t
*attr
; /* Current attribute */
160 ipp_attribute_t
*charset
; /* Character set attribute */
161 ipp_attribute_t
*language
; /* Language attribute */
162 ipp_attribute_t
*uri
; /* Printer URI attribute */
163 ipp_attribute_t
*username
; /* requesting-user-name attr */
166 DEBUG_printf(("ProcessIPPRequest(%08x)\n", con
));
167 DEBUG_printf(("ProcessIPPRequest: operation_id = %04x\n",
168 con
->request
->request
.op
.operation_id
));
171 * First build an empty response message for this request...
174 con
->response
= ippNew();
176 con
->response
->request
.status
.version
[0] = con
->request
->request
.op
.version
[0];
177 con
->response
->request
.status
.version
[1] = con
->request
->request
.op
.version
[1];
178 con
->response
->request
.status
.request_id
= con
->request
->request
.op
.request_id
;
181 * Then validate the request header and required attributes...
184 if (con
->request
->request
.any
.version
[0] != 1)
187 * Return an error, since we only support IPP 1.x.
190 LogMessage(L_ERROR
, "ProcessIPPRequest: bad request version (%d.%d)!",
191 con
->request
->request
.any
.version
[0],
192 con
->request
->request
.any
.version
[1]);
194 send_ipp_error(con
, IPP_VERSION_NOT_SUPPORTED
);
196 else if (con
->request
->attrs
== NULL
)
198 LogMessage(L_ERROR
, "ProcessIPPRequest: no attributes in request!");
199 send_ipp_error(con
, IPP_BAD_REQUEST
);
204 * Make sure that the attributes are provided in the correct order and
205 * don't repeat groups...
208 for (attr
= con
->request
->attrs
, group
= attr
->group_tag
;
211 if (attr
->group_tag
< group
)
214 * Out of order; return an error...
217 LogMessage(L_ERROR
, "ProcessIPPRequest: attribute groups are out of order!");
218 send_ipp_error(con
, IPP_BAD_REQUEST
);
222 group
= attr
->group_tag
;
227 * Then make sure that the first three attributes are:
230 * attributes-natural-language
231 * printer-uri/job-uri
234 attr
= con
->request
->attrs
;
235 if (attr
!= NULL
&& strcmp(attr
->name
, "attributes-charset") == 0 &&
236 attr
->value_tag
== IPP_TAG_CHARSET
)
243 if (attr
!= NULL
&& strcmp(attr
->name
, "attributes-natural-language") == 0 &&
244 attr
->value_tag
== IPP_TAG_LANGUAGE
)
249 if ((attr
= ippFindAttribute(con
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
251 else if ((attr
= ippFindAttribute(con
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
257 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
258 "attributes-charset", NULL
, charset
->values
[0].string
.text
);
260 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
261 "attributes-charset", NULL
, DefaultCharset
);
264 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
265 "attributes-natural-language", NULL
,
266 language
->values
[0].string
.text
);
268 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
269 "attributes-natural-language", NULL
, DefaultLanguage
);
271 if (charset
== NULL
|| language
== NULL
||
273 con
->request
->request
.op
.operation_id
!= CUPS_GET_DEFAULT
&&
274 con
->request
->request
.op
.operation_id
!= CUPS_GET_PRINTERS
&&
275 con
->request
->request
.op
.operation_id
!= CUPS_GET_CLASSES
&&
276 con
->request
->request
.op
.operation_id
!= CUPS_GET_DEVICES
&&
277 con
->request
->request
.op
.operation_id
!= CUPS_GET_PPDS
))
280 * Return an error, since attributes-charset,
281 * attributes-natural-language, and printer-uri/job-uri are required
282 * for all operations.
286 LogMessage(L_ERROR
, "ProcessIPPRequest: missing attributes-charset attribute!");
288 if (language
== NULL
)
289 LogMessage(L_ERROR
, "ProcessIPPRequest: missing attributes-natural-language attribute!");
292 LogMessage(L_ERROR
, "ProcessIPPRequest: missing printer-uri or job-uri attribute!");
294 send_ipp_error(con
, IPP_BAD_REQUEST
);
299 * OK, all the checks pass so far; make sure requesting-user-name is
300 * not "root" from a remote host...
303 if ((username
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
306 * Check for root user...
309 if (strcmp(username
->values
[0].string
.text
, "root") == 0 &&
310 strcasecmp(con
->http
.hostname
, "localhost") != 0 &&
311 strcmp(con
->username
, "root") != 0)
314 * Remote unauthenticated user masquerading as local root...
317 free(username
->values
[0].string
.text
);
318 username
->values
[0].string
.text
= strdup(RemoteRoot
);
323 * Then try processing the operation...
326 switch (con
->request
->request
.op
.operation_id
)
332 case IPP_VALIDATE_JOB
:
333 validate_job(con
, uri
);
336 case IPP_CREATE_JOB
:
337 create_job(con
, uri
);
340 case IPP_SEND_DOCUMENT
:
341 send_document(con
, uri
);
344 case IPP_CANCEL_JOB
:
345 cancel_job(con
, uri
);
348 case IPP_GET_JOB_ATTRIBUTES
:
349 get_job_attrs(con
, uri
);
356 case IPP_GET_PRINTER_ATTRIBUTES
:
357 get_printer_attrs(con
, uri
);
364 case IPP_RELEASE_JOB
:
365 release_job(con
, uri
);
368 case IPP_RESTART_JOB
:
369 restart_job(con
, uri
);
372 case IPP_PAUSE_PRINTER
:
373 stop_printer(con
, uri
);
376 case IPP_RESUME_PRINTER
:
377 start_printer(con
, uri
);
380 case IPP_PURGE_JOBS
:
381 cancel_all_jobs(con
, uri
);
384 case IPP_SET_JOB_ATTRIBUTES
:
385 set_job_attrs(con
, uri
);
388 case CUPS_GET_DEFAULT
:
392 case CUPS_GET_PRINTERS
:
393 get_printers(con
, 0);
396 case CUPS_GET_CLASSES
:
397 get_printers(con
, CUPS_PRINTER_CLASS
);
400 case CUPS_ADD_PRINTER
:
401 add_printer(con
, uri
);
404 case CUPS_DELETE_PRINTER
:
405 delete_printer(con
, uri
);
408 case CUPS_ADD_CLASS
:
412 case CUPS_DELETE_CLASS
:
413 delete_printer(con
, uri
);
416 case CUPS_ACCEPT_JOBS
:
417 case IPP_ENABLE_PRINTER
:
418 accept_jobs(con
, uri
);
421 case CUPS_REJECT_JOBS
:
422 case IPP_DISABLE_PRINTER
:
423 reject_jobs(con
, uri
);
426 case CUPS_SET_DEFAULT
:
427 set_default(con
, uri
);
430 case CUPS_GET_DEVICES
:
443 send_ipp_error(con
, IPP_OPERATION_NOT_SUPPORTED
);
449 LogMessage(L_DEBUG
, "ProcessIPPRequest: %d status_code=%x",
450 con
->http
.fd
, con
->response
->request
.status
.status_code
);
452 if (SendHeader(con
, HTTP_OK
, "application/ipp"))
455 if (con
->http
.version
== HTTP_1_1
)
457 con
->http
.data_encoding
= HTTP_ENCODE_CHUNKED
;
459 httpPrintf(HTTP(con
), "Transfer-Encoding: chunked\r\n\r\n");
464 con
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
465 con
->http
.data_remaining
= ippLength(con
->response
);
467 httpPrintf(HTTP(con
), "Content-Length: %d\r\n\r\n",
468 con
->http
.data_remaining
);
471 LogMessage(L_DEBUG2
, "ProcessIPPRequest: Adding fd %d to OutputSet...",
474 FD_SET(con
->http
.fd
, OutputSet
);
477 * Tell the caller the response header was sent successfully...
485 * Tell the caller the response header could not be sent...
494 * 'accept_jobs()' - Accept print jobs to a printer.
498 accept_jobs(client_t
*con
, /* I - Client connection */
499 ipp_attribute_t
*uri
) /* I - Printer or class URI */
501 cups_ptype_t dtype
; /* Destination type (printer or class) */
502 char method
[HTTP_MAX_URI
],
503 /* Method portion of URI */
504 username
[HTTP_MAX_URI
],
505 /* Username portion of URI */
507 /* Host portion of URI */
508 resource
[HTTP_MAX_URI
];
509 /* Resource portion of URI */
510 int port
; /* Port portion of URI */
511 const char *name
; /* Printer name */
512 printer_t
*printer
; /* Printer data */
515 LogMessage(L_DEBUG2
, "accept_jobs(%d, %s)\n", con
->http
.fd
,
516 uri
->values
[0].string
.text
);
519 * Was this operation called from the correct URI?
522 if (strncmp(con
->uri
, "/admin/", 7) != 0)
524 LogMessage(L_ERROR
, "accept_jobs: admin request on bad resource \'%s\'!",
526 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
531 * Is the destination valid?
534 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
536 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
542 LogMessage(L_ERROR
, "accept_jobs: resource name \'%s\' no good!", resource
);
543 send_ipp_error(con
, IPP_NOT_FOUND
);
548 * Accept jobs sent to the printer...
551 if (dtype
& CUPS_PRINTER_CLASS
)
552 printer
= FindClass(name
);
554 printer
= FindPrinter(name
);
556 printer
->accepting
= 1;
557 printer
->state_message
[0] = '\0';
559 if (dtype
& CUPS_PRINTER_CLASS
)
564 LogMessage(L_INFO
, "Printer \'%s\' now accepting jobs (\'%s\').", name
,
568 * Everything was ok, so return OK status...
571 con
->response
->request
.status
.status_code
= IPP_OK
;
576 * 'add_class()' - Add a class to the system.
580 add_class(client_t
*con
, /* I - Client connection */
581 ipp_attribute_t
*uri
) /* I - URI of class */
583 int i
; /* Looping var */
584 char method
[HTTP_MAX_URI
],
585 /* Method portion of URI */
586 username
[HTTP_MAX_URI
],
587 /* Username portion of URI */
589 /* Host portion of URI */
590 resource
[HTTP_MAX_URI
];
591 /* Resource portion of URI */
592 int port
; /* Port portion of URI */
593 printer_t
*pclass
; /* Class */
594 cups_ptype_t dtype
; /* Destination type */
595 const char *dest
; /* Printer or class name */
596 ipp_attribute_t
*attr
; /* Printer attribute */
597 int modify
; /* Non-zero if we just modified */
600 LogMessage(L_DEBUG2
, "add_class(%d, %s)\n", con
->http
.fd
,
601 uri
->values
[0].string
.text
);
604 * Was this operation called from the correct URI?
607 if (strncmp(con
->uri
, "/admin/", 7) != 0)
609 LogMessage(L_ERROR
, "add_class: admin request on bad resource \'%s\'!",
611 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
615 DEBUG_printf(("add_class(%08x, %08x)\n", con
, uri
));
618 * Do we have a valid URI?
621 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
623 if (strncmp(resource
, "/classes/", 9) != 0 || strlen(resource
) == 9)
626 * No, return an error...
629 send_ipp_error(con
, IPP_BAD_REQUEST
);
634 * See if the class already exists; if not, create a new class...
637 if ((pclass
= FindClass(resource
+ 9)) == NULL
)
640 * Class doesn't exist; see if we have a printer of the same name...
643 if ((pclass
= FindPrinter(resource
+ 9)) != NULL
&&
644 !(pclass
->type
& CUPS_PRINTER_REMOTE
))
647 * Yes, return an error...
650 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
655 * No, add the pclass...
658 pclass
= AddClass(resource
+ 9);
661 else if (pclass
->type
& CUPS_PRINTER_IMPLICIT
)
664 * Rename the implicit class to "AnyClass" or remove it...
667 if (ImplicitAnyClasses
)
669 snprintf(pclass
->name
, sizeof(pclass
->name
), "Any%s", resource
+ 9);
673 DeletePrinter(pclass
);
676 * Add the class as a new local class...
679 pclass
= AddClass(resource
+ 9);
682 else if (pclass
->type
& CUPS_PRINTER_REMOTE
)
685 * Rename the remote class to "Class"...
688 DeletePrinterFilters(pclass
);
689 snprintf(pclass
->name
, sizeof(pclass
->name
), "%s@%s", resource
+ 9,
691 SetPrinterAttrs(pclass
);
695 * Add the class as a new local class...
698 pclass
= AddClass(resource
+ 9);
705 * Look for attributes and copy them over as needed...
708 if ((attr
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
709 SetString(&pclass
->location
, attr
->values
[0].string
.text
);
711 if ((attr
= ippFindAttribute(con
->request
, "printer-info", IPP_TAG_TEXT
)) != NULL
)
712 SetString(&pclass
->info
, attr
->values
[0].string
.text
);
714 if ((attr
= ippFindAttribute(con
->request
, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN
)) != NULL
)
716 LogMessage(L_INFO
, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
717 pclass
->name
, attr
->values
[0].boolean
, pclass
->accepting
);
719 pclass
->accepting
= attr
->values
[0].boolean
;
721 if ((attr
= ippFindAttribute(con
->request
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
723 LogMessage(L_INFO
, "Setting %s printer-state to %d (was %d.)", pclass
->name
,
724 attr
->values
[0].integer
, pclass
->state
);
726 if (pclass
->state
== IPP_PRINTER_STOPPED
&&
727 attr
->values
[0].integer
!= IPP_PRINTER_STOPPED
)
728 pclass
->state
= IPP_PRINTER_IDLE
;
729 else if (pclass
->state
!= IPP_PRINTER_STOPPED
&&
730 attr
->values
[0].integer
== IPP_PRINTER_STOPPED
)
732 if (pclass
->state
== IPP_PRINTER_PROCESSING
)
733 StopJob(((job_t
*)pclass
->job
)->id
, 0);
735 pclass
->state
= IPP_PRINTER_STOPPED
;
738 pclass
->browse_time
= 0;
740 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message", IPP_TAG_TEXT
)) != NULL
)
741 strlcpy(pclass
->state_message
, attr
->values
[0].string
.text
,
742 sizeof(pclass
->state_message
));
743 if ((attr
= ippFindAttribute(con
->request
, "job-sheets-default", IPP_TAG_ZERO
)) != NULL
&&
746 SetString(&pclass
->job_sheets
[0], attr
->values
[0].string
.text
);
747 if (attr
->num_values
> 1)
748 SetString(&pclass
->job_sheets
[1], attr
->values
[1].string
.text
);
750 SetString(&pclass
->job_sheets
[1], "none");
752 if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name-allowed",
753 IPP_TAG_ZERO
)) != NULL
)
755 FreePrinterUsers(pclass
);
757 pclass
->deny_users
= 0;
758 if (attr
->value_tag
== IPP_TAG_NAME
&&
759 (attr
->num_values
> 1 ||
760 strcmp(attr
->values
[0].string
.text
, "all") != 0))
761 for (i
= 0; i
< attr
->num_values
; i
++)
762 AddPrinterUser(pclass
, attr
->values
[i
].string
.text
);
764 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name-denied",
765 IPP_TAG_ZERO
)) != NULL
)
767 FreePrinterUsers(pclass
);
769 pclass
->deny_users
= 1;
770 if (attr
->value_tag
== IPP_TAG_NAME
&&
771 (attr
->num_values
> 1 ||
772 strcmp(attr
->values
[0].string
.text
, "none") != 0))
773 for (i
= 0; i
< attr
->num_values
; i
++)
774 AddPrinterUser(pclass
, attr
->values
[i
].string
.text
);
776 if ((attr
= ippFindAttribute(con
->request
, "job-quota-period",
777 IPP_TAG_INTEGER
)) != NULL
)
779 LogMessage(L_DEBUG
, "add_class: Setting job-quota-period to %d...",
780 attr
->values
[0].integer
);
782 pclass
->quota_period
= attr
->values
[0].integer
;
784 if ((attr
= ippFindAttribute(con
->request
, "job-k-limit",
785 IPP_TAG_INTEGER
)) != NULL
)
787 LogMessage(L_DEBUG
, "add_class: Setting job-k-limit to %d...",
788 attr
->values
[0].integer
);
790 pclass
->k_limit
= attr
->values
[0].integer
;
792 if ((attr
= ippFindAttribute(con
->request
, "job-page-limit",
793 IPP_TAG_INTEGER
)) != NULL
)
795 LogMessage(L_DEBUG
, "add_class: Setting job-page-limit to %d...",
796 attr
->values
[0].integer
);
798 pclass
->page_limit
= attr
->values
[0].integer
;
801 if ((attr
= ippFindAttribute(con
->request
, "member-uris", IPP_TAG_URI
)) != NULL
)
804 * Clear the printer array as needed...
807 if (pclass
->num_printers
> 0)
809 free(pclass
->printers
);
810 pclass
->num_printers
= 0;
814 * Add each printer or class that is listed...
817 for (i
= 0; i
< attr
->num_values
; i
++)
820 * Search for the printer or class URI...
823 httpSeparate(attr
->values
[i
].string
.text
, method
, username
, host
,
826 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
832 LogMessage(L_ERROR
, "add_class: resource name \'%s\' no good!", resource
);
833 send_ipp_error(con
, IPP_NOT_FOUND
);
838 * Add it to the class...
841 if (dtype
& CUPS_PRINTER_CLASS
)
842 AddPrinterToClass(pclass
, FindClass(dest
));
844 AddPrinterToClass(pclass
, FindPrinter(dest
));
849 * Update the printer class attributes and return...
852 SetPrinterAttrs(pclass
);
859 LogMessage(L_INFO
, "Class \'%s\' modified by \'%s\'.", pclass
->name
,
863 AddPrinterHistory(pclass
);
865 LogMessage(L_INFO
, "New class \'%s\' added by \'%s\'.", pclass
->name
,
869 con
->response
->request
.status
.status_code
= IPP_OK
;
874 * 'add_file()' - Add a file to a job.
877 static int /* O - 0 on success, -1 on error */
878 add_file(client_t
*con
, /* I - Connection to client */
879 job_t
*job
, /* I - Job to add to */
880 mime_type_t
*filetype
, /* I - Type of file */
881 int compression
) /* I - Compression */
883 mime_type_t
**filetypes
; /* New filetypes array... */
884 int *compressions
; /* New compressions array... */
887 LogMessage(L_DEBUG2
, "add_file(con=%p[%d], job=%d, filetype=%s/%s, compression=%d)\n",
888 con
, con
->http
.fd
, job
->id
, filetype
->super
, filetype
->type
,
892 * Add the file to the job...
895 if (job
->num_files
== 0)
897 compressions
= (int *)malloc(sizeof(int));
898 filetypes
= (mime_type_t
**)malloc(sizeof(mime_type_t
*));
902 compressions
= (int *)realloc(job
->compressions
,
903 (job
->num_files
+ 1) * sizeof(int));
904 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
905 (job
->num_files
+ 1) *
906 sizeof(mime_type_t
*));
909 if (compressions
== NULL
|| filetypes
== NULL
)
911 CancelJob(job
->id
, 1);
912 LogMessage(L_ERROR
, "add_file: unable to allocate memory for file types!");
913 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
917 job
->compressions
= compressions
;
918 job
->compressions
[job
->num_files
] = compression
;
919 job
->filetypes
= filetypes
;
920 job
->filetypes
[job
->num_files
] = filetype
;
929 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
930 * upon the job and printer state...
934 add_job_state_reasons(client_t
*con
, /* I - Client connection */
935 job_t
*job
) /* I - Job info */
937 printer_t
*dest
; /* Destination printer */
940 LogMessage(L_DEBUG2
, "add_job_state_reasons(%d, %d)\n", con
->http
.fd
,
943 switch (job
->state
->values
[0].integer
)
945 case IPP_JOB_PENDING
:
946 if (job
->dtype
& CUPS_PRINTER_CLASS
)
947 dest
= FindClass(job
->dest
);
949 dest
= FindPrinter(job
->dest
);
951 if (dest
!= NULL
&& dest
->state
== IPP_PRINTER_STOPPED
)
952 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
953 "job-state-reasons", NULL
, "printer-stopped");
955 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
956 "job-state-reasons", NULL
, "none");
960 if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
) != NULL
||
961 ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
) != NULL
)
962 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
963 "job-state-reasons", NULL
, "job-hold-until-specified");
965 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
966 "job-state-reasons", NULL
, "job-incoming");
969 case IPP_JOB_PROCESSING
:
970 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
971 "job-state-reasons", NULL
, "job-printing");
974 case IPP_JOB_STOPPED
:
975 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
976 "job-state-reasons", NULL
, "job-stopped");
979 case IPP_JOB_CANCELLED
:
980 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
981 "job-state-reasons", NULL
, "job-canceled-by-user");
984 case IPP_JOB_ABORTED
:
985 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
986 "job-state-reasons", NULL
, "aborted-by-system");
989 case IPP_JOB_COMPLETED
:
990 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
991 "job-state-reasons", NULL
, "job-completed-successfully");
998 * 'add_printer()' - Add a printer to the system.
1002 add_printer(client_t
*con
, /* I - Client connection */
1003 ipp_attribute_t
*uri
) /* I - URI of printer */
1005 int i
; /* Looping var */
1006 char method
[HTTP_MAX_URI
],
1007 /* Method portion of URI */
1008 username
[HTTP_MAX_URI
],
1009 /* Username portion of URI */
1011 /* Host portion of URI */
1012 resource
[HTTP_MAX_URI
];
1013 /* Resource portion of URI */
1014 int port
; /* Port portion of URI */
1015 printer_t
*printer
; /* Printer/class */
1016 ipp_attribute_t
*attr
; /* Printer attribute */
1017 cups_file_t
*fp
; /* Script/PPD file */
1018 char line
[1024]; /* Line from file... */
1019 char srcfile
[1024], /* Source Script/PPD file */
1020 dstfile
[1024]; /* Destination Script/PPD file */
1021 int modify
; /* Non-zero if we are modifying */
1024 LogMessage(L_DEBUG2
, "add_printer(%d, %s)\n", con
->http
.fd
,
1025 uri
->values
[0].string
.text
);
1028 * Was this operation called from the correct URI?
1031 if (strncmp(con
->uri
, "/admin/", 7) != 0)
1033 LogMessage(L_ERROR
, "add_printer: admin request on bad resource \'%s\'!",
1035 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1040 * Do we have a valid URI?
1043 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1045 if (strncmp(resource
, "/printers/", 10) != 0 || strlen(resource
) == 10)
1048 * No, return an error...
1051 LogMessage(L_ERROR
, "add_printer: bad printer URI \"%s\"!",
1052 uri
->values
[0].string
.text
);
1053 send_ipp_error(con
, IPP_BAD_REQUEST
);
1058 * See if the printer already exists; if not, create a new printer...
1061 if ((printer
= FindPrinter(resource
+ 10)) == NULL
)
1064 * Printer doesn't exist; see if we have a class of the same name...
1067 if ((printer
= FindClass(resource
+ 10)) != NULL
&&
1068 !(printer
->type
& CUPS_PRINTER_REMOTE
))
1071 * Yes, return an error...
1074 LogMessage(L_ERROR
, "add_printer: \"%s\" already exists as a class!",
1076 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1081 * No, add the printer...
1084 printer
= AddPrinter(resource
+ 10);
1087 else if (printer
->type
& CUPS_PRINTER_IMPLICIT
)
1090 * Rename the implicit printer to "AnyPrinter" or delete it...
1093 if (ImplicitAnyClasses
)
1095 snprintf(printer
->name
, sizeof(printer
->name
), "Any%s", resource
+ 10);
1099 DeletePrinter(printer
);
1102 * Add the printer as a new local printer...
1105 printer
= AddPrinter(resource
+ 10);
1108 else if (printer
->type
& CUPS_PRINTER_REMOTE
)
1111 * Rename the remote printer to "Printer@server"...
1114 DeletePrinterFilters(printer
);
1115 snprintf(printer
->name
, sizeof(printer
->name
), "%s@%s", resource
+ 10,
1117 SetPrinterAttrs(printer
);
1121 * Add the printer as a new local printer...
1124 printer
= AddPrinter(resource
+ 10);
1131 * Look for attributes and copy them over as needed...
1134 if ((attr
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
1135 SetString(&printer
->location
, attr
->values
[0].string
.text
);
1137 if ((attr
= ippFindAttribute(con
->request
, "printer-info", IPP_TAG_TEXT
)) != NULL
)
1138 SetString(&printer
->info
, attr
->values
[0].string
.text
);
1140 if ((attr
= ippFindAttribute(con
->request
, "device-uri", IPP_TAG_URI
)) != NULL
)
1142 ipp_attribute_t
*device
; /* Current device */
1143 int methodlen
; /* Length of method string */
1147 * Do we have a valid device URI?
1150 httpSeparate(attr
->values
[0].string
.text
, method
, username
, host
,
1152 methodlen
= strlen(method
);
1154 if (strcmp(method
, "file") == 0)
1157 * See if the administrator has enabled file devices...
1160 if (!FileDevice
&& strcmp(resource
, "/dev/null"))
1163 * File devices are disabled and the URL is not file:/dev/null...
1166 LogMessage(L_ERROR
, "add_printer: File device URIs have been disabled! "
1167 "To enable, see the FileDevice directive in cupsd.conf.");
1168 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1175 * See if the backend is listed as a device...
1178 for (device
= ippFindAttribute(Devices
, "device-uri", IPP_TAG_URI
);
1180 device
= ippFindNextAttribute(Devices
, "device-uri", IPP_TAG_URI
))
1181 if (strncmp(method
, device
->values
[0].string
.text
, methodlen
) == 0 &&
1182 (device
->values
[0].string
.text
[methodlen
] == ':' ||
1183 device
->values
[0].string
.text
[methodlen
] == '\0'))
1189 * Could not find device in list!
1192 LogMessage(L_ERROR
, "add_printer: bad device-uri attribute \'%s\'!",
1193 attr
->values
[0].string
.text
);
1194 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1199 LogMessage(L_INFO
, "Setting %s device-uri to \"%s\" (was \"%s\".)",
1200 printer
->name
, attr
->values
[0].string
.text
, printer
->device_uri
);
1202 SetString(&printer
->device_uri
, attr
->values
[0].string
.text
);
1205 if ((attr
= ippFindAttribute(con
->request
, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN
)) != NULL
)
1207 LogMessage(L_INFO
, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
1208 printer
->name
, attr
->values
[0].boolean
, printer
->accepting
);
1210 printer
->accepting
= attr
->values
[0].boolean
;
1212 if ((attr
= ippFindAttribute(con
->request
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
1214 LogMessage(L_INFO
, "Setting %s printer-state to %d (was %d.)", printer
->name
,
1215 attr
->values
[0].integer
, printer
->state
);
1217 if (printer
->state
== IPP_PRINTER_STOPPED
&&
1218 attr
->values
[0].integer
!= IPP_PRINTER_STOPPED
)
1219 printer
->state
= IPP_PRINTER_IDLE
;
1220 else if (printer
->state
!= IPP_PRINTER_STOPPED
&&
1221 attr
->values
[0].integer
== IPP_PRINTER_STOPPED
)
1223 if (printer
->state
== IPP_PRINTER_PROCESSING
)
1224 StopJob(((job_t
*)printer
->job
)->id
, 0);
1226 printer
->state
= IPP_PRINTER_STOPPED
;
1229 printer
->browse_time
= 0;
1231 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message", IPP_TAG_TEXT
)) != NULL
)
1232 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
1233 sizeof(printer
->state_message
));
1234 if ((attr
= ippFindAttribute(con
->request
, "job-sheets-default", IPP_TAG_ZERO
)) != NULL
&&
1237 SetString(&printer
->job_sheets
[0], attr
->values
[0].string
.text
);
1238 if (attr
->num_values
> 1)
1239 SetString(&printer
->job_sheets
[1], attr
->values
[1].string
.text
);
1241 SetString(&printer
->job_sheets
[1], "none");
1243 if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name-allowed",
1244 IPP_TAG_ZERO
)) != NULL
)
1246 FreePrinterUsers(printer
);
1248 printer
->deny_users
= 0;
1249 if (attr
->value_tag
== IPP_TAG_NAME
&&
1250 (attr
->num_values
> 1 ||
1251 strcmp(attr
->values
[0].string
.text
, "all") != 0))
1252 for (i
= 0; i
< attr
->num_values
; i
++)
1253 AddPrinterUser(printer
, attr
->values
[i
].string
.text
);
1255 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name-denied",
1256 IPP_TAG_ZERO
)) != NULL
)
1258 FreePrinterUsers(printer
);
1260 printer
->deny_users
= 1;
1261 if (attr
->value_tag
== IPP_TAG_NAME
&&
1262 (attr
->num_values
> 1 ||
1263 strcmp(attr
->values
[0].string
.text
, "none") != 0))
1264 for (i
= 0; i
< attr
->num_values
; i
++)
1265 AddPrinterUser(printer
, attr
->values
[i
].string
.text
);
1267 if ((attr
= ippFindAttribute(con
->request
, "job-quota-period",
1268 IPP_TAG_INTEGER
)) != NULL
)
1270 LogMessage(L_DEBUG
, "add_printer: Setting job-quota-period to %d...",
1271 attr
->values
[0].integer
);
1272 FreeQuotas(printer
);
1273 printer
->quota_period
= attr
->values
[0].integer
;
1275 if ((attr
= ippFindAttribute(con
->request
, "job-k-limit",
1276 IPP_TAG_INTEGER
)) != NULL
)
1278 LogMessage(L_DEBUG
, "add_printer: Setting job-k-limit to %d...",
1279 attr
->values
[0].integer
);
1280 FreeQuotas(printer
);
1281 printer
->k_limit
= attr
->values
[0].integer
;
1283 if ((attr
= ippFindAttribute(con
->request
, "job-page-limit",
1284 IPP_TAG_INTEGER
)) != NULL
)
1286 LogMessage(L_DEBUG
, "add_printer: Setting job-page-limit to %d...",
1287 attr
->values
[0].integer
);
1288 FreeQuotas(printer
);
1289 printer
->page_limit
= attr
->values
[0].integer
;
1293 * See if we have all required attributes...
1296 if (!printer
->device_uri
)
1297 SetString(&printer
->device_uri
, "file:/dev/null");
1300 * See if we have an interface script or PPD file attached to the request...
1305 strlcpy(srcfile
, con
->filename
, sizeof(srcfile
));
1307 if ((fp
= cupsFileOpen(srcfile
, "rb")) != NULL
)
1310 * Yes; get the first line from it...
1314 cupsFileGets(fp
, line
, sizeof(line
));
1318 * Then see what kind of file it is...
1321 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
1324 if (strncmp(line
, "*PPD-Adobe", 10) == 0)
1327 * The new file is a PPD file, so remove any old interface script
1328 * that might be lying around...
1336 * This must be an interface script, so move the file over to the
1337 * interfaces directory and make it executable...
1340 if (copy_file(srcfile
, dstfile
))
1342 LogMessage(L_ERROR
, "add_printer: Unable to copy interface script from %s to %s - %s!",
1343 srcfile
, dstfile
, strerror(errno
));
1344 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
1349 LogMessage(L_DEBUG
, "add_printer: Copied interface script successfully!");
1350 chmod(dstfile
, 0755);
1354 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
1357 if (strncmp(line
, "*PPD-Adobe", 10) == 0)
1360 * The new file is a PPD file, so move the file over to the
1361 * ppd directory and make it readable by all...
1364 if (copy_file(srcfile
, dstfile
))
1366 LogMessage(L_ERROR
, "add_printer: Unable to copy PPD file from %s to %s - %s!",
1367 srcfile
, dstfile
, strerror(errno
));
1368 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
1373 LogMessage(L_DEBUG
, "add_printer: Copied PPD file successfully!");
1374 chmod(dstfile
, 0644);
1380 * This must be an interface script, so remove any old PPD file that
1381 * may be lying around...
1388 else if ((attr
= ippFindAttribute(con
->request
, "ppd-name", IPP_TAG_NAME
)) != NULL
)
1390 if (strcmp(attr
->values
[0].string
.text
, "raw") == 0)
1393 * Raw driver, remove any existing PPD or interface script files.
1396 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
1400 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
1410 snprintf(srcfile
, sizeof(srcfile
), "%s/model/%s", DataDir
,
1411 attr
->values
[0].string
.text
);
1413 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
1417 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
1420 if (copy_model(srcfile
, dstfile
))
1422 LogMessage(L_ERROR
, "add_printer: Unable to copy PPD file from %s to %s - %s!",
1423 srcfile
, dstfile
, strerror(errno
));
1424 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
1429 LogMessage(L_DEBUG
, "add_printer: Copied PPD file successfully!");
1430 chmod(dstfile
, 0644);
1436 * Make this printer the default if there is none...
1439 if (DefaultPrinter
== NULL
)
1440 DefaultPrinter
= printer
;
1443 * Update the printer attributes and return...
1446 SetPrinterAttrs(printer
);
1449 if (printer
->job
!= NULL
)
1454 * Stop the current job and then restart it below...
1457 job
= (job_t
*)printer
->job
;
1459 StopJob(job
->id
, 1);
1460 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1468 LogMessage(L_INFO
, "Printer \'%s\' modified by \'%s\'.", printer
->name
,
1472 AddPrinterHistory(printer
);
1474 LogMessage(L_INFO
, "New printer \'%s\' added by \'%s\'.", printer
->name
,
1478 con
->response
->request
.status
.status_code
= IPP_OK
;
1483 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
1484 * based upon the printer state...
1488 add_printer_state_reasons(client_t
*con
, /* I - Client connection */
1489 printer_t
*p
) /* I - Printer info */
1491 LogMessage(L_DEBUG2
, "add_printer_state_reasons(%d, %s)\n", con
->http
.fd
,
1494 if (p
->num_reasons
== 0)
1495 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1496 "printer-state-reasons", NULL
,
1497 p
->state
== IPP_PRINTER_STOPPED
? "paused" : "none");
1499 ippAddStrings(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1500 "printer-state-reasons", p
->num_reasons
, NULL
,
1501 (const char * const *)p
->reasons
);
1506 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
1507 * the specified printer or class.
1511 add_queued_job_count(client_t
*con
, /* I - Client connection */
1512 printer_t
*p
) /* I - Printer or class */
1514 int count
; /* Number of jobs on destination */
1517 LogMessage(L_DEBUG2
, "add_queued_job_count(%d, %s)\n", con
->http
.fd
,
1520 count
= GetPrinterJobCount(p
->name
);
1522 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1523 "queued-job-count", count
);
1528 * 'cancel_all_jobs()' - Cancel all print jobs.
1532 cancel_all_jobs(client_t
*con
, /* I - Client connection */
1533 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
1535 const char *dest
; /* Destination */
1536 cups_ptype_t dtype
; /* Destination type */
1537 char method
[HTTP_MAX_URI
],
1538 /* Method portion of URI */
1539 userpass
[HTTP_MAX_URI
],
1540 /* Username portion of URI */
1542 /* Host portion of URI */
1543 resource
[HTTP_MAX_URI
];
1544 /* Resource portion of URI */
1545 int port
; /* Port portion of URI */
1546 ipp_attribute_t
*attr
; /* Attribute in request */
1547 const char *username
; /* Username */
1548 int purge
; /* Purge? */
1551 LogMessage(L_DEBUG2
, "cancel_all_jobs(%d, %s)\n", con
->http
.fd
,
1552 uri
->values
[0].string
.text
);
1555 * Was this operation called from the correct URI?
1558 if (strncmp(con
->uri
, "/admin/", 7) != 0)
1560 LogMessage(L_ERROR
, "cancel_all_jobs: admin request on bad resource \'%s\'!",
1562 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1567 * See if we have a printer URI...
1570 if (strcmp(uri
->name
, "printer-uri") != 0)
1572 LogMessage(L_ERROR
, "cancel_all_jobs: bad %s attribute \'%s\'!",
1573 uri
->name
, uri
->values
[0].string
.text
);
1574 send_ipp_error(con
, IPP_BAD_REQUEST
);
1579 * Get the username (if any) for the jobs we want to cancel (only if
1580 * "my-jobs" is specified...
1583 if ((attr
= ippFindAttribute(con
->request
, "my-jobs", IPP_TAG_BOOLEAN
)) != NULL
&&
1584 attr
->values
[0].boolean
)
1586 if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1587 username
= attr
->values
[0].string
.text
;
1590 LogMessage(L_ERROR
, "cancel_all_jobs: missing requesting-user-name attribute!");
1591 send_ipp_error(con
, IPP_BAD_REQUEST
);
1599 * Look for the "purge-jobs" attribute...
1602 if ((attr
= ippFindAttribute(con
->request
, "purge-jobs", IPP_TAG_BOOLEAN
)) != NULL
)
1603 purge
= attr
->values
[0].boolean
;
1608 * And if the destination is valid...
1611 httpSeparate(uri
->values
[0].string
.text
, method
, userpass
, host
, &port
,
1614 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
1620 if (strcmp(resource
, "/printers/") != 0)
1622 LogMessage(L_ERROR
, "cancel_all_jobs: resource name \'%s\' no good!", resource
);
1623 send_ipp_error(con
, IPP_NOT_FOUND
);
1628 * Cancel all jobs on all printers...
1631 CancelJobs(NULL
, username
, purge
);
1633 LogMessage(L_INFO
, "All jobs were %s by \'%s\'.",
1634 purge
? "purged" : "cancelled", con
->username
);
1639 * Cancel all of the jobs on the named printer...
1642 CancelJobs(dest
, username
, purge
);
1644 LogMessage(L_INFO
, "All jobs on \'%s\' were %s by \'%s\'.", dest
,
1645 purge
? "purged" : "cancelled", con
->username
);
1648 con
->response
->request
.status
.status_code
= IPP_OK
;
1653 * 'cancel_job()' - Cancel a print job.
1657 cancel_job(client_t
*con
, /* I - Client connection */
1658 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
1660 ipp_attribute_t
*attr
; /* Current attribute */
1661 int jobid
; /* Job ID */
1662 char method
[HTTP_MAX_URI
],
1663 /* Method portion of URI */
1664 username
[HTTP_MAX_URI
],
1665 /* Username portion of URI */
1667 /* Host portion of URI */
1668 resource
[HTTP_MAX_URI
];
1669 /* Resource portion of URI */
1670 int port
; /* Port portion of URI */
1671 job_t
*job
; /* Job information */
1672 const char *dest
; /* Destination */
1673 cups_ptype_t dtype
; /* Destination type (printer or class) */
1674 printer_t
*printer
; /* Printer data */
1677 LogMessage(L_DEBUG2
, "cancel_job(%d, %s)\n", con
->http
.fd
,
1678 uri
->values
[0].string
.text
);
1681 * Verify that the POST operation was done to a valid URI.
1684 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
1685 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
1686 strncmp(con
->uri
, "/printers/", 10) != 0)
1688 LogMessage(L_ERROR
, "cancel_job: cancel request on bad resource \'%s\'!",
1690 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1695 * See if we have a job URI or a printer URI...
1698 if (strcmp(uri
->name
, "printer-uri") == 0)
1701 * Got a printer URI; see if we also have a job-id attribute...
1704 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
1706 LogMessage(L_ERROR
, "cancel_job: got a printer-uri attribute but no job-id!");
1707 send_ipp_error(con
, IPP_BAD_REQUEST
);
1711 if ((jobid
= attr
->values
[0].integer
) == 0)
1714 * Find the current job on the specified printer...
1717 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1719 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
1725 LogMessage(L_ERROR
, "cancel_job: resource name \'%s\' no good!", resource
);
1726 send_ipp_error(con
, IPP_NOT_FOUND
);
1730 if (dtype
& CUPS_PRINTER_CLASS
)
1731 printer
= FindClass(dest
);
1733 printer
= FindPrinter(dest
);
1736 * See if the printer is currently printing a job...
1740 jobid
= ((job_t
*)printer
->job
)->id
;
1744 * No, see if there are any pending jobs...
1747 for (job
= Jobs
; job
!= NULL
; job
= job
->next
)
1748 if (job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
&&
1749 strcasecmp(job
->dest
, dest
) == 0)
1756 LogMessage(L_ERROR
, "cancel_job: No active jobs on %s!", dest
);
1757 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1766 * Got a job URI; parse it to get the job ID...
1769 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1771 if (strncmp(resource
, "/jobs/", 6) != 0)
1777 LogMessage(L_ERROR
, "cancel_job: bad job-uri attribute \'%s\'!",
1778 uri
->values
[0].string
.text
);
1779 send_ipp_error(con
, IPP_BAD_REQUEST
);
1783 jobid
= atoi(resource
+ 6);
1787 * See if the job exists...
1790 if ((job
= FindJob(jobid
)) == NULL
)
1793 * Nope - return a "not found" error...
1796 LogMessage(L_ERROR
, "cancel_job: job #%d doesn't exist!", jobid
);
1797 send_ipp_error(con
, IPP_NOT_FOUND
);
1802 * See if the job is owned by the requesting user...
1805 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
1807 LogMessage(L_ERROR
, "cancel_job: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
1808 username
, jobid
, job
->username
);
1809 send_ipp_error(con
, IPP_FORBIDDEN
);
1814 * See if the job is already completed, cancelled, or aborted; if so,
1815 * we can't cancel...
1818 if (job
->state
->values
[0].integer
>= IPP_JOB_CANCELLED
)
1820 LogMessage(L_ERROR
, "cancel_job: job id %d is %s - can't cancel!",
1822 job
->state
->values
[0].integer
== IPP_JOB_CANCELLED
? "cancelled" :
1823 job
->state
->values
[0].integer
== IPP_JOB_ABORTED
? "aborted" :
1825 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1830 * Cancel the job and return...
1833 CancelJob(jobid
, 0);
1836 LogMessage(L_INFO
, "Job %d was cancelled by \'%s\'.", jobid
, username
);
1838 con
->response
->request
.status
.status_code
= IPP_OK
;
1843 * 'check_quotas()' - Check quotas for a printer and user.
1846 static int /* O - 1 if OK, 0 if not */
1847 check_quotas(client_t
*con
, /* I - Client connection */
1848 printer_t
*p
) /* I - Printer or class */
1850 int i
, j
; /* Looping vars */
1851 ipp_attribute_t
*attr
; /* Current attribute */
1852 char username
[33]; /* Username */
1853 quota_t
*q
; /* Quota data */
1854 struct passwd
*pw
; /* User password data */
1855 struct group
*grp
; /* Group data */
1858 LogMessage(L_DEBUG2
, "check_quotas(%d, %s)\n", con
->http
.fd
, p
->name
);
1864 if (con
== NULL
|| p
== NULL
)
1868 * Figure out who is printing...
1871 attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
);
1873 if (con
->username
[0])
1874 strlcpy(username
, con
->username
, sizeof(username
));
1875 else if (attr
!= NULL
)
1877 LogMessage(L_DEBUG
, "check_quotas: requesting-user-name = \'%s\'",
1878 attr
->values
[0].string
.text
);
1880 strlcpy(username
, attr
->values
[0].string
.text
, sizeof(username
));
1883 strcpy(username
, "anonymous");
1886 * Check global active job limits for printers and users...
1889 if (MaxJobsPerPrinter
)
1892 * Check if there are too many pending jobs on this printer...
1895 if (GetPrinterJobCount(p
->name
) >= MaxJobsPerPrinter
)
1897 LogMessage(L_INFO
, "Too many jobs for printer \"%s\"...", p
->name
);
1905 * Check if there are too many pending jobs for this user...
1908 if (GetUserJobCount(username
) >= MaxJobsPerUser
)
1910 LogMessage(L_INFO
, "Too many jobs for user \"%s\"...", username
);
1916 * Check against users...
1919 if (p
->num_users
== 0 && p
->k_limit
== 0 && p
->page_limit
== 0)
1924 pw
= getpwnam(username
);
1927 for (i
= 0; i
< p
->num_users
; i
++)
1928 if (p
->users
[i
][0] == '@')
1931 * Check group membership...
1934 grp
= getgrnam(p
->users
[i
] + 1);
1940 * Check primary group...
1943 if (pw
&& grp
->gr_gid
== pw
->pw_gid
)
1947 * Check usernames in group...
1950 for (j
= 0; grp
->gr_mem
[j
]; j
++)
1951 if (!strcmp(username
, grp
->gr_mem
[j
]))
1958 else if (!strcasecmp(username
, p
->users
[i
]))
1961 if ((i
< p
->num_users
) == p
->deny_users
)
1963 LogMessage(L_INFO
, "Denying user \"%s\" access to printer \"%s\"...",
1973 if (p
->k_limit
|| p
->page_limit
)
1975 if ((q
= UpdateQuota(p
, username
, 0, 0)) == NULL
)
1977 LogMessage(L_ERROR
, "Unable to allocate quota data for user \"%s\"!",
1982 if ((q
->k_count
>= p
->k_limit
&& p
->k_limit
) ||
1983 (q
->page_count
>= p
->page_limit
&& p
->page_limit
))
1985 LogMessage(L_INFO
, "User \"%s\" is over the quota limit...",
1992 * If we have gotten this far, we're done!
2000 * 'copy_attribute()' - Copy a single attribute.
2004 copy_attribute(ipp_t
*to
, /* O - Destination request/response */
2005 ipp_attribute_t
*attr
, /* I - Attribute to copy */
2006 int quickcopy
)/* I - Do a quick copy? */
2008 int i
; /* Looping var */
2009 ipp_attribute_t
*toattr
; /* Destination attribute */
2012 LogMessage(L_DEBUG2
, "copy_attribute(%p, %s)\n", to
,
2013 attr
->name
? attr
->name
: "(null)");
2015 switch (attr
->value_tag
& ~IPP_TAG_COPY
)
2018 ippAddSeparator(to
);
2021 case IPP_TAG_INTEGER
:
2023 toattr
= ippAddIntegers(to
, attr
->group_tag
, attr
->value_tag
,
2024 attr
->name
, attr
->num_values
, NULL
);
2026 for (i
= 0; i
< attr
->num_values
; i
++)
2027 toattr
->values
[i
].integer
= attr
->values
[i
].integer
;
2030 case IPP_TAG_BOOLEAN
:
2031 toattr
= ippAddBooleans(to
, attr
->group_tag
, attr
->name
,
2032 attr
->num_values
, NULL
);
2034 for (i
= 0; i
< attr
->num_values
; i
++)
2035 toattr
->values
[i
].boolean
= attr
->values
[i
].boolean
;
2038 case IPP_TAG_STRING
:
2041 case IPP_TAG_KEYWORD
:
2043 case IPP_TAG_URISCHEME
:
2044 case IPP_TAG_CHARSET
:
2045 case IPP_TAG_LANGUAGE
:
2046 case IPP_TAG_MIMETYPE
:
2047 toattr
= ippAddStrings(to
, attr
->group_tag
,
2048 (ipp_tag_t
)(attr
->value_tag
| quickcopy
),
2049 attr
->name
, attr
->num_values
, NULL
, NULL
);
2053 for (i
= 0; i
< attr
->num_values
; i
++)
2054 toattr
->values
[i
].string
.text
= attr
->values
[i
].string
.text
;
2058 for (i
= 0; i
< attr
->num_values
; i
++)
2059 toattr
->values
[i
].string
.text
= strdup(attr
->values
[i
].string
.text
);
2064 toattr
= ippAddDate(to
, attr
->group_tag
, attr
->name
,
2065 attr
->values
[0].date
);
2068 case IPP_TAG_RESOLUTION
:
2069 toattr
= ippAddResolutions(to
, attr
->group_tag
, attr
->name
,
2070 attr
->num_values
, IPP_RES_PER_INCH
,
2073 for (i
= 0; i
< attr
->num_values
; i
++)
2075 toattr
->values
[i
].resolution
.xres
= attr
->values
[i
].resolution
.xres
;
2076 toattr
->values
[i
].resolution
.yres
= attr
->values
[i
].resolution
.yres
;
2077 toattr
->values
[i
].resolution
.units
= attr
->values
[i
].resolution
.units
;
2081 case IPP_TAG_RANGE
:
2082 toattr
= ippAddRanges(to
, attr
->group_tag
, attr
->name
,
2083 attr
->num_values
, NULL
, NULL
);
2085 for (i
= 0; i
< attr
->num_values
; i
++)
2087 toattr
->values
[i
].range
.lower
= attr
->values
[i
].range
.lower
;
2088 toattr
->values
[i
].range
.upper
= attr
->values
[i
].range
.upper
;
2092 case IPP_TAG_TEXTLANG
:
2093 case IPP_TAG_NAMELANG
:
2094 toattr
= ippAddStrings(to
, attr
->group_tag
,
2095 (ipp_tag_t
)(attr
->value_tag
| quickcopy
),
2096 attr
->name
, attr
->num_values
, NULL
, NULL
);
2100 for (i
= 0; i
< attr
->num_values
; i
++)
2102 toattr
->values
[i
].string
.charset
= attr
->values
[i
].string
.charset
;
2103 toattr
->values
[i
].string
.text
= attr
->values
[i
].string
.text
;
2108 for (i
= 0; i
< attr
->num_values
; i
++)
2111 toattr
->values
[i
].string
.charset
=
2112 strdup(attr
->values
[i
].string
.charset
);
2114 toattr
->values
[i
].string
.charset
=
2115 toattr
->values
[0].string
.charset
;
2117 toattr
->values
[i
].string
.text
= strdup(attr
->values
[i
].string
.text
);
2122 case IPP_TAG_BEGIN_COLLECTION
:
2123 toattr
= ippAddCollections(to
, attr
->group_tag
, attr
->name
,
2124 attr
->num_values
, NULL
);
2126 for (i
= 0; i
< attr
->num_values
; i
++)
2128 toattr
->values
[i
].collection
= ippNew();
2129 copy_attrs(toattr
->values
[i
].collection
, attr
->values
[i
].collection
,
2130 NULL
, IPP_TAG_ZERO
, 0);
2135 toattr
= ippAddIntegers(to
, attr
->group_tag
, attr
->value_tag
,
2136 attr
->name
, attr
->num_values
, NULL
);
2138 for (i
= 0; i
< attr
->num_values
; i
++)
2140 toattr
->values
[i
].unknown
.length
= attr
->values
[i
].unknown
.length
;
2142 if (toattr
->values
[i
].unknown
.length
> 0)
2144 if ((toattr
->values
[i
].unknown
.data
= malloc(toattr
->values
[i
].unknown
.length
)) == NULL
)
2145 toattr
->values
[i
].unknown
.length
= 0;
2147 memcpy(toattr
->values
[i
].unknown
.data
,
2148 attr
->values
[i
].unknown
.data
,
2149 toattr
->values
[i
].unknown
.length
);
2152 break; /* anti-compiler-warning-code */
2158 * 'copy_attrs()' - Copy attributes from one request to another.
2162 copy_attrs(ipp_t
*to
, /* I - Destination request */
2163 ipp_t
*from
, /* I - Source request */
2164 ipp_attribute_t
*req
, /* I - Requested attributes */
2165 ipp_tag_t group
, /* I - Group to copy */
2166 int quickcopy
) /* I - Do a quick copy? */
2168 int i
; /* Looping var */
2169 ipp_attribute_t
*fromattr
; /* Source attribute */
2172 LogMessage(L_DEBUG2
, "copy_attrs(%p, %p, %p, %x)\n", to
, from
, req
, group
);
2174 if (to
== NULL
|| from
== NULL
)
2177 if (req
!= NULL
&& strcmp(req
->values
[0].string
.text
, "all") == 0)
2178 req
= NULL
; /* "all" means no filter... */
2180 for (fromattr
= from
->attrs
; fromattr
!= NULL
; fromattr
= fromattr
->next
)
2183 * Filter attributes as needed...
2186 if (group
!= IPP_TAG_ZERO
&& fromattr
->group_tag
!= group
&&
2187 fromattr
->group_tag
!= IPP_TAG_ZERO
)
2190 if (req
!= NULL
&& fromattr
->name
!= NULL
)
2192 for (i
= 0; i
< req
->num_values
; i
++)
2193 if (strcmp(fromattr
->name
, req
->values
[i
].string
.text
) == 0)
2196 if (i
== req
->num_values
)
2200 copy_attribute(to
, fromattr
, quickcopy
);
2206 * 'copy_banner()' - Copy a banner file to the requests directory for the
2210 static int /* O - Size of banner file in kbytes */
2211 copy_banner(client_t
*con
, /* I - Client connection */
2212 job_t
*job
, /* I - Job information */
2213 const char *name
) /* I - Name of banner */
2215 int i
; /* Looping var */
2216 int kbytes
; /* Size of banner file in kbytes */
2217 char filename
[1024]; /* Job filename */
2218 banner_t
*banner
; /* Pointer to banner */
2219 cups_file_t
*in
; /* Input file */
2220 cups_file_t
*out
; /* Output file */
2221 int ch
; /* Character from file */
2222 char attrname
[255], /* Name of attribute */
2223 *s
; /* Pointer into name */
2224 ipp_attribute_t
*attr
; /* Attribute */
2227 LogMessage(L_DEBUG2
, "copy_banner(%d, %d, %s)\n", con
->http
.fd
, job
->id
,
2231 * Find the banner; return if not found or "none"...
2234 LogMessage(L_DEBUG
, "copy_banner(%p, %d, \"%s\")", con
, job
->id
,
2235 name
? name
: "(null)");
2238 strcmp(name
, "none") == 0 ||
2239 (banner
= FindBanner(name
)) == NULL
)
2243 * Open the banner and job files...
2246 if (add_file(con
, job
, banner
->filetype
, 0))
2249 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
2251 if ((out
= cupsFileOpen(filename
, "w")) == NULL
)
2253 LogMessage(L_ERROR
, "copy_banner: Unable to create banner job file %s - %s",
2254 filename
, strerror(errno
));
2259 fchmod(cupsFileNumber(out
), 0640);
2260 fchown(cupsFileNumber(out
), User
, Group
);
2265 * Try the localized banner file under the subdirectory...
2268 snprintf(filename
, sizeof(filename
), "%s/banners/%s/%s", DataDir
,
2269 con
->language
->language
, name
);
2271 if (access(filename
, 0) && con
->language
->language
[2])
2274 * Wasn't able to find "ll_CC" locale file; try the non-national
2275 * localization banner directory.
2278 attrname
[0] = con
->language
->language
[0];
2279 attrname
[1] = con
->language
->language
[1];
2282 snprintf(filename
, sizeof(filename
), "%s/banners/%s/%s", DataDir
,
2286 if (access(filename
, 0))
2289 * Use the non-localized banner file.
2292 snprintf(filename
, sizeof(filename
), "%s/banners/%s", DataDir
, name
);
2298 * Use the non-localized banner file.
2301 snprintf(filename
, sizeof(filename
), "%s/banners/%s", DataDir
, name
);
2304 if ((in
= cupsFileOpen(filename
, "r")) == NULL
)
2308 LogMessage(L_ERROR
, "copy_banner: Unable to open banner template file %s - %s",
2309 filename
, strerror(errno
));
2315 * Parse the file to the end...
2318 while ((ch
= cupsFileGetChar(in
)) != EOF
)
2322 * Get an attribute name...
2325 for (s
= attrname
; (ch
= cupsFileGetChar(in
)) != EOF
;)
2326 if (!isalpha(ch
) && ch
!= '-' && ch
!= '?')
2328 else if (s
< (attrname
+ sizeof(attrname
) - 1))
2338 * Ignore { followed by stuff that is not an attribute name...
2341 cupsFilePrintf(out
, "{%s}", attrname
);
2346 * See if it is defined...
2349 if (attrname
[0] == '?')
2354 if (strcmp(s
, "printer-name") == 0)
2356 cupsFilePuts(out
, job
->dest
);
2359 else if ((attr
= ippFindAttribute(job
->attrs
, s
, IPP_TAG_ZERO
)) == NULL
)
2362 * See if we have a leading question mark...
2365 if (attrname
[0] != '?')
2368 * Nope, write to file as-is; probably a PostScript procedure...
2371 cupsFilePrintf(out
, "{%s}", attrname
);
2378 * Output value(s)...
2381 for (i
= 0; i
< attr
->num_values
; i
++)
2384 cupsFilePutChar(out
, ',');
2386 switch (attr
->value_tag
)
2388 case IPP_TAG_INTEGER
:
2390 if (strncmp(s
, "time-at-", 8) == 0)
2391 cupsFilePuts(out
, GetDateTime(attr
->values
[i
].integer
));
2393 cupsFilePrintf(out
, "%d", attr
->values
[i
].integer
);
2396 case IPP_TAG_BOOLEAN
:
2397 cupsFilePrintf(out
, "%d", attr
->values
[i
].boolean
);
2400 case IPP_TAG_NOVALUE
:
2401 cupsFilePuts(out
, "novalue");
2404 case IPP_TAG_RANGE
:
2405 cupsFilePrintf(out
, "%d-%d", attr
->values
[i
].range
.lower
,
2406 attr
->values
[i
].range
.upper
);
2409 case IPP_TAG_RESOLUTION
:
2410 cupsFilePrintf(out
, "%dx%d%s", attr
->values
[i
].resolution
.xres
,
2411 attr
->values
[i
].resolution
.yres
,
2412 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
2417 case IPP_TAG_STRING
:
2420 case IPP_TAG_KEYWORD
:
2421 case IPP_TAG_CHARSET
:
2422 case IPP_TAG_LANGUAGE
:
2423 if (strcasecmp(banner
->filetype
->type
, "postscript") == 0)
2426 * Need to quote strings for PS banners...
2431 for (p
= attr
->values
[i
].string
.text
; *p
; p
++)
2433 if (*p
== '(' || *p
== ')' || *p
== '\\')
2435 cupsFilePutChar(out
, '\\');
2436 cupsFilePutChar(out
, *p
);
2438 else if (*p
< 32 || *p
> 126)
2439 cupsFilePrintf(out
, "\\%03o", *p
);
2441 cupsFilePutChar(out
, *p
);
2445 cupsFilePuts(out
, attr
->values
[i
].string
.text
);
2449 break; /* anti-compiler-warning-code */
2453 else if (ch
== '\\') /* Quoted char */
2455 ch
= cupsFileGetChar(in
);
2457 if (ch
!= '{') /* Only do special handling for \{ */
2458 cupsFilePutChar(out
, '\\');
2460 cupsFilePutChar(out
, ch
);
2463 cupsFilePutChar(out
, ch
);
2467 kbytes
= (cupsFileTell(out
) + 1023) / 1024;
2469 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
2470 attr
->values
[0].integer
+= kbytes
;
2479 * 'copy_file()' - Copy a PPD file or interface script...
2482 static int /* O - 0 = success, -1 = error */
2483 copy_file(const char *from
, /* I - Source file */
2484 const char *to
) /* I - Destination file */
2486 cups_file_t
*src
, /* Source file */
2487 *dst
; /* Destination file */
2488 int bytes
; /* Bytes to read/write */
2489 char buffer
[2048]; /* Copy buffer */
2492 LogMessage(L_DEBUG2
, "copy_file(\"%s\", \"%s\")\n", from
, to
);
2495 * Open the source and destination file for a copy...
2498 if ((src
= cupsFileOpen(from
, "rb")) == NULL
)
2501 if ((dst
= cupsFileOpen(to
, "wb")) == NULL
)
2508 * Copy the source file to the destination...
2511 while ((bytes
= cupsFileRead(src
, buffer
, sizeof(buffer
))) > 0)
2512 if (cupsFileWrite(dst
, buffer
, bytes
) < bytes
)
2520 * Close both files and return...
2525 return (cupsFileClose(dst
));
2530 * 'copy_model()' - Copy a PPD model file, substituting default values
2534 static int /* O - 0 = success, -1 = error */
2535 copy_model(const char *from
, /* I - Source file */
2536 const char *to
) /* I - Destination file */
2538 cups_file_t
*src
, /* Source file */
2539 *dst
; /* Destination file */
2540 char buffer
[2048]; /* Copy buffer */
2541 int i
; /* Looping var */
2542 char option
[PPD_MAX_NAME
], /* Option name */
2543 choice
[PPD_MAX_NAME
]; /* Choice name */
2544 int num_defaults
; /* Number of default options */
2545 ppd_default_t
*defaults
; /* Default options */
2548 LogMessage(L_DEBUG2
, "copy_model(\"%s\", \"%s\")\n", from
, to
);
2551 * Open the destination (if possible) and set the default options...
2557 if ((dst
= cupsFileOpen(to
, "rb")) != NULL
)
2560 * Read all of the default lines from the old PPD...
2563 while (cupsFileGets(src
, buffer
, sizeof(buffer
)) != NULL
)
2564 if (!strncmp(buffer
, "*Default", 8))
2567 * Add the default option...
2570 if (!ppd_parse_line(buffer
, option
, sizeof(option
),
2571 choice
, sizeof(choice
)))
2572 num_defaults
= ppd_add_default(option
, choice
, num_defaults
,
2581 * Add the default media sizes...
2583 * Note: These values are generally not valid for large-format devices
2584 * like plotters, however it is probably safe to say that those
2585 * users will configure the media size after initially adding
2586 * the device anyways...
2589 if (!DefaultLanguage
||
2590 !strcasecmp(DefaultLanguage
, "C") ||
2591 !strcasecmp(DefaultLanguage
, "POSIX") ||
2592 !strcasecmp(DefaultLanguage
, "en") ||
2593 !strncasecmp(DefaultLanguage
, "en_US", 5) ||
2594 !strncasecmp(DefaultLanguage
, "en_CA", 5) ||
2595 !strncasecmp(DefaultLanguage
, "fr_CA", 5))
2598 * These are the only locales that will default to "letter" size...
2601 num_defaults
= ppd_add_default("PageSize", "Letter", num_defaults
,
2603 num_defaults
= ppd_add_default("PageRegion", "Letter", num_defaults
,
2605 num_defaults
= ppd_add_default("PaperDimension", "Letter", num_defaults
,
2607 num_defaults
= ppd_add_default("ImageableArea", "Letter", num_defaults
,
2613 * The rest default to "a4" size...
2616 num_defaults
= ppd_add_default("PageSize", "A4", num_defaults
,
2618 num_defaults
= ppd_add_default("PageRegion", "A4", num_defaults
,
2620 num_defaults
= ppd_add_default("PaperDimension", "A4", num_defaults
,
2622 num_defaults
= ppd_add_default("ImageableArea", "A4", num_defaults
,
2628 * Open the source and destination file for a copy...
2631 if ((src
= cupsFileOpen(from
, "rb")) == NULL
)
2634 if ((dst
= cupsFileOpen(to
, "wb")) == NULL
)
2641 * Copy the source file to the destination...
2644 while (cupsFileGets(src
, buffer
, sizeof(buffer
)) != NULL
)
2646 if (!strncmp(buffer
, "*Default", 8))
2649 * Check for an previous default option choice...
2652 if (!ppd_parse_line(buffer
, option
, sizeof(option
),
2653 choice
, sizeof(choice
)))
2655 for (i
= 0; i
< num_defaults
; i
++)
2656 if (!strcmp(option
, defaults
[i
].option
))
2659 * Substitute the previous choice...
2662 snprintf(buffer
, sizeof(buffer
), "*Default%s: %s", option
,
2663 defaults
[i
].choice
);
2669 cupsFilePrintf(dst
, "%s\n", buffer
);
2673 * Close both files and return...
2678 return (cupsFileClose(dst
));
2683 * 'create_job()' - Print a file to a printer or class.
2687 create_job(client_t
*con
, /* I - Client connection */
2688 ipp_attribute_t
*uri
) /* I - Printer URI */
2690 ipp_attribute_t
*attr
; /* Current attribute */
2691 const char *dest
; /* Destination */
2692 cups_ptype_t dtype
; /* Destination type (printer or class) */
2693 int priority
; /* Job priority */
2694 char *title
; /* Job name/title */
2695 job_t
*job
; /* Current job */
2696 char job_uri
[HTTP_MAX_URI
],
2698 printer_uri
[HTTP_MAX_URI
],
2700 method
[HTTP_MAX_URI
],
2701 /* Method portion of URI */
2702 username
[HTTP_MAX_URI
],
2703 /* Username portion of URI */
2705 /* Host portion of URI */
2706 resource
[HTTP_MAX_URI
];
2707 /* Resource portion of URI */
2708 int port
; /* Port portion of URI */
2709 printer_t
*printer
; /* Printer data */
2710 int kbytes
; /* Size of print file */
2711 int i
; /* Looping var */
2712 int lowerpagerange
; /* Page range bound */
2715 LogMessage(L_DEBUG2
, "create_job(%d, %s)\n", con
->http
.fd
,
2716 uri
->values
[0].string
.text
);
2719 * Verify that the POST operation was done to a valid URI.
2722 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
2723 strncmp(con
->uri
, "/printers/", 10) != 0)
2725 LogMessage(L_ERROR
, "create_job: cancel request on bad resource \'%s\'!",
2727 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
2732 * Is the destination valid?
2735 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
2737 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
2743 LogMessage(L_ERROR
, "create_job: resource name \'%s\' no good!", resource
);
2744 send_ipp_error(con
, IPP_NOT_FOUND
);
2749 * See if the printer is accepting jobs...
2752 if (dtype
& CUPS_PRINTER_CLASS
)
2754 printer
= FindClass(dest
);
2757 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
2758 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/classes/%s",
2759 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), dest
);
2761 #endif /* AF_INET6 */
2762 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/classes/%s",
2763 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), dest
);
2767 printer
= FindPrinter(dest
);
2770 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
2771 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/printers/%s",
2772 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), dest
);
2774 #endif /* AF_INET6 */
2775 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/printers/%s",
2776 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), dest
);
2779 if (!printer
->accepting
)
2781 LogMessage(L_INFO
, "create_job: destination \'%s\' is not accepting jobs.",
2783 send_ipp_error(con
, IPP_NOT_ACCEPTING
);
2788 * Validate job template attributes; for now just copies and page-ranges...
2791 if ((attr
= ippFindAttribute(con
->request
, "copies", IPP_TAG_INTEGER
)) != NULL
)
2793 if (attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> MaxCopies
)
2795 LogMessage(L_INFO
, "create_job: bad copies value %d.",
2796 attr
->values
[0].integer
);
2797 send_ipp_error(con
, IPP_BAD_REQUEST
);
2802 if ((attr
= ippFindAttribute(con
->request
, "page-ranges", IPP_TAG_RANGE
)) != NULL
)
2804 for (i
= 0, lowerpagerange
= 1; i
< attr
->num_values
; i
++)
2806 if (attr
->values
[i
].range
.lower
< lowerpagerange
||
2807 attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
2809 LogMessage(L_ERROR
, "create_job: bad page-ranges values %d-%d.",
2810 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
2811 send_ipp_error(con
, IPP_BAD_REQUEST
);
2815 lowerpagerange
= attr
->values
[i
].range
.upper
+ 1;
2820 * Make sure we aren't over our limit...
2823 if (NumJobs
>= MaxJobs
&& MaxJobs
)
2826 if (NumJobs
>= MaxJobs
&& MaxJobs
)
2828 LogMessage(L_INFO
, "create_job: too many jobs.");
2829 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
2833 if (!check_quotas(con
, printer
))
2835 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
2840 * Create the job and set things up...
2843 if ((attr
= ippFindAttribute(con
->request
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
2844 priority
= attr
->values
[0].integer
;
2846 ippAddInteger(con
->request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
2849 if ((attr
= ippFindAttribute(con
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
2850 title
= attr
->values
[0].string
.text
;
2852 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
2853 title
= "Untitled");
2855 if ((job
= AddJob(priority
, printer
->name
)) == NULL
)
2857 LogMessage(L_ERROR
, "create_job: unable to add job for destination \'%s\'!",
2859 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
2864 job
->attrs
= con
->request
;
2865 con
->request
= NULL
;
2867 attr
= ippFindAttribute(job
->attrs
, "requesting-user-name", IPP_TAG_NAME
);
2869 if (con
->username
[0])
2870 SetString(&job
->username
, con
->username
);
2871 else if (attr
!= NULL
)
2873 LogMessage(L_DEBUG
, "create_job: requesting-user-name = \'%s\'",
2874 attr
->values
[0].string
.text
);
2876 SetString(&job
->username
, attr
->values
[0].string
.text
);
2879 SetString(&job
->username
, "anonymous");
2882 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name",
2883 NULL
, job
->username
);
2886 attr
->group_tag
= IPP_TAG_JOB
;
2887 SetString(&attr
->name
, "job-originating-user-name");
2890 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-host-name",
2891 IPP_TAG_ZERO
)) != NULL
)
2894 * Request contains a job-originating-host-name attribute; validate it...
2897 if (attr
->value_tag
!= IPP_TAG_NAME
||
2898 attr
->num_values
!= 1 ||
2899 strcmp(con
->http
.hostname
, "localhost") != 0)
2902 * Can't override the value if we aren't connected via localhost.
2903 * Also, we can only have 1 value and it must be a name value.
2906 int i
; /* Looping var */
2908 switch (attr
->value_tag
)
2910 case IPP_TAG_STRING
:
2911 case IPP_TAG_TEXTLANG
:
2912 case IPP_TAG_NAMELANG
:
2915 case IPP_TAG_KEYWORD
:
2917 case IPP_TAG_URISCHEME
:
2918 case IPP_TAG_CHARSET
:
2919 case IPP_TAG_LANGUAGE
:
2920 case IPP_TAG_MIMETYPE
:
2922 * Free old strings...
2925 for (i
= 0; i
< attr
->num_values
; i
++)
2927 free(attr
->values
[i
].string
.text
);
2928 attr
->values
[i
].string
.text
= NULL
;
2929 if (attr
->values
[i
].string
.charset
)
2931 free(attr
->values
[i
].string
.charset
);
2932 attr
->values
[i
].string
.charset
= NULL
;
2941 * Use the default connection hostname instead...
2944 attr
->value_tag
= IPP_TAG_NAME
;
2945 attr
->num_values
= 1;
2946 attr
->values
[0].string
.text
= strdup(con
->http
.hostname
);
2952 * No job-originating-host-name attribute, so use the hostname from
2956 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
,
2957 "job-originating-host-name", NULL
, con
->http
.hostname
);
2960 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
2962 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
2963 "time-at-processing", 0);
2964 attr
->value_tag
= IPP_TAG_NOVALUE
;
2965 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
2966 "time-at-completed", 0);
2967 attr
->value_tag
= IPP_TAG_NOVALUE
;
2970 * Add remaining job attributes...
2973 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
2974 job
->state
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
2975 "job-state", IPP_JOB_STOPPED
);
2976 job
->sheets
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
2977 "job-media-sheets-completed", 0);
2978 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
2980 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
2983 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
2984 attr
->values
[0].integer
= 0;
2986 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
2989 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
2990 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
2992 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
2993 "job-hold-until", NULL
, "no-hold");
2994 if (attr
!= NULL
&& strcmp(attr
->values
[0].string
.text
, "no-hold") != 0 &&
2995 !(printer
->type
& CUPS_PRINTER_REMOTE
))
2998 * Hold job until specified time...
3001 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
3004 job
->hold_until
= time(NULL
) + 60;
3006 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
3008 if (!(printer
->type
& CUPS_PRINTER_REMOTE
) || Classification
)
3011 * Add job sheets options...
3014 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) == NULL
)
3016 LogMessage(L_DEBUG
, "Adding default job-sheets values \"%s,%s\"...",
3017 printer
->job_sheets
[0], printer
->job_sheets
[1]);
3019 attr
= ippAddStrings(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-sheets",
3021 attr
->values
[0].string
.text
= strdup(printer
->job_sheets
[0]);
3022 attr
->values
[1].string
.text
= strdup(printer
->job_sheets
[1]);
3025 job
->job_sheets
= attr
;
3028 * Enforce classification level if set...
3033 if (ClassifyOverride
)
3035 if (strcmp(attr
->values
[0].string
.text
, "none") == 0 &&
3036 (attr
->num_values
== 1 ||
3037 strcmp(attr
->values
[1].string
.text
, "none") == 0))
3040 * Force the leading banner to have the classification on it...
3043 SetString(&attr
->values
[0].string
.text
, Classification
);
3045 else if (attr
->num_values
== 2 &&
3046 strcmp(attr
->values
[0].string
.text
, attr
->values
[1].string
.text
) != 0 &&
3047 strcmp(attr
->values
[0].string
.text
, "none") != 0 &&
3048 strcmp(attr
->values
[1].string
.text
, "none") != 0)
3051 * Can't put two different security markings on the same document!
3054 SetString(&attr
->values
[1].string
.text
, attr
->values
[0].string
.text
);
3057 else if (strcmp(attr
->values
[0].string
.text
, Classification
) != 0 &&
3058 (attr
->num_values
== 1 ||
3059 strcmp(attr
->values
[1].string
.text
, Classification
) != 0))
3062 * Force the leading banner to have the classification on it...
3065 SetString(&attr
->values
[0].string
.text
, Classification
);
3070 * See if we need to add the starting sheet...
3073 if (!(printer
->type
& CUPS_PRINTER_REMOTE
))
3075 kbytes
= copy_banner(con
, job
, attr
->values
[0].string
.text
);
3077 UpdateQuota(printer
, job
->username
, 0, kbytes
);
3080 else if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
3084 * Save and log the job...
3089 LogMessage(L_INFO
, "Job %d created on \'%s\' by \'%s\'.", job
->id
,
3090 job
->dest
, job
->username
);
3093 * Fill in the response info...
3097 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
3098 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
3099 ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), job
->id
);
3101 #endif /* AF_INET6 */
3102 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
3103 ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), job
->id
);
3105 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, job_uri
);
3107 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
3109 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
3110 job
->state
->values
[0].integer
);
3112 con
->response
->request
.status
.status_code
= IPP_OK
;
3117 * 'delete_printer()' - Remove a printer or class from the system.
3121 delete_printer(client_t
*con
, /* I - Client connection */
3122 ipp_attribute_t
*uri
) /* I - URI of printer or class */
3124 const char *dest
; /* Destination */
3125 cups_ptype_t dtype
; /* Destination type (printer or class) */
3126 char method
[HTTP_MAX_URI
],
3127 /* Method portion of URI */
3128 username
[HTTP_MAX_URI
],
3129 /* Username portion of URI */
3131 /* Host portion of URI */
3132 resource
[HTTP_MAX_URI
];
3133 /* Resource portion of URI */
3134 int port
; /* Port portion of URI */
3135 printer_t
*printer
; /* Printer/class */
3136 char filename
[1024]; /* Script/PPD filename */
3139 LogMessage(L_DEBUG2
, "delete_printer(%d, %s)\n", con
->http
.fd
,
3140 uri
->values
[0].string
.text
);
3143 * Was this operation called from the correct URI?
3146 if (strncmp(con
->uri
, "/admin/", 7) != 0)
3148 LogMessage(L_ERROR
, "delete_printer: admin request on bad resource \'%s\'!",
3150 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
3154 DEBUG_printf(("delete_printer(%08x, %08x)\n", con
, uri
));
3157 * Do we have a valid URI?
3160 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3162 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
3168 LogMessage(L_ERROR
, "delete_printer: resource name \'%s\' no good!", resource
);
3169 send_ipp_error(con
, IPP_NOT_FOUND
);
3174 * Find the printer or class and delete it...
3177 if (dtype
& CUPS_PRINTER_CLASS
)
3178 printer
= FindClass(dest
);
3180 printer
= FindPrinter(dest
);
3183 * Remove old jobs...
3186 CancelJobs(dest
, NULL
, 1);
3189 * Remove any old PPD or script files...
3192 snprintf(filename
, sizeof(filename
), "%s/interfaces/%s", ServerRoot
, dest
);
3195 snprintf(filename
, sizeof(filename
), "%s/ppd/%s.ppd", ServerRoot
, dest
);
3198 if (dtype
& CUPS_PRINTER_CLASS
)
3200 LogMessage(L_INFO
, "Class \'%s\' deleted by \'%s\'.", dest
,
3203 DeletePrinter(printer
);
3208 LogMessage(L_INFO
, "Printer \'%s\' deleted by \'%s\'.", dest
,
3211 DeletePrinter(printer
);
3216 * Return with no errors...
3219 con
->response
->request
.status
.status_code
= IPP_OK
;
3224 * 'get_default()' - Get the default destination.
3228 get_default(client_t
*con
) /* I - Client connection */
3230 LogMessage(L_DEBUG2
, "get_default(%d)\n", con
->http
.fd
);
3232 if (DefaultPrinter
!= NULL
)
3234 copy_attrs(con
->response
, DefaultPrinter
->attrs
,
3235 ippFindAttribute(con
->request
, "requested-attributes",
3236 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
, 0);
3238 con
->response
->request
.status
.status_code
= IPP_OK
;
3241 con
->response
->request
.status
.status_code
= IPP_NOT_FOUND
;
3246 * 'get_devices()' - Get the list of available devices on the local system.
3250 get_devices(client_t
*con
) /* I - Client connection */
3252 LogMessage(L_DEBUG2
, "get_devices(%d)\n", con
->http
.fd
);
3255 * Copy the device attributes to the response using the requested-attributes
3256 * attribute that may be provided by the client.
3259 copy_attrs(con
->response
, Devices
,
3260 ippFindAttribute(con
->request
, "requested-attributes",
3261 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
, IPP_TAG_COPY
);
3263 con
->response
->request
.status
.status_code
= IPP_OK
;
3268 * 'get_jobs()' - Get a list of jobs for the specified printer.
3272 get_jobs(client_t
*con
, /* I - Client connection */
3273 ipp_attribute_t
*uri
) /* I - Printer URI */
3275 ipp_attribute_t
*attr
, /* Current attribute */
3276 *requested
; /* Requested attributes */
3277 const char *dest
; /* Destination */
3278 cups_ptype_t dtype
; /* Destination type (printer or class) */
3279 cups_ptype_t dmask
; /* Destination type mask */
3280 char method
[HTTP_MAX_URI
],
3281 /* Method portion of URI */
3282 username
[HTTP_MAX_URI
],
3283 /* Username portion of URI */
3285 /* Host portion of URI */
3286 resource
[HTTP_MAX_URI
];
3287 /* Resource portion of URI */
3288 int port
; /* Port portion of URI */
3289 int completed
; /* Completed jobs? */
3290 int limit
; /* Maximum number of jobs to return */
3291 int count
; /* Number of jobs that match */
3292 job_t
*job
; /* Current job pointer */
3293 char job_uri
[HTTP_MAX_URI
];
3297 LogMessage(L_DEBUG2
, "get_jobs(%d, %s)\n", con
->http
.fd
,
3298 uri
->values
[0].string
.text
);
3301 * Is the destination valid?
3304 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3306 if (strcmp(resource
, "/") == 0 ||
3307 (strncmp(resource
, "/jobs", 5) == 0 && strlen(resource
) <= 6))
3310 dtype
= (cups_ptype_t
)0;
3311 dmask
= (cups_ptype_t
)0;
3313 else if (strncmp(resource
, "/printers", 9) == 0 && strlen(resource
) <= 10)
3316 dtype
= (cups_ptype_t
)0;
3317 dmask
= CUPS_PRINTER_CLASS
;
3319 else if (strncmp(resource
, "/classes", 8) == 0 && strlen(resource
) <= 9)
3322 dtype
= CUPS_PRINTER_CLASS
;
3323 dmask
= CUPS_PRINTER_CLASS
;
3325 else if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
3331 LogMessage(L_ERROR
, "get_jobs: resource name \'%s\' no good!", resource
);
3332 send_ipp_error(con
, IPP_NOT_FOUND
);
3336 dmask
= CUPS_PRINTER_CLASS
;
3339 * See if the "which-jobs" attribute have been specified...
3342 if ((attr
= ippFindAttribute(con
->request
, "which-jobs", IPP_TAG_KEYWORD
)) != NULL
&&
3343 strcmp(attr
->values
[0].string
.text
, "completed") == 0)
3349 * See if they want to limit the number of jobs reported...
3352 if ((attr
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
)) != NULL
)
3353 limit
= attr
->values
[0].integer
;
3358 * See if we only want to see jobs for a specific user...
3361 if ((attr
= ippFindAttribute(con
->request
, "my-jobs", IPP_TAG_BOOLEAN
)) != NULL
&&
3362 attr
->values
[0].boolean
)
3364 if (con
->username
[0])
3365 strlcpy(username
, con
->username
, sizeof(username
));
3366 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
3367 strlcpy(username
, attr
->values
[0].string
.text
, sizeof(username
));
3369 strcpy(username
, "anonymous");
3374 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3378 * OK, build a list of jobs for this printer...
3381 for (count
= 0, job
= Jobs
; count
< limit
&& job
!= NULL
; job
= job
->next
)
3384 * Filter out jobs that don't match...
3387 DEBUG_printf(("get_jobs: job->id = %d\n", job
->id
));
3389 if ((dest
!= NULL
&& strcmp(job
->dest
, dest
) != 0) &&
3390 (job
->printer
== NULL
|| dest
== NULL
||
3391 strcmp(job
->printer
->name
, dest
) != 0))
3393 if ((job
->dtype
& dmask
) != dtype
&&
3394 (job
->printer
== NULL
|| (job
->printer
->type
& dmask
) != dtype
))
3396 if (username
[0] != '\0' && strcmp(username
, job
->username
) != 0)
3399 if (completed
&& job
->state
->values
[0].integer
<= IPP_JOB_STOPPED
)
3401 if (!completed
&& job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
3406 DEBUG_printf(("get_jobs: count = %d\n", count
));
3409 * Send the requested attributes for each job...
3413 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
3414 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
3415 ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), job
->id
);
3417 #endif /* AF_INET6 */
3418 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
3419 ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), job
->id
);
3421 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
3422 "job-more-info", NULL
, job_uri
);
3424 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
3425 "job-uri", NULL
, job_uri
);
3427 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3428 "job-printer-up-time", time(NULL
));
3431 * Copy the job attributes to the response using the requested-attributes
3432 * attribute that may be provided by the client.
3435 copy_attrs(con
->response
, job
->attrs
, requested
, IPP_TAG_JOB
, 0);
3437 add_job_state_reasons(con
, job
);
3439 ippAddSeparator(con
->response
);
3442 if (requested
!= NULL
)
3443 con
->response
->request
.status
.status_code
= IPP_OK_SUBST
;
3445 con
->response
->request
.status
.status_code
= IPP_OK
;
3450 * 'get_job_attrs()' - Get job attributes.
3454 get_job_attrs(client_t
*con
, /* I - Client connection */
3455 ipp_attribute_t
*uri
) /* I - Job URI */
3457 ipp_attribute_t
*attr
, /* Current attribute */
3458 *requested
; /* Requested attributes */
3459 int jobid
; /* Job ID */
3460 job_t
*job
; /* Current job */
3461 char method
[HTTP_MAX_URI
],
3462 /* Method portion of URI */
3463 username
[HTTP_MAX_URI
],
3464 /* Username portion of URI */
3466 /* Host portion of URI */
3467 resource
[HTTP_MAX_URI
];
3468 /* Resource portion of URI */
3469 int port
; /* Port portion of URI */
3470 char job_uri
[HTTP_MAX_URI
];
3474 LogMessage(L_DEBUG2
, "get_job_attrs(%d, %s)\n", con
->http
.fd
,
3475 uri
->values
[0].string
.text
);
3478 * See if we have a job URI or a printer URI...
3481 if (strcmp(uri
->name
, "printer-uri") == 0)
3484 * Got a printer URI; see if we also have a job-id attribute...
3487 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
3489 LogMessage(L_ERROR
, "get_job_attrs: got a printer-uri attribute but no job-id!");
3490 send_ipp_error(con
, IPP_BAD_REQUEST
);
3494 jobid
= attr
->values
[0].integer
;
3499 * Got a job URI; parse it to get the job ID...
3502 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3504 if (strncmp(resource
, "/jobs/", 6) != 0)
3510 LogMessage(L_ERROR
, "get_job_attrs: bad job-uri attribute \'%s\'!\n",
3511 uri
->values
[0].string
.text
);
3512 send_ipp_error(con
, IPP_BAD_REQUEST
);
3516 jobid
= atoi(resource
+ 6);
3520 * See if the job exists...
3523 if ((job
= FindJob(jobid
)) == NULL
)
3526 * Nope - return a "not found" error...
3529 LogMessage(L_ERROR
, "get_job_attrs: job #%d doesn't exist!", jobid
);
3530 send_ipp_error(con
, IPP_NOT_FOUND
);
3535 * Put out the standard attributes...
3539 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
3540 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d",
3541 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
),
3544 #endif /* AF_INET6 */
3545 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d",
3546 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
),
3549 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
3551 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
3552 "job-more-info", NULL
, job_uri
);
3554 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
3555 "job-uri", NULL
, job_uri
);
3557 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3558 "job-printer-up-time", time(NULL
));
3561 * Copy the job attributes to the response using the requested-attributes
3562 * attribute that may be provided by the client.
3565 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3568 copy_attrs(con
->response
, job
->attrs
, requested
, IPP_TAG_JOB
, 0);
3570 add_job_state_reasons(con
, job
);
3572 if (requested
!= NULL
)
3573 con
->response
->request
.status
.status_code
= IPP_OK_SUBST
;
3575 con
->response
->request
.status
.status_code
= IPP_OK
;
3580 * 'get_ppds()' - Get the list of PPD files on the local system.
3584 get_ppds(client_t
*con
) /* I - Client connection */
3586 LogMessage(L_DEBUG2
, "get_ppds(%d)\n", con
->http
.fd
);
3589 * Copy the PPD attributes to the response using the requested-attributes
3590 * attribute that may be provided by the client.
3593 copy_attrs(con
->response
, PPDs
,
3594 ippFindAttribute(con
->request
, "requested-attributes",
3595 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
, IPP_TAG_COPY
);
3597 con
->response
->request
.status
.status_code
= IPP_OK
;
3602 * 'get_printer_attrs()' - Get printer attributes.
3606 get_printer_attrs(client_t
*con
, /* I - Client connection */
3607 ipp_attribute_t
*uri
) /* I - Printer URI */
3609 const char *dest
; /* Destination */
3610 cups_ptype_t dtype
; /* Destination type (printer or class) */
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 printer_t
*printer
; /* Printer/class */
3621 time_t curtime
; /* Current time */
3622 int i
; /* Looping var */
3623 ipp_attribute_t
*requested
, /* requested-attributes */
3624 *history
; /* History collection */
3625 int need_history
; /* Need to send history collection? */
3628 LogMessage(L_DEBUG2
, "get_printer_attrs(%d, %s)\n", con
->http
.fd
,
3629 uri
->values
[0].string
.text
);
3632 * Is the destination valid?
3635 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3637 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
3643 LogMessage(L_ERROR
, "get_printer_attrs: resource name \'%s\' no good!", resource
);
3644 send_ipp_error(con
, IPP_NOT_FOUND
);
3648 if (dtype
& CUPS_PRINTER_CLASS
)
3649 printer
= FindClass(dest
);
3651 printer
= FindPrinter(dest
);
3653 curtime
= time(NULL
);
3656 * Copy the printer attributes to the response using requested-attributes
3657 * and document-format attributes that may be provided by the client.
3660 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
3663 add_printer_state_reasons(con
, printer
);
3665 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
3666 "printer-state-message", NULL
, printer
->state_message
);
3668 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
3669 printer
->accepting
);
3671 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3672 "printer-up-time", curtime
);
3673 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3674 "printer-state-time", printer
->state_time
);
3675 ippAddDate(con
->response
, IPP_TAG_PRINTER
, "printer-current-time",
3676 ippTimeToDate(curtime
));
3678 add_queued_job_count(con
, printer
);
3680 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3683 copy_attrs(con
->response
, printer
->attrs
, requested
, IPP_TAG_ZERO
, 0);
3684 copy_attrs(con
->response
, CommonData
, requested
, IPP_TAG_ZERO
, IPP_TAG_COPY
);
3688 if (MaxPrinterHistory
> 0 && printer
->num_history
> 0 && requested
)
3690 for (i
= 0; i
< requested
->num_values
; i
++)
3691 if (!strcmp(requested
->values
[i
].string
.text
, "all") ||
3692 !strcmp(requested
->values
[i
].string
.text
, "printer-state-history"))
3701 history
= ippAddCollections(con
->response
, IPP_TAG_PRINTER
,
3702 "printer-state-history",
3703 printer
->num_history
, NULL
);
3705 for (i
= 0; i
< printer
->num_history
; i
++)
3706 copy_attrs(history
->values
[i
].collection
= ippNew(), printer
->history
[i
],
3707 NULL
, IPP_TAG_ZERO
, 0);
3710 con
->response
->request
.status
.status_code
= requested
? IPP_OK_SUBST
: IPP_OK
;
3715 * 'get_printers()' - Get a list of printers or classes.
3719 get_printers(client_t
*con
, /* I - Client connection */
3720 int type
) /* I - 0 or CUPS_PRINTER_CLASS */
3722 int i
; /* Looping var */
3723 ipp_attribute_t
*requested
, /* requested-attributes */
3724 *history
, /* History collection */
3725 *attr
; /* Current attribute */
3726 int need_history
; /* Need to send history collection? */
3727 int limit
; /* Maximum number of printers to return */
3728 int count
; /* Number of printers that match */
3729 printer_t
*printer
; /* Current printer pointer */
3730 time_t curtime
; /* Current time */
3731 int printer_type
, /* printer-type attribute */
3732 printer_mask
; /* printer-type-mask attribute */
3733 char *location
; /* Location string */
3734 char name
[IPP_MAX_NAME
],
3736 *nameptr
; /* Pointer into name */
3737 printer_t
*iclass
; /* Implicit class */
3740 LogMessage(L_DEBUG2
, "get_printers(%d, %x)\n", con
->http
.fd
, type
);
3743 * See if they want to limit the number of printers reported...
3746 if ((attr
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
)) != NULL
)
3747 limit
= attr
->values
[0].integer
;
3752 * Support filtering...
3755 if ((attr
= ippFindAttribute(con
->request
, "printer-type", IPP_TAG_ENUM
)) != NULL
)
3756 printer_type
= attr
->values
[0].integer
;
3760 if ((attr
= ippFindAttribute(con
->request
, "printer-type-mask", IPP_TAG_ENUM
)) != NULL
)
3761 printer_mask
= attr
->values
[0].integer
;
3765 if ((attr
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
3766 location
= attr
->values
[0].string
.text
;
3770 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3775 if (MaxPrinterHistory
> 0 && requested
)
3777 for (i
= 0; i
< requested
->num_values
; i
++)
3778 if (!strcmp(requested
->values
[i
].string
.text
, "all") ||
3779 !strcmp(requested
->values
[i
].string
.text
, "printer-state-history"))
3787 * OK, build a list of printers for this printer...
3790 curtime
= time(NULL
);
3792 for (count
= 0, printer
= Printers
;
3793 count
< limit
&& printer
!= NULL
;
3794 printer
= printer
->next
)
3795 if ((printer
->type
& CUPS_PRINTER_CLASS
) == type
&&
3796 (printer
->type
& printer_mask
) == printer_type
&&
3797 (location
== NULL
|| printer
->location
== NULL
||
3798 strcasecmp(printer
->location
, location
) == 0))
3801 * If HideImplicitMembers is enabled, see if this printer or class
3802 * is a member of an implicit class...
3805 if (ImplicitClasses
&& HideImplicitMembers
&&
3806 (printer
->type
& CUPS_PRINTER_REMOTE
))
3809 * Make a copy of the printer name...
3812 strlcpy(name
, printer
->name
, sizeof(name
));
3814 if ((nameptr
= strchr(name
, '@')) != NULL
)
3817 * Strip trailing @server...
3823 * Find the core printer, if any...
3826 if ((iclass
= FindPrinter(name
)) != NULL
&&
3827 (iclass
->type
& CUPS_PRINTER_IMPLICIT
))
3833 * Add the group separator as needed...
3837 ippAddSeparator(con
->response
);
3842 * Send the following attributes for each printer:
3845 * printer-state-message
3846 * printer-is-accepting-jobs
3847 * + all printer attributes
3850 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3851 "printer-state", printer
->state
);
3853 add_printer_state_reasons(con
, printer
);
3855 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
3856 "printer-state-message", NULL
, printer
->state_message
);
3858 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
3859 printer
->accepting
);
3861 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3862 "printer-up-time", curtime
);
3863 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3864 "printer-state-time", printer
->state_time
);
3865 ippAddDate(con
->response
, IPP_TAG_PRINTER
, "printer-current-time",
3866 ippTimeToDate(curtime
));
3868 add_queued_job_count(con
, printer
);
3870 copy_attrs(con
->response
, printer
->attrs
, requested
, IPP_TAG_ZERO
, 0);
3872 copy_attrs(con
->response
, CommonData
, requested
, IPP_TAG_ZERO
,
3875 if (need_history
&& printer
->num_history
> 0)
3877 history
= ippAddCollections(con
->response
, IPP_TAG_PRINTER
,
3878 "printer-state-history",
3879 printer
->num_history
, NULL
);
3881 for (i
= 0; i
< printer
->num_history
; i
++)
3882 copy_attrs(history
->values
[i
].collection
= ippNew(),
3883 printer
->history
[i
], NULL
, IPP_TAG_ZERO
, 0);
3887 con
->response
->request
.status
.status_code
= requested
? IPP_OK_SUBST
: IPP_OK
;
3892 * 'hold_job()' - Hold a print job.
3896 hold_job(client_t
*con
, /* I - Client connection */
3897 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
3899 ipp_attribute_t
*attr
, /* Current job-hold-until */
3900 *newattr
; /* New job-hold-until */
3901 int jobid
; /* Job ID */
3902 char method
[HTTP_MAX_URI
],
3903 /* Method portion of URI */
3904 username
[HTTP_MAX_URI
],
3905 /* Username portion of URI */
3907 /* Host portion of URI */
3908 resource
[HTTP_MAX_URI
];
3909 /* Resource portion of URI */
3910 int port
; /* Port portion of URI */
3911 job_t
*job
; /* Job information */
3914 LogMessage(L_DEBUG2
, "hold_job(%d, %s)\n", con
->http
.fd
,
3915 uri
->values
[0].string
.text
);
3918 * Verify that the POST operation was done to a valid URI.
3921 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
3922 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
3923 strncmp(con
->uri
, "/printers/", 10) != 0)
3925 LogMessage(L_ERROR
, "hold_job: hold request on bad resource \'%s\'!",
3927 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
3932 * See if we have a job URI or a printer URI...
3935 if (strcmp(uri
->name
, "printer-uri") == 0)
3938 * Got a printer URI; see if we also have a job-id attribute...
3941 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
3943 LogMessage(L_ERROR
, "hold_job: got a printer-uri attribute but no job-id!");
3944 send_ipp_error(con
, IPP_BAD_REQUEST
);
3948 jobid
= attr
->values
[0].integer
;
3953 * Got a job URI; parse it to get the job ID...
3956 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3958 if (strncmp(resource
, "/jobs/", 6) != 0)
3964 LogMessage(L_ERROR
, "hold_job: bad job-uri attribute \'%s\'!",
3965 uri
->values
[0].string
.text
);
3966 send_ipp_error(con
, IPP_BAD_REQUEST
);
3970 jobid
= atoi(resource
+ 6);
3974 * See if the job exists...
3977 if ((job
= FindJob(jobid
)) == NULL
)
3980 * Nope - return a "not found" error...
3983 LogMessage(L_ERROR
, "hold_job: job #%d doesn't exist!", jobid
);
3984 send_ipp_error(con
, IPP_NOT_FOUND
);
3989 * See if the job is owned by the requesting user...
3992 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
3994 LogMessage(L_ERROR
, "hold_job: \"%s\" not authorized to hold job id %d owned by \"%s\"!",
3995 username
, jobid
, job
->username
);
3996 send_ipp_error(con
, IPP_FORBIDDEN
);
4001 * Hold the job and return...
4006 if ((newattr
= ippFindAttribute(con
->request
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
4007 newattr
= ippFindAttribute(con
->request
, "job-hold-until", IPP_TAG_NAME
);
4009 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
4010 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
4015 * Free the old hold value and copy the new one over...
4018 free(attr
->values
[0].string
.text
);
4020 if (newattr
!= NULL
)
4022 attr
->value_tag
= newattr
->value_tag
;
4023 attr
->values
[0].string
.text
= strdup(newattr
->values
[0].string
.text
);
4027 attr
->value_tag
= IPP_TAG_KEYWORD
;
4028 attr
->values
[0].string
.text
= strdup("indefinite");
4032 * Hold job until specified time...
4035 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
4038 LogMessage(L_INFO
, "Job %d was held by \'%s\'.", jobid
, username
);
4040 con
->response
->request
.status
.status_code
= IPP_OK
;
4045 * 'move_job()' - Move a job to a new destination.
4049 move_job(client_t
*con
, /* I - Client connection */
4050 ipp_attribute_t
*uri
) /* I - Job URI */
4052 ipp_attribute_t
*attr
; /* Current attribute */
4053 int jobid
; /* Job ID */
4054 job_t
*job
; /* Current job */
4055 const char *dest
; /* Destination */
4056 cups_ptype_t dtype
; /* Destination type (printer or class) */
4057 char method
[HTTP_MAX_URI
],
4058 /* Method portion of URI */
4059 username
[HTTP_MAX_URI
],
4060 /* Username portion of URI */
4062 /* Host portion of URI */
4063 resource
[HTTP_MAX_URI
];
4064 /* Resource portion of URI */
4065 int port
; /* Port portion of URI */
4068 LogMessage(L_DEBUG2
, "move_job(%d, %s)\n", con
->http
.fd
,
4069 uri
->values
[0].string
.text
);
4072 * See if we have a job URI or a printer URI...
4075 if (strcmp(uri
->name
, "printer-uri") == 0)
4078 * Got a printer URI; see if we also have a job-id attribute...
4081 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
4083 LogMessage(L_ERROR
, "move_job: got a printer-uri attribute but no job-id!");
4084 send_ipp_error(con
, IPP_BAD_REQUEST
);
4088 jobid
= attr
->values
[0].integer
;
4093 * Got a job URI; parse it to get the job ID...
4096 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
4098 if (strncmp(resource
, "/jobs/", 6) != 0)
4104 LogMessage(L_ERROR
, "move_job: bad job-uri attribute \'%s\'!\n",
4105 uri
->values
[0].string
.text
);
4106 send_ipp_error(con
, IPP_BAD_REQUEST
);
4110 jobid
= atoi(resource
+ 6);
4114 * See if the job exists...
4117 if ((job
= FindJob(jobid
)) == NULL
)
4120 * Nope - return a "not found" error...
4123 LogMessage(L_ERROR
, "move_job: job #%d doesn't exist!", jobid
);
4124 send_ipp_error(con
, IPP_NOT_FOUND
);
4129 * See if the job has been completed...
4132 if (job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
4135 * Return a "not-possible" error...
4138 LogMessage(L_ERROR
, "move_job: job #%d is finished and cannot be altered!", jobid
);
4139 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
4144 * See if the job is owned by the requesting user...
4147 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
4149 LogMessage(L_ERROR
, "move_job: \"%s\" not authorized to move job id %d owned by \"%s\"!",
4150 username
, jobid
, job
->username
);
4151 send_ipp_error(con
, IPP_FORBIDDEN
);
4155 if ((attr
= ippFindAttribute(con
->request
, "job-printer-uri", IPP_TAG_URI
)) == NULL
)
4158 * Need job-printer-uri...
4161 LogMessage(L_ERROR
, "move_job: job-printer-uri attribute missing!");
4162 send_ipp_error(con
, IPP_BAD_REQUEST
);
4167 * Move the job to a different printer or class...
4170 httpSeparate(attr
->values
[0].string
.text
, method
, username
, host
, &port
,
4172 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
4178 LogMessage(L_ERROR
, "move_job: resource name \'%s\' no good!", resource
);
4179 send_ipp_error(con
, IPP_NOT_FOUND
);
4183 MoveJob(jobid
, dest
);
4186 * Start jobs if possible...
4192 * Return with "everything is OK" status...
4195 con
->response
->request
.status
.status_code
= IPP_OK
;
4200 * 'ppd_add_default()' - Add a PPD default choice.
4203 static int /* O - Number of defaults */
4204 ppd_add_default(const char *option
, /* I - Option name */
4205 const char *choice
, /* I - Choice name */
4207 /* I - Number of defaults */
4208 ppd_default_t
**defaults
)
4211 int i
; /* Looping var */
4212 ppd_default_t
*temp
; /* Temporary defaults array */
4216 * First check if the option already has a default value; the PPD spec
4217 * says that the first one is used...
4220 for (i
= 0, temp
= *defaults
; i
< num_defaults
; i
++)
4221 if (!strcmp(option
, temp
[i
].option
))
4222 return (num_defaults
);
4225 * Now add the option...
4228 if (num_defaults
== 0)
4229 temp
= malloc(sizeof(ppd_default_t
));
4231 temp
= realloc(*defaults
, (num_defaults
+ 1) * sizeof(ppd_default_t
));
4235 LogMessage(L_ERROR
, "ppd_add_default: Unable to add default value for \"%s\" - %s",
4236 option
, strerror(errno
));
4237 return (num_defaults
);
4241 temp
+= num_defaults
;
4243 strlcpy(temp
->option
, option
, sizeof(temp
->option
));
4244 strlcpy(temp
->choice
, choice
, sizeof(temp
->choice
));
4246 return (num_defaults
+ 1);
4251 * 'ppd_parse_line()' - Parse a PPD default line.
4254 static int /* O - 0 on success, -1 on failure */
4255 ppd_parse_line(const char *line
, /* I - Line */
4256 char *option
, /* O - Option name */
4257 int olen
, /* I - Size of option name */
4258 char *choice
, /* O - Choice name */
4259 int clen
) /* I - Size of choice name */
4262 * Verify this is a default option line...
4265 if (strncmp(line
, "*Default", 8))
4269 * Read the option name...
4272 for (line
+= 8, olen
--; isalnum(*line
); line
++)
4282 * Skip everything else up to the colon (:)...
4285 while (*line
&& *line
!= ':')
4294 * Now grab the option choice, skipping leading whitespace...
4297 while (isspace(*line
))
4300 for (clen
--; isalnum(*line
); line
++)
4310 * Return with no errors...
4318 * 'print_job()' - Print a file to a printer or class.
4322 print_job(client_t
*con
, /* I - Client connection */
4323 ipp_attribute_t
*uri
) /* I - Printer URI */
4325 ipp_attribute_t
*attr
; /* Current attribute */
4326 ipp_attribute_t
*format
; /* Document-format attribute */
4327 const char *dest
; /* Destination */
4328 cups_ptype_t dtype
; /* Destination type (printer or class) */
4329 int priority
; /* Job priority */
4330 char *title
; /* Job name/title */
4331 job_t
*job
; /* Current job */
4332 char job_uri
[HTTP_MAX_URI
],
4334 printer_uri
[HTTP_MAX_URI
],
4336 method
[HTTP_MAX_URI
],
4337 /* Method portion of URI */
4338 username
[HTTP_MAX_URI
],
4339 /* Username portion of URI */
4341 /* Host portion of URI */
4342 resource
[HTTP_MAX_URI
],
4343 /* Resource portion of URI */
4344 filename
[1024]; /* Job filename */
4345 int port
; /* Port portion of URI */
4346 mime_type_t
*filetype
; /* Type of file */
4347 char super
[MIME_MAX_SUPER
],
4348 /* Supertype of file */
4349 type
[MIME_MAX_TYPE
],
4350 /* Subtype of file */
4351 mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
4352 /* Textual name of mime type */
4353 printer_t
*printer
; /* Printer data */
4354 struct stat fileinfo
; /* File information */
4355 int kbytes
; /* Size of file */
4356 int i
; /* Looping var */
4357 int lowerpagerange
; /* Page range bound */
4358 int compression
; /* Document compression */
4361 LogMessage(L_DEBUG2
, "print_job(%d, %s)\n", con
->http
.fd
,
4362 uri
->values
[0].string
.text
);
4365 * Verify that the POST operation was done to a valid URI.
4368 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
4369 strncmp(con
->uri
, "/printers/", 10) != 0)
4371 LogMessage(L_ERROR
, "print_job: cancel request on bad resource \'%s\'!",
4373 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
4378 * Validate job template attributes; for now just copies and page-ranges...
4381 if ((attr
= ippFindAttribute(con
->request
, "copies", IPP_TAG_INTEGER
)) != NULL
)
4383 if (attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> MaxCopies
)
4385 LogMessage(L_INFO
, "print_job: bad copies value %d.",
4386 attr
->values
[0].integer
);
4387 send_ipp_error(con
, IPP_BAD_REQUEST
);
4392 if ((attr
= ippFindAttribute(con
->request
, "page-ranges", IPP_TAG_RANGE
)) != NULL
)
4394 for (i
= 0, lowerpagerange
= 1; i
< attr
->num_values
; i
++)
4396 if (attr
->values
[i
].range
.lower
< lowerpagerange
||
4397 attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4399 LogMessage(L_ERROR
, "print_job: bad page-ranges values %d-%d.",
4400 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
4401 send_ipp_error(con
, IPP_BAD_REQUEST
);
4405 lowerpagerange
= attr
->values
[i
].range
.upper
+ 1;
4410 * OK, see if the client is sending the document compressed - CUPS
4411 * only supports "none" and "gzip".
4414 compression
= CUPS_FILE_NONE
;
4416 if ((attr
= ippFindAttribute(con
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
)
4418 if (strcmp(attr
->values
[0].string
.text
, "none")
4420 && strcmp(attr
->values
[0].string
.text
, "gzip")
4421 #endif /* HAVE_LIBZ */
4424 LogMessage(L_ERROR
, "print_job: Unsupported compression \"%s\"!",
4425 attr
->values
[0].string
.text
);
4426 send_ipp_error(con
, IPP_ATTRIBUTES
);
4427 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
4428 "compression", NULL
, attr
->values
[0].string
.text
);
4433 if (!strcmp(attr
->values
[0].string
.text
, "gzip"))
4434 compression
= CUPS_FILE_GZIP
;
4435 #endif /* HAVE_LIBZ */
4439 * Do we have a file to print?
4444 LogMessage(L_ERROR
, "print_job: No file!?!");
4445 send_ipp_error(con
, IPP_BAD_REQUEST
);
4450 * Is it a format we support?
4453 if ((format
= ippFindAttribute(con
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
4456 * Grab format from client...
4459 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
, type
) != 2)
4461 LogMessage(L_ERROR
, "print_job: could not scan type \'%s\'!",
4462 format
->values
[0].string
.text
);
4463 send_ipp_error(con
, IPP_BAD_REQUEST
);
4470 * No document format attribute? Auto-type it!
4473 strcpy(super
, "application");
4474 strcpy(type
, "octet-stream");
4477 if (strcmp(super
, "application") == 0 &&
4478 strcmp(type
, "octet-stream") == 0)
4481 * Auto-type the file...
4484 LogMessage(L_DEBUG
, "print_job: auto-typing file...");
4486 filetype
= mimeFileType(MimeDatabase
, con
->filename
, &compression
);
4488 if (filetype
!= NULL
)
4491 * Replace the document-format attribute value with the auto-typed one.
4494 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
4499 free(format
->values
[0].string
.text
);
4500 format
->values
[0].string
.text
= strdup(mimetype
);
4503 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
4504 "document-format", NULL
, mimetype
);
4507 filetype
= mimeType(MimeDatabase
, super
, type
);
4510 filetype
= mimeType(MimeDatabase
, super
, type
);
4512 if (filetype
== NULL
)
4514 LogMessage(L_ERROR
, "print_job: Unsupported format \'%s/%s\'!",
4516 LogMessage(L_INFO
, "Hint: Do you have the raw file printing rules enabled?");
4517 send_ipp_error(con
, IPP_DOCUMENT_FORMAT
);
4520 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
4521 "document-format", NULL
, format
->values
[0].string
.text
);
4526 LogMessage(L_DEBUG
, "print_job: request file type is %s/%s.",
4527 filetype
->super
, filetype
->type
);
4530 * Read any embedded job ticket info from PS files...
4533 if (strcasecmp(filetype
->super
, "application") == 0 &&
4534 strcasecmp(filetype
->type
, "postscript") == 0)
4535 read_ps_job_ticket(con
);
4538 * Is the destination valid?
4541 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
4543 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
4549 LogMessage(L_ERROR
, "print_job: resource name \'%s\' no good!", resource
);
4550 send_ipp_error(con
, IPP_NOT_FOUND
);
4555 * See if the printer is accepting jobs...
4558 if (dtype
& CUPS_PRINTER_CLASS
)
4560 printer
= FindClass(dest
);
4562 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
4563 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/classes/%s",
4564 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), dest
);
4566 #endif /* AF_INET6 */
4567 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/classes/%s",
4568 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), dest
);
4572 printer
= FindPrinter(dest
);
4575 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
4576 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/printers/%s",
4577 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), dest
);
4579 #endif /* AF_INET6 */
4580 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/printers/%s",
4581 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), dest
);
4584 if (!printer
->accepting
)
4586 LogMessage(L_INFO
, "print_job: destination \'%s\' is not accepting jobs.",
4588 send_ipp_error(con
, IPP_NOT_ACCEPTING
);
4593 * Make sure we aren't over our limit...
4596 if (NumJobs
>= MaxJobs
&& MaxJobs
)
4599 if (NumJobs
>= MaxJobs
&& MaxJobs
)
4601 LogMessage(L_INFO
, "print_job: too many jobs - %d jobs, max jobs is %d.",
4603 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
4607 if (!check_quotas(con
, printer
))
4609 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
4614 * Create the job and set things up...
4617 if ((attr
= ippFindAttribute(con
->request
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
4618 priority
= attr
->values
[0].integer
;
4620 ippAddInteger(con
->request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
4623 if ((attr
= ippFindAttribute(con
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
4624 title
= attr
->values
[0].string
.text
;
4626 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
4627 title
= "Untitled");
4629 if ((job
= AddJob(priority
, printer
->name
)) == NULL
)
4631 LogMessage(L_ERROR
, "print_job: unable to add job for destination \'%s\'!",
4633 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
4638 job
->attrs
= con
->request
;
4639 con
->request
= NULL
;
4642 * Copy the rest of the job info...
4645 attr
= ippFindAttribute(job
->attrs
, "requesting-user-name", IPP_TAG_NAME
);
4647 if (con
->username
[0])
4648 SetString(&job
->username
, con
->username
);
4649 else if (attr
!= NULL
)
4651 LogMessage(L_DEBUG
, "print_job: requesting-user-name = \'%s\'",
4652 attr
->values
[0].string
.text
);
4654 SetString(&job
->username
, attr
->values
[0].string
.text
);
4657 SetString(&job
->username
, "anonymous");
4660 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name",
4661 NULL
, job
->username
);
4664 attr
->group_tag
= IPP_TAG_JOB
;
4665 SetString(&attr
->name
, "job-originating-user-name");
4669 * Add remaining job attributes...
4672 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-host-name",
4673 IPP_TAG_ZERO
)) != NULL
)
4676 * Request contains a job-originating-host-name attribute; validate it...
4679 if (attr
->value_tag
!= IPP_TAG_NAME
||
4680 attr
->num_values
!= 1 ||
4681 strcmp(con
->http
.hostname
, "localhost") != 0)
4684 * Can't override the value if we aren't connected via localhost.
4685 * Also, we can only have 1 value and it must be a name value.
4688 int i
; /* Looping var */
4690 switch (attr
->value_tag
)
4692 case IPP_TAG_STRING
:
4693 case IPP_TAG_TEXTLANG
:
4694 case IPP_TAG_NAMELANG
:
4697 case IPP_TAG_KEYWORD
:
4699 case IPP_TAG_URISCHEME
:
4700 case IPP_TAG_CHARSET
:
4701 case IPP_TAG_LANGUAGE
:
4702 case IPP_TAG_MIMETYPE
:
4704 * Free old strings...
4707 for (i
= 0; i
< attr
->num_values
; i
++)
4709 free(attr
->values
[i
].string
.text
);
4710 attr
->values
[i
].string
.text
= NULL
;
4711 if (attr
->values
[i
].string
.charset
)
4713 free(attr
->values
[i
].string
.charset
);
4714 attr
->values
[i
].string
.charset
= NULL
;
4723 * Use the default connection hostname instead...
4726 attr
->value_tag
= IPP_TAG_NAME
;
4727 attr
->num_values
= 1;
4728 attr
->values
[0].string
.text
= strdup(con
->http
.hostname
);
4734 * No job-originating-host-name attribute, so use the hostname from
4738 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
,
4739 "job-originating-host-name", NULL
, con
->http
.hostname
);
4742 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
4743 job
->state
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
4744 "job-state", IPP_JOB_PENDING
);
4745 job
->sheets
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4746 "job-media-sheets-completed", 0);
4747 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
4749 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
4752 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) == NULL
)
4753 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4756 if (stat(con
->filename
, &fileinfo
))
4759 kbytes
= (fileinfo
.st_size
+ 1023) / 1024;
4761 UpdateQuota(printer
, job
->username
, 0, kbytes
);
4762 attr
->values
[0].integer
+= kbytes
;
4764 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
4766 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4767 "time-at-processing", 0);
4768 attr
->value_tag
= IPP_TAG_NOVALUE
;
4769 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4770 "time-at-completed", 0);
4771 attr
->value_tag
= IPP_TAG_NOVALUE
;
4773 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
4774 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
4776 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
4777 "job-hold-until", NULL
, "no-hold");
4779 if (attr
!= NULL
&& strcmp(attr
->values
[0].string
.text
, "no-hold") != 0 &&
4780 !(printer
->type
& CUPS_PRINTER_REMOTE
))
4783 * Hold job until specified time...
4786 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
4787 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
4790 if (!(printer
->type
& CUPS_PRINTER_REMOTE
) || Classification
)
4793 * Add job sheets options...
4796 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) == NULL
)
4798 LogMessage(L_DEBUG
, "Adding default job-sheets values \"%s,%s\"...",
4799 printer
->job_sheets
[0], printer
->job_sheets
[1]);
4801 attr
= ippAddStrings(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-sheets",
4803 attr
->values
[0].string
.text
= strdup(printer
->job_sheets
[0]);
4804 attr
->values
[1].string
.text
= strdup(printer
->job_sheets
[1]);
4807 job
->job_sheets
= attr
;
4810 * Enforce classification level if set...
4815 if (ClassifyOverride
)
4817 if (strcmp(attr
->values
[0].string
.text
, "none") == 0 &&
4818 (attr
->num_values
== 1 ||
4819 strcmp(attr
->values
[1].string
.text
, "none") == 0))
4822 * Force the leading banner to have the classification on it...
4825 SetString(&attr
->values
[0].string
.text
, Classification
);
4827 else if (attr
->num_values
== 2 &&
4828 strcmp(attr
->values
[0].string
.text
, attr
->values
[1].string
.text
) != 0 &&
4829 strcmp(attr
->values
[0].string
.text
, "none") != 0 &&
4830 strcmp(attr
->values
[1].string
.text
, "none") != 0)
4833 * Can't put two different security markings on the same document!
4836 SetString(&attr
->values
[1].string
.text
, attr
->values
[0].string
.text
);
4839 else if (strcmp(attr
->values
[0].string
.text
, Classification
) != 0 &&
4840 (attr
->num_values
== 1 ||
4841 strcmp(attr
->values
[1].string
.text
, Classification
) != 0))
4844 * Force the leading banner to have the classification on it...
4847 SetString(&attr
->values
[0].string
.text
, Classification
);
4852 * Add the starting sheet...
4855 if (!(printer
->type
& CUPS_PRINTER_REMOTE
))
4857 kbytes
= copy_banner(con
, job
, attr
->values
[0].string
.text
);
4859 UpdateQuota(printer
, job
->username
, 0, kbytes
);
4862 else if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
4866 * Add the job file...
4869 if (add_file(con
, job
, filetype
, compression
))
4872 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
4874 rename(con
->filename
, filename
);
4875 ClearString(&con
->filename
);
4878 * See if we need to add the ending sheet...
4881 if (!(printer
->type
& CUPS_PRINTER_REMOTE
) && attr
->num_values
> 1)
4887 kbytes
= copy_banner(con
, job
, attr
->values
[1].string
.text
);
4888 UpdateQuota(printer
, job
->username
, 0, kbytes
);
4892 * Log and save the job...
4895 LogMessage(L_INFO
, "Job %d queued on \'%s\' by \'%s\'.", job
->id
,
4896 job
->dest
, job
->username
);
4897 LogMessage(L_DEBUG
, "Job %d hold_until = %d", job
->id
, (int)job
->hold_until
);
4902 * Start the job if possible...
4908 * Fill in the response info...
4912 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
4913 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
4914 ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), job
->id
);
4916 #endif /* AF_INET6 */
4917 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
4918 ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), job
->id
);
4920 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, job_uri
);
4922 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
4924 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
4925 job
->state
->values
[0].integer
);
4926 add_job_state_reasons(con
, job
);
4928 con
->response
->request
.status
.status_code
= IPP_OK
;
4933 * 'read_ps_job_ticket()' - Reads a job ticket embedded in a PS file.
4935 * This function only gets called when printing a single PostScript
4936 * file using the Print-Job operation. It doesn't work for Create-Job +
4937 * Send-File, since the job attributes need to be set at job creation
4938 * time for banners to work. The embedded PS job ticket stuff is here
4939 * only to allow the Windows printer driver for CUPS to pass in JCL
4940 * options and IPP attributes which otherwise would be lost.
4942 * The format of a PS job ticket is simple:
4944 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
4946 * %cupsJobTicket: attr1=value1
4947 * %cupsJobTicket: attr2=value2
4949 * %cupsJobTicket: attrN=valueN
4951 * Job ticket lines must appear immediately after the first line that
4952 * specifies PostScript format (%!PS-Adobe-3.0), and CUPS will stop
4953 * looking for job ticket info when it finds a line that does not begin
4954 * with "%cupsJobTicket:".
4956 * The maximum length of a job ticket line, including the prefix, is
4957 * 255 characters to conform with the Adobe DSC.
4959 * Read-only attributes are rejected with a notice to the error log in
4960 * case a malicious user tries anything. Since the job ticket is read
4961 * prior to attribute validation in print_job(), job ticket attributes
4962 * will go through the same validation as IPP attributes...
4966 read_ps_job_ticket(client_t
*con
) /* I - Client connection */
4968 cups_file_t
*fp
; /* File to read from */
4969 char line
[256]; /* Line data */
4970 int num_options
; /* Number of options */
4971 cups_option_t
*options
; /* Options */
4972 ipp_t
*ticket
; /* New attributes */
4973 ipp_attribute_t
*attr
, /* Current attribute */
4974 *attr2
, /* Job attribute */
4975 *prev2
; /* Previous job attribute */
4979 * First open the print file...
4982 if ((fp
= cupsFileOpen(con
->filename
, "rb")) == NULL
)
4984 LogMessage(L_ERROR
, "read_ps_job_ticket: Unable to open PostScript print file - %s",
4990 * Skip the first line...
4993 if (cupsFileGets(fp
, line
, sizeof(line
)) == NULL
)
4995 LogMessage(L_ERROR
, "read_ps_job_ticket: Unable to read from PostScript print file - %s",
5001 if (strncmp(line
, "%!PS-Adobe-", 11) != 0)
5004 * Not a DSC-compliant file, so no job ticket info will be available...
5012 * Read job ticket info from the file...
5018 while (cupsFileGets(fp
, line
, sizeof(line
)) != NULL
)
5021 * Stop at the first non-ticket line...
5024 if (strncmp(line
, "%cupsJobTicket:", 15) != 0)
5028 * Add the options to the option array...
5031 num_options
= cupsParseOptions(line
+ 15, num_options
, &options
);
5035 * Done with the file; see if we have any options...
5040 if (num_options
== 0)
5044 * OK, convert the options to an attribute list, and apply them to
5049 cupsEncodeOptions(ticket
, num_options
, options
);
5052 * See what the user wants to change.
5055 for (attr
= ticket
->attrs
; attr
!= NULL
; attr
= attr
->next
)
5057 if (attr
->group_tag
!= IPP_TAG_JOB
|| !attr
->name
)
5060 if (strcmp(attr
->name
, "job-originating-host-name") == 0 ||
5061 strcmp(attr
->name
, "job-originating-user-name") == 0 ||
5062 strcmp(attr
->name
, "job-media-sheets-completed") == 0 ||
5063 strcmp(attr
->name
, "job-k-octets") == 0 ||
5064 strcmp(attr
->name
, "job-id") == 0 ||
5065 strncmp(attr
->name
, "job-state", 9) == 0 ||
5066 strncmp(attr
->name
, "time-at-", 8) == 0)
5067 continue; /* Read-only attrs */
5069 if ((attr2
= ippFindAttribute(con
->request
, attr
->name
, IPP_TAG_ZERO
)) != NULL
)
5072 * Some other value; first free the old value...
5075 if (con
->request
->attrs
== attr2
)
5077 con
->request
->attrs
= attr2
->next
;
5082 for (prev2
= con
->request
->attrs
; prev2
!= NULL
; prev2
= prev2
->next
)
5083 if (prev2
->next
== attr2
)
5085 prev2
->next
= attr2
->next
;
5090 if (con
->request
->last
== attr2
)
5091 con
->request
->last
= prev2
;
5093 _ipp_free_attr(attr2
);
5097 * Add new option by copying it...
5100 copy_attribute(con
->request
, attr
, 0);
5104 * Then free the attribute list and option array...
5108 cupsFreeOptions(num_options
, options
);
5113 * 'reject_jobs()' - Reject print jobs to a printer.
5117 reject_jobs(client_t
*con
, /* I - Client connection */
5118 ipp_attribute_t
*uri
) /* I - Printer or class URI */
5120 cups_ptype_t dtype
; /* Destination type (printer or class) */
5121 char method
[HTTP_MAX_URI
],
5122 /* Method portion of URI */
5123 username
[HTTP_MAX_URI
],
5124 /* Username portion of URI */
5126 /* Host portion of URI */
5127 resource
[HTTP_MAX_URI
];
5128 /* Resource portion of URI */
5129 int port
; /* Port portion of URI */
5130 const char *name
; /* Printer name */
5131 printer_t
*printer
; /* Printer data */
5132 ipp_attribute_t
*attr
; /* printer-state-message text */
5135 LogMessage(L_DEBUG2
, "reject_jobs(%d, %s)\n", con
->http
.fd
,
5136 uri
->values
[0].string
.text
);
5139 * Was this operation called from the correct URI?
5142 if (strncmp(con
->uri
, "/admin/", 7) != 0)
5144 LogMessage(L_ERROR
, "reject_jobs: admin request on bad resource \'%s\'!",
5146 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5151 * Is the destination valid?
5154 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5156 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
5162 LogMessage(L_ERROR
, "reject_jobs: resource name \'%s\' no good!", resource
);
5163 send_ipp_error(con
, IPP_NOT_FOUND
);
5168 * Reject jobs sent to the printer...
5171 if (dtype
& CUPS_PRINTER_CLASS
)
5172 printer
= FindClass(name
);
5174 printer
= FindPrinter(name
);
5176 printer
->accepting
= 0;
5178 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
5179 IPP_TAG_TEXT
)) == NULL
)
5180 strcpy(printer
->state_message
, "Rejecting Jobs");
5182 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
5183 sizeof(printer
->state_message
));
5185 if (dtype
& CUPS_PRINTER_CLASS
)
5189 LogMessage(L_INFO
, "Class \'%s\' rejecting jobs (\'%s\').", name
,
5196 LogMessage(L_INFO
, "Printer \'%s\' rejecting jobs (\'%s\').", name
,
5201 * Everything was ok, so return OK status...
5204 con
->response
->request
.status
.status_code
= IPP_OK
;
5209 * 'release_job()' - Release a held print job.
5213 release_job(client_t
*con
, /* I - Client connection */
5214 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
5216 ipp_attribute_t
*attr
; /* Current attribute */
5217 int jobid
; /* Job ID */
5218 char method
[HTTP_MAX_URI
],
5219 /* Method portion of URI */
5220 username
[HTTP_MAX_URI
],
5221 /* Username portion of URI */
5223 /* Host portion of URI */
5224 resource
[HTTP_MAX_URI
];
5225 /* Resource portion of URI */
5226 int port
; /* Port portion of URI */
5227 job_t
*job
; /* Job information */
5230 LogMessage(L_DEBUG2
, "release_job(%d, %s)\n", con
->http
.fd
,
5231 uri
->values
[0].string
.text
);
5234 * Verify that the POST operation was done to a valid URI.
5237 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
5238 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
5239 strncmp(con
->uri
, "/printers/", 10) != 0)
5241 LogMessage(L_ERROR
, "release_job: release request on bad resource \'%s\'!",
5243 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5248 * See if we have a job URI or a printer URI...
5251 if (strcmp(uri
->name
, "printer-uri") == 0)
5254 * Got a printer URI; see if we also have a job-id attribute...
5257 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
5259 LogMessage(L_ERROR
, "release_job: got a printer-uri attribute but no job-id!");
5260 send_ipp_error(con
, IPP_BAD_REQUEST
);
5264 jobid
= attr
->values
[0].integer
;
5269 * Got a job URI; parse it to get the job ID...
5272 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5274 if (strncmp(resource
, "/jobs/", 6) != 0)
5280 LogMessage(L_ERROR
, "release_job: bad job-uri attribute \'%s\'!",
5281 uri
->values
[0].string
.text
);
5282 send_ipp_error(con
, IPP_BAD_REQUEST
);
5286 jobid
= atoi(resource
+ 6);
5290 * See if the job exists...
5293 if ((job
= FindJob(jobid
)) == NULL
)
5296 * Nope - return a "not found" error...
5299 LogMessage(L_ERROR
, "release_job: job #%d doesn't exist!", jobid
);
5300 send_ipp_error(con
, IPP_NOT_FOUND
);
5305 * See if job is "held"...
5308 if (job
->state
->values
[0].integer
!= IPP_JOB_HELD
)
5311 * Nope - return a "not possible" error...
5314 LogMessage(L_ERROR
, "release_job: job #%d is not held!", jobid
);
5315 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
5320 * See if the job is owned by the requesting user...
5323 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
5325 LogMessage(L_ERROR
, "release_job: \"%s\" not authorized to release job id %d owned by \"%s\"!",
5326 username
, jobid
, job
->username
);
5327 send_ipp_error(con
, IPP_FORBIDDEN
);
5332 * Reset the job-hold-until value to "no-hold"...
5335 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
5336 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
5340 free(attr
->values
[0].string
.text
);
5341 attr
->value_tag
= IPP_TAG_KEYWORD
;
5342 attr
->values
[0].string
.text
= strdup("no-hold");
5346 * Release the job and return...
5351 LogMessage(L_INFO
, "Job %d was released by \'%s\'.", jobid
, username
);
5353 con
->response
->request
.status
.status_code
= IPP_OK
;
5358 * 'restart_job()' - Restart an old print job.
5362 restart_job(client_t
*con
, /* I - Client connection */
5363 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
5365 ipp_attribute_t
*attr
; /* Current attribute */
5366 int jobid
; /* Job ID */
5367 char method
[HTTP_MAX_URI
],
5368 /* Method portion of URI */
5369 username
[HTTP_MAX_URI
],
5370 /* Username portion of URI */
5372 /* Host portion of URI */
5373 resource
[HTTP_MAX_URI
];
5374 /* Resource portion of URI */
5375 int port
; /* Port portion of URI */
5376 job_t
*job
; /* Job information */
5379 LogMessage(L_DEBUG2
, "restart_job(%d, %s)\n", con
->http
.fd
,
5380 uri
->values
[0].string
.text
);
5383 * Verify that the POST operation was done to a valid URI.
5386 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
5387 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
5388 strncmp(con
->uri
, "/printers/", 10) != 0)
5390 LogMessage(L_ERROR
, "restart_job: restart request on bad resource \'%s\'!",
5392 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5397 * See if we have a job URI or a printer URI...
5400 if (strcmp(uri
->name
, "printer-uri") == 0)
5403 * Got a printer URI; see if we also have a job-id attribute...
5406 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
5408 LogMessage(L_ERROR
, "restart_job: got a printer-uri attribute but no job-id!");
5409 send_ipp_error(con
, IPP_BAD_REQUEST
);
5413 jobid
= attr
->values
[0].integer
;
5418 * Got a job URI; parse it to get the job ID...
5421 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5423 if (strncmp(resource
, "/jobs/", 6) != 0)
5429 LogMessage(L_ERROR
, "restart_job: bad job-uri attribute \'%s\'!",
5430 uri
->values
[0].string
.text
);
5431 send_ipp_error(con
, IPP_BAD_REQUEST
);
5435 jobid
= atoi(resource
+ 6);
5439 * See if the job exists...
5442 if ((job
= FindJob(jobid
)) == NULL
)
5445 * Nope - return a "not found" error...
5448 LogMessage(L_ERROR
, "restart_job: job #%d doesn't exist!", jobid
);
5449 send_ipp_error(con
, IPP_NOT_FOUND
);
5454 * See if job is in any of the "completed" states...
5457 if (job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
)
5460 * Nope - return a "not possible" error...
5463 LogMessage(L_ERROR
, "restart_job: job #%d is not complete!", jobid
);
5464 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
5469 * See if we have retained the job files...
5472 if (!JobFiles
&& job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
5475 * Nope - return a "not possible" error...
5478 LogMessage(L_ERROR
, "restart_job: job #%d cannot be restarted - no files!", jobid
);
5479 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
5484 * See if the job is owned by the requesting user...
5487 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
5489 LogMessage(L_ERROR
, "restart_job: \"%s\" not authorized to restart job id %d owned by \"%s\"!",
5490 username
, jobid
, job
->username
);
5491 send_ipp_error(con
, IPP_FORBIDDEN
);
5496 * Restart the job and return...
5501 LogMessage(L_INFO
, "Job %d was restarted by \'%s\'.", jobid
, username
);
5503 con
->response
->request
.status
.status_code
= IPP_OK
;
5508 * 'send_document()' - Send a file to a printer or class.
5512 send_document(client_t
*con
, /* I - Client connection */
5513 ipp_attribute_t
*uri
) /* I - Printer URI */
5515 ipp_attribute_t
*attr
; /* Current attribute */
5516 ipp_attribute_t
*format
; /* Document-format attribute */
5517 int jobid
; /* Job ID number */
5518 job_t
*job
; /* Current job */
5519 char job_uri
[HTTP_MAX_URI
],
5521 method
[HTTP_MAX_URI
],
5522 /* Method portion of URI */
5523 username
[HTTP_MAX_URI
],
5524 /* Username portion of URI */
5526 /* Host portion of URI */
5527 resource
[HTTP_MAX_URI
];
5528 /* Resource portion of URI */
5529 int port
; /* Port portion of URI */
5530 mime_type_t
*filetype
; /* Type of file */
5531 char super
[MIME_MAX_SUPER
],
5532 /* Supertype of file */
5533 type
[MIME_MAX_TYPE
],
5534 /* Subtype of file */
5535 mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
5536 /* Textual name of mime type */
5537 char filename
[1024]; /* Job filename */
5538 printer_t
*printer
; /* Current printer */
5539 struct stat fileinfo
; /* File information */
5540 int kbytes
; /* Size of file */
5541 int compression
; /* Type of compression */
5544 LogMessage(L_DEBUG2
, "send_document(%d, %s)\n", con
->http
.fd
,
5545 uri
->values
[0].string
.text
);
5548 * Verify that the POST operation was done to a valid URI.
5551 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
5552 strncmp(con
->uri
, "/jobs/", 6) != 0 &&
5553 strncmp(con
->uri
, "/printers/", 10) != 0)
5555 LogMessage(L_ERROR
, "send_document: print request on bad resource \'%s\'!",
5557 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5562 * See if we have a job URI or a printer URI...
5565 if (strcmp(uri
->name
, "printer-uri") == 0)
5568 * Got a printer URI; see if we also have a job-id attribute...
5571 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
5573 LogMessage(L_ERROR
, "send_document: got a printer-uri attribute but no job-id!");
5574 send_ipp_error(con
, IPP_BAD_REQUEST
);
5578 jobid
= attr
->values
[0].integer
;
5583 * Got a job URI; parse it to get the job ID...
5586 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5588 if (strncmp(resource
, "/jobs/", 6) != 0)
5594 LogMessage(L_ERROR
, "send_document: bad job-uri attribute \'%s\'!",
5595 uri
->values
[0].string
.text
);
5596 send_ipp_error(con
, IPP_BAD_REQUEST
);
5600 jobid
= atoi(resource
+ 6);
5604 * See if the job exists...
5607 if ((job
= FindJob(jobid
)) == NULL
)
5610 * Nope - return a "not found" error...
5613 LogMessage(L_ERROR
, "send_document: job #%d doesn't exist!", jobid
);
5614 send_ipp_error(con
, IPP_NOT_FOUND
);
5619 * See if the job is owned by the requesting user...
5622 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
5624 LogMessage(L_ERROR
, "send_document: \"%s\" not authorized to send document for job id %d owned by \"%s\"!",
5625 username
, jobid
, job
->username
);
5626 send_ipp_error(con
, IPP_FORBIDDEN
);
5631 * OK, see if the client is sending the document compressed - CUPS
5632 * only supports "none" and "gzip".
5635 compression
= CUPS_FILE_NONE
;
5637 if ((attr
= ippFindAttribute(con
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
)
5639 if (strcmp(attr
->values
[0].string
.text
, "none")
5641 && strcmp(attr
->values
[0].string
.text
, "gzip")
5642 #endif /* HAVE_LIBZ */
5645 LogMessage(L_ERROR
, "print_job: Unsupported compression \"%s\"!",
5646 attr
->values
[0].string
.text
);
5647 send_ipp_error(con
, IPP_ATTRIBUTES
);
5648 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
5649 "compression", NULL
, attr
->values
[0].string
.text
);
5654 if (!strcmp(attr
->values
[0].string
.text
, "gzip"))
5655 compression
= CUPS_FILE_GZIP
;
5656 #endif /* HAVE_LIBZ */
5660 * Do we have a file to print?
5665 LogMessage(L_ERROR
, "send_document: No file!?!");
5666 send_ipp_error(con
, IPP_BAD_REQUEST
);
5671 * Is it a format we support?
5674 if ((format
= ippFindAttribute(con
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
5677 * Grab format from client...
5680 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
, type
) != 2)
5682 LogMessage(L_ERROR
, "send_document: could not scan type \'%s\'!",
5683 format
->values
[0].string
.text
);
5684 send_ipp_error(con
, IPP_BAD_REQUEST
);
5691 * No document format attribute? Auto-type it!
5694 strcpy(super
, "application");
5695 strcpy(type
, "octet-stream");
5698 if (strcmp(super
, "application") == 0 &&
5699 strcmp(type
, "octet-stream") == 0)
5702 * Auto-type the file...
5705 LogMessage(L_DEBUG
, "send_document: auto-typing file...");
5707 filetype
= mimeFileType(MimeDatabase
, con
->filename
, &compression
);
5709 if (filetype
!= NULL
)
5712 * Replace the document-format attribute value with the auto-typed one.
5715 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
5720 free(format
->values
[0].string
.text
);
5721 format
->values
[0].string
.text
= strdup(mimetype
);
5724 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
5725 "document-format", NULL
, mimetype
);
5728 filetype
= mimeType(MimeDatabase
, super
, type
);
5731 filetype
= mimeType(MimeDatabase
, super
, type
);
5733 if (filetype
== NULL
)
5735 LogMessage(L_ERROR
, "send_document: Unsupported format \'%s/%s\'!",
5737 LogMessage(L_INFO
, "Hint: Do you have the raw file printing rules enabled?");
5738 send_ipp_error(con
, IPP_DOCUMENT_FORMAT
);
5741 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
5742 "document-format", NULL
, format
->values
[0].string
.text
);
5747 LogMessage(L_DEBUG
, "send_document: request file type is %s/%s.",
5748 filetype
->super
, filetype
->type
);
5751 * Add the file to the job...
5754 if (add_file(con
, job
, filetype
, compression
))
5757 if (job
->dtype
& CUPS_PRINTER_CLASS
)
5758 printer
= FindClass(job
->dest
);
5760 printer
= FindPrinter(job
->dest
);
5762 if (stat(con
->filename
, &fileinfo
))
5765 kbytes
= (fileinfo
.st_size
+ 1023) / 1024;
5767 UpdateQuota(printer
, job
->username
, 0, kbytes
);
5769 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
5770 attr
->values
[0].integer
+= kbytes
;
5772 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
5774 rename(con
->filename
, filename
);
5776 ClearString(&con
->filename
);
5778 LogMessage(L_INFO
, "File of type %s/%s queued in job #%d by \'%s\'.",
5779 filetype
->super
, filetype
->type
, job
->id
, job
->username
);
5782 * Start the job if this is the last document...
5785 if ((attr
= ippFindAttribute(con
->request
, "last-document", IPP_TAG_BOOLEAN
)) != NULL
&&
5786 attr
->values
[0].boolean
)
5789 * See if we need to add the ending sheet...
5792 if (printer
!= NULL
&& !(printer
->type
& CUPS_PRINTER_REMOTE
) &&
5793 (attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
&&
5794 attr
->num_values
> 1)
5800 kbytes
= copy_banner(con
, job
, attr
->values
[1].string
.text
);
5801 UpdateQuota(printer
, job
->username
, 0, kbytes
);
5804 if (job
->state
->values
[0].integer
== IPP_JOB_STOPPED
)
5805 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
5806 else if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
5808 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
5809 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
5811 if (attr
== NULL
|| strcmp(attr
->values
[0].string
.text
, "no-hold") == 0)
5812 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
5820 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
5821 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
5823 if (attr
== NULL
|| strcmp(attr
->values
[0].string
.text
, "no-hold") == 0)
5825 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
5826 job
->hold_until
= time(NULL
) + 60;
5832 * Fill in the response info...
5836 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
5837 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
5838 ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), job
->id
);
5840 #endif /* AF_INET6 */
5841 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
5842 ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), job
->id
);
5844 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
,
5847 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
5849 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
5850 job
->state
->values
[0].integer
);
5851 add_job_state_reasons(con
, job
);
5853 con
->response
->request
.status
.status_code
= IPP_OK
;
5858 * 'send_ipp_error()' - Send an error status back to the IPP client.
5862 send_ipp_error(client_t
*con
, /* I - Client connection */
5863 ipp_status_t status
) /* I - IPP status code */
5865 LogMessage(L_DEBUG2
, "send_ipp_error(%d, %x)\n", con
->http
.fd
, status
);
5867 LogMessage(L_DEBUG
, "Sending error: %s", ippErrorString(status
));
5869 con
->response
->request
.status
.status_code
= status
;
5871 if (ippFindAttribute(con
->response
, "attributes-charset", IPP_TAG_ZERO
) == NULL
)
5872 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
5873 "attributes-charset", NULL
, DefaultCharset
);
5875 if (ippFindAttribute(con
->response
, "attributes-natural-language",
5876 IPP_TAG_ZERO
) == NULL
)
5877 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
5878 "attributes-natural-language", NULL
, DefaultLanguage
);
5883 * 'set_default()' - Set the default destination...
5887 set_default(client_t
*con
, /* I - Client connection */
5888 ipp_attribute_t
*uri
) /* I - Printer URI */
5890 cups_ptype_t dtype
; /* Destination type (printer or class) */
5891 char method
[HTTP_MAX_URI
],
5892 /* Method portion of URI */
5893 username
[HTTP_MAX_URI
],
5894 /* Username portion of URI */
5896 /* Host portion of URI */
5897 resource
[HTTP_MAX_URI
];
5898 /* Resource portion of URI */
5899 int port
; /* Port portion of URI */
5900 const char *name
; /* Printer name */
5903 LogMessage(L_DEBUG2
, "set_default(%d, %s)\n", con
->http
.fd
,
5904 uri
->values
[0].string
.text
);
5907 * Was this operation called from the correct URI?
5910 if (strncmp(con
->uri
, "/admin/", 7) != 0)
5912 LogMessage(L_ERROR
, "set_default: admin request on bad resource \'%s\'!",
5914 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5919 * Is the destination valid?
5922 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5924 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
5930 LogMessage(L_ERROR
, "set_default: resource name \'%s\' no good!", resource
);
5931 send_ipp_error(con
, IPP_NOT_FOUND
);
5936 * Set it as the default...
5939 if (dtype
& CUPS_PRINTER_CLASS
)
5940 DefaultPrinter
= FindClass(name
);
5942 DefaultPrinter
= FindPrinter(name
);
5947 LogMessage(L_INFO
, "Default destination set to \'%s\' by \'%s\'.", name
,
5951 * Everything was ok, so return OK status...
5954 con
->response
->request
.status
.status_code
= IPP_OK
;
5959 * 'set_job_attrs()' - Set job attributes.
5963 set_job_attrs(client_t
*con
, /* I - Client connection */
5964 ipp_attribute_t
*uri
) /* I - Job URI */
5966 ipp_attribute_t
*attr
, /* Current attribute */
5967 *attr2
, /* Job attribute */
5968 *prev2
; /* Previous job attribute */
5969 int jobid
; /* Job ID */
5970 job_t
*job
; /* Current job */
5971 char method
[HTTP_MAX_URI
],
5972 /* Method portion of URI */
5973 username
[HTTP_MAX_URI
],
5974 /* Username portion of URI */
5976 /* Host portion of URI */
5977 resource
[HTTP_MAX_URI
];
5978 /* Resource portion of URI */
5979 int port
; /* Port portion of URI */
5982 LogMessage(L_DEBUG2
, "set_job_attrs(%d, %s)\n", con
->http
.fd
,
5983 uri
->values
[0].string
.text
);
5986 * See if we have a job URI or a printer URI...
5989 if (strcmp(uri
->name
, "printer-uri") == 0)
5992 * Got a printer URI; see if we also have a job-id attribute...
5995 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
5997 LogMessage(L_ERROR
, "set_job_attrs: got a printer-uri attribute but no job-id!");
5998 send_ipp_error(con
, IPP_BAD_REQUEST
);
6002 jobid
= attr
->values
[0].integer
;
6007 * Got a job URI; parse it to get the job ID...
6010 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6012 if (strncmp(resource
, "/jobs/", 6) != 0)
6018 LogMessage(L_ERROR
, "set_job_attrs: bad job-uri attribute \'%s\'!\n",
6019 uri
->values
[0].string
.text
);
6020 send_ipp_error(con
, IPP_BAD_REQUEST
);
6024 jobid
= atoi(resource
+ 6);
6028 * See if the job exists...
6031 if ((job
= FindJob(jobid
)) == NULL
)
6034 * Nope - return a "not found" error...
6037 LogMessage(L_ERROR
, "set_job_attrs: job #%d doesn't exist!", jobid
);
6038 send_ipp_error(con
, IPP_NOT_FOUND
);
6043 * See if the job has been completed...
6046 if (job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
6049 * Return a "not-possible" error...
6052 LogMessage(L_ERROR
, "set_job_attrs: job #%d is finished and cannot be altered!", jobid
);
6053 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
6058 * See if the job is owned by the requesting user...
6061 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
6063 LogMessage(L_ERROR
, "set_job_attrs: \"%s\" not authorized to alter job id %d owned by \"%s\"!",
6064 username
, jobid
, job
->username
);
6065 send_ipp_error(con
, IPP_FORBIDDEN
);
6070 * See what the user wants to change.
6073 for (attr
= con
->request
->attrs
; attr
!= NULL
; attr
= attr
->next
)
6075 if (attr
->group_tag
!= IPP_TAG_JOB
|| !attr
->name
)
6078 if (strcmp(attr
->name
, "job-originating-host-name") == 0 ||
6079 strcmp(attr
->name
, "job-originating-user-name") == 0 ||
6080 strcmp(attr
->name
, "job-media-sheets-completed") == 0 ||
6081 strcmp(attr
->name
, "job-k-octets") == 0 ||
6082 strcmp(attr
->name
, "job-id") == 0 ||
6083 strcmp(attr
->name
, "job-sheets") == 0 ||
6084 strncmp(attr
->name
, "time-at-", 8) == 0)
6085 continue; /* Read-only attrs */
6087 if (strcmp(attr
->name
, "job-priority") == 0 &&
6088 attr
->value_tag
== IPP_TAG_INTEGER
&&
6089 job
->state
->values
[0].integer
!= IPP_JOB_PROCESSING
)
6092 * Change the job priority
6095 SetJobPriority(jobid
, attr
->values
[0].integer
);
6097 else if ((attr2
= ippFindAttribute(job
->attrs
, attr
->name
, IPP_TAG_ZERO
)) != NULL
)
6100 * Some other value; first free the old value...
6103 if (job
->attrs
->attrs
== attr2
)
6105 job
->attrs
->attrs
= attr2
->next
;
6110 for (prev2
= job
->attrs
->attrs
; prev2
!= NULL
; prev2
= prev2
->next
)
6111 if (prev2
->next
== attr2
)
6113 prev2
->next
= attr2
->next
;
6118 if (job
->attrs
->last
== attr2
)
6119 job
->attrs
->last
= prev2
;
6121 _ipp_free_attr(attr2
);
6124 * Then copy the attribute...
6127 copy_attribute(job
->attrs
, attr
, 0);
6130 * See if the job-name or job-hold-until is being changed.
6133 if (strcmp(attr
->name
, "job-hold-until") == 0)
6135 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
6137 if (strcmp(attr
->values
[0].string
.text
, "no-hold") == 0)
6138 ReleaseJob(job
->id
);
6143 else if (attr
->value_tag
== IPP_TAG_DELETEATTR
)
6146 * Delete the attribute...
6149 for (attr2
= job
->attrs
->attrs
, prev2
= NULL
;
6151 prev2
= attr2
, attr2
= attr2
->next
)
6152 if (attr2
->name
&& strcmp(attr2
->name
, attr
->name
) == 0)
6158 prev2
->next
= attr2
->next
;
6160 job
->attrs
->attrs
= attr2
->next
;
6162 if (attr2
== job
->attrs
->last
)
6163 job
->attrs
->last
= prev2
;
6165 _ipp_free_attr(attr2
);
6171 * Add new option by copying it...
6174 copy_attribute(job
->attrs
, attr
, 0);
6185 * Start jobs if possible...
6191 * Return with "everything is OK" status...
6194 con
->response
->request
.status
.status_code
= IPP_OK
;
6199 * 'start_printer()' - Start a printer.
6203 start_printer(client_t
*con
, /* I - Client connection */
6204 ipp_attribute_t
*uri
) /* I - Printer URI */
6206 cups_ptype_t dtype
; /* Destination type (printer or class) */
6207 char method
[HTTP_MAX_URI
],
6208 /* Method portion of URI */
6209 username
[HTTP_MAX_URI
],
6210 /* Username portion of URI */
6212 /* Host portion of URI */
6213 resource
[HTTP_MAX_URI
];
6214 /* Resource portion of URI */
6215 int port
; /* Port portion of URI */
6216 const char *name
; /* Printer name */
6217 printer_t
*printer
; /* Printer data */
6220 LogMessage(L_DEBUG2
, "start_printer(%d, %s)\n", con
->http
.fd
,
6221 uri
->values
[0].string
.text
);
6224 * Was this operation called from the correct URI?
6227 if (strncmp(con
->uri
, "/admin/", 7) != 0)
6229 LogMessage(L_ERROR
, "start_printer: admin request on bad resource \'%s\'!",
6231 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
6236 * Is the destination valid?
6239 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6241 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
6247 LogMessage(L_ERROR
, "start_printer: resource name \'%s\' no good!", resource
);
6248 send_ipp_error(con
, IPP_NOT_FOUND
);
6253 * Start the printer...
6256 if (dtype
& CUPS_PRINTER_CLASS
)
6257 printer
= FindClass(name
);
6259 printer
= FindPrinter(name
);
6261 StartPrinter(printer
);
6263 printer
->state_message
[0] = '\0';
6265 if (dtype
& CUPS_PRINTER_CLASS
)
6269 LogMessage(L_INFO
, "Class \'%s\' started by \'%s\'.", name
,
6276 LogMessage(L_INFO
, "Printer \'%s\' started by \'%s\'.", name
,
6283 * Everything was ok, so return OK status...
6286 con
->response
->request
.status
.status_code
= IPP_OK
;
6291 * 'stop_printer()' - Stop a printer.
6295 stop_printer(client_t
*con
, /* I - Client connection */
6296 ipp_attribute_t
*uri
) /* I - Printer URI */
6298 cups_ptype_t dtype
; /* Destination type (printer or class) */
6299 char method
[HTTP_MAX_URI
],
6300 /* Method portion of URI */
6301 username
[HTTP_MAX_URI
],
6302 /* Username portion of URI */
6304 /* Host portion of URI */
6305 resource
[HTTP_MAX_URI
];
6306 /* Resource portion of URI */
6307 int port
; /* Port portion of URI */
6308 const char *name
; /* Printer name */
6309 printer_t
*printer
; /* Printer data */
6310 ipp_attribute_t
*attr
; /* printer-state-message attribute */
6313 LogMessage(L_DEBUG2
, "stop_printer(%d, %s)\n", con
->http
.fd
,
6314 uri
->values
[0].string
.text
);
6317 * Was this operation called from the correct URI?
6320 if (strncmp(con
->uri
, "/admin/", 7) != 0)
6322 LogMessage(L_ERROR
, "stop_printer: admin request on bad resource \'%s\'!",
6324 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
6329 * Is the destination valid?
6332 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6334 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
6340 LogMessage(L_ERROR
, "stop_printer: resource name \'%s\' no good!", resource
);
6341 send_ipp_error(con
, IPP_NOT_FOUND
);
6346 * Stop the printer...
6349 if (dtype
& CUPS_PRINTER_CLASS
)
6350 printer
= FindClass(name
);
6352 printer
= FindPrinter(name
);
6354 StopPrinter(printer
);
6356 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
6357 IPP_TAG_TEXT
)) == NULL
)
6358 strcpy(printer
->state_message
, "Paused");
6361 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
6362 sizeof(printer
->state_message
));
6365 if (dtype
& CUPS_PRINTER_CLASS
)
6369 LogMessage(L_INFO
, "Class \'%s\' stopped by \'%s\'.", name
,
6376 LogMessage(L_INFO
, "Printer \'%s\' stopped by \'%s\'.", name
,
6381 * Everything was ok, so return OK status...
6384 con
->response
->request
.status
.status_code
= IPP_OK
;
6389 * 'validate_job()' - Validate printer options and destination.
6393 validate_job(client_t
*con
, /* I - Client connection */
6394 ipp_attribute_t
*uri
) /* I - Printer URI */
6396 ipp_attribute_t
*attr
; /* Current attribute */
6397 ipp_attribute_t
*format
; /* Document-format attribute */
6398 cups_ptype_t dtype
; /* Destination type (printer or class) */
6399 char method
[HTTP_MAX_URI
],
6400 /* Method portion of URI */
6401 username
[HTTP_MAX_URI
],
6402 /* Username portion of URI */
6404 /* Host portion of URI */
6405 resource
[HTTP_MAX_URI
];
6406 /* Resource portion of URI */
6407 int port
; /* Port portion of URI */
6408 char super
[MIME_MAX_SUPER
],
6409 /* Supertype of file */
6410 type
[MIME_MAX_TYPE
];
6411 /* Subtype of file */
6414 LogMessage(L_DEBUG2
, "validate_job(%d, %s)\n", con
->http
.fd
,
6415 uri
->values
[0].string
.text
);
6418 * Verify that the POST operation was done to a valid URI.
6421 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
6422 strncmp(con
->uri
, "/printers/", 10) != 0)
6424 LogMessage(L_ERROR
, "validate_job: request on bad resource \'%s\'!",
6426 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
6431 * OK, see if the client is sending the document compressed - CUPS
6432 * doesn't support compression yet...
6435 if ((attr
= ippFindAttribute(con
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
&&
6436 strcmp(attr
->values
[0].string
.text
, "none") == 0)
6438 LogMessage(L_ERROR
, "validate_job: Unsupported compression attribute %s!",
6439 attr
->values
[0].string
.text
);
6440 send_ipp_error(con
, IPP_ATTRIBUTES
);
6441 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
6442 "compression", NULL
, attr
->values
[0].string
.text
);
6447 * Is it a format we support?
6450 if ((format
= ippFindAttribute(con
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
6452 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
, type
) != 2)
6454 LogMessage(L_ERROR
, "validate_job: could not scan type \'%s\'!\n",
6455 format
->values
[0].string
.text
);
6456 send_ipp_error(con
, IPP_BAD_REQUEST
);
6460 if ((strcmp(super
, "application") != 0 ||
6461 strcmp(type
, "octet-stream") != 0) &&
6462 mimeType(MimeDatabase
, super
, type
) == NULL
)
6464 LogMessage(L_ERROR
, "validate_job: Unsupported format \'%s\'!\n",
6465 format
->values
[0].string
.text
);
6466 LogMessage(L_INFO
, "Hint: Do you have the raw file printing rules enabled?");
6467 send_ipp_error(con
, IPP_DOCUMENT_FORMAT
);
6468 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
6469 "document-format", NULL
, format
->values
[0].string
.text
);
6475 * Is the destination valid?
6478 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6480 if (ValidateDest(host
, resource
, &dtype
) == NULL
)
6486 LogMessage(L_ERROR
, "validate_job: resource name \'%s\' no good!", resource
);
6487 send_ipp_error(con
, IPP_NOT_FOUND
);
6492 * Everything was ok, so return OK status...
6495 con
->response
->request
.status
.status_code
= IPP_OK
;
6500 * 'validate_user()' - Validate the user for the request.
6503 static int /* O - 1 if permitted, 0 otherwise */
6504 validate_user(client_t
*con
, /* I - Client connection */
6505 const char *owner
, /* I - Owner of job/resource */
6506 char *username
, /* O - Authenticated username */
6507 int userlen
) /* I - Length of username */
6509 int i
, j
; /* Looping vars */
6510 ipp_attribute_t
*attr
; /* requesting-user-name attribute */
6511 struct passwd
*user
; /* User info */
6512 struct group
*group
; /* System group info */
6513 char junk
[33]; /* MD5 password (not used) */
6516 LogMessage(L_DEBUG2
, "validate_user(%d, %s, %s, %d)\n", con
->http
.fd
,
6517 owner
, username
, userlen
);
6523 if (con
== NULL
|| owner
== NULL
|| username
== NULL
|| userlen
<= 0)
6527 * Get the best authenticated username that is available.
6530 if (con
->username
[0])
6531 strlcpy(username
, con
->username
, userlen
);
6532 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
6533 strlcpy(username
, attr
->values
[0].string
.text
, userlen
);
6535 strlcpy(username
, "anonymous", userlen
);
6538 * Check the username against the owner...
6541 if (strcasecmp(username
, owner
) != 0 && strcasecmp(username
, "root") != 0)
6544 * Not the owner or root; check to see if the user is a member of the
6548 user
= getpwnam(username
);
6551 for (i
= 0, j
= 0, group
= NULL
; i
< NumSystemGroups
; i
++)
6553 group
= getgrnam(SystemGroups
[i
]);
6558 for (j
= 0; group
->gr_mem
[j
]; j
++)
6559 if (strcasecmp(username
, group
->gr_mem
[j
]) == 0)
6562 if (group
->gr_mem
[j
])
6569 if (user
== NULL
|| group
== NULL
||
6570 (group
->gr_mem
[j
] == NULL
&& group
->gr_gid
!= user
->pw_gid
))
6573 * Username not found, group not found, or user is not part of the
6574 * system group... Check for a user and group in the MD5 password
6578 for (i
= 0; i
< NumSystemGroups
; i
++)
6579 if (GetMD5Passwd(username
, SystemGroups
[i
], junk
) != NULL
)
6583 * Nope, not an MD5 user, either. Return 0 indicating no-go...
6595 * End of "$Id: ipp.c,v 1.127.2.58 2003/04/08 03:48:08 mike Exp $".