2 * "$Id: ipp.c,v 1.127.2.71 2003/07/20 12:51:47 mike Exp $"
4 * IPP routines for the Common UNIX Printing System (CUPS) scheduler.
6 * Copyright 1997-2003 by Easy Software Products, all rights reserved.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * ProcessIPPRequest() - Process an incoming IPP request...
27 * accept_jobs() - Accept print jobs to a printer.
28 * add_class() - Add a class to the system.
29 * add_file() - Add a file to a job.
30 * add_job_state_reasons() - Add the "job-state-reasons" attribute based
31 * upon the job and printer state...
32 * add_printer() - Add a printer to the system.
33 * add_printer_state_reasons() - Add the "printer-state-reasons" attribute
34 * based upon the printer state...
35 * add_queued_job_count() - Add the "queued-job-count" attribute for
36 * cancel_all_jobs() - Cancel all print jobs.
37 * cancel_job() - Cancel a print job.
38 * check_quotas() - Check quotas for a printer and user.
39 * copy_attribute() - Copy a single attribute.
40 * copy_attrs() - Copy attributes from one request to another.
41 * copy_banner() - Copy a banner file to the requests directory
42 * for the specified job.
43 * copy_file() - Copy a PPD file or interface script...
44 * copy_model() - Copy a PPD model file, substituting default
46 * create_job() - Print a file to a printer or class.
47 * delete_printer() - Remove a printer or class from the system.
48 * get_default() - Get the default destination.
49 * get_devices() - Get the list of available devices on the
51 * get_jobs() - Get a list of jobs for the specified printer.
52 * get_job_attrs() - Get job attributes.
53 * get_ppds() - Get the list of PPD files on the local
55 * get_printer_attrs() - Get printer attributes.
56 * get_printers() - Get a list of printers.
57 * hold_job() - Hold a print job.
58 * move_job() - Move a job to a new destination.
59 * ppd_add_default() - Add a PPD default choice.
60 * ppd_parse_line() - Parse a PPD default line.
61 * print_job() - Print a file to a printer or class.
62 * read_ps_line() - Read a line from a PS file...
63 * read_ps_job_ticket() - Reads a job ticket embedded in a PS file.
64 * reject_jobs() - Reject print jobs to a printer.
65 * release_job() - Release a held print job.
66 * restart_job() - Restart an old print job.
67 * send_document() - Send a file to a printer or class.
68 * send_ipp_error() - Send an error status back to the IPP client.
69 * set_default() - Set the default destination...
70 * set_job_attrs() - Set job attributes.
71 * start_printer() - Start a printer.
72 * stop_printer() - Stop a printer.
73 * validate_job() - Validate printer options and destination.
74 * validate_user() - Validate the user for the request.
78 * Include necessary headers...
87 #endif /* HAVE_LIBPAPER */
91 * PPD default choice structure...
96 char option
[PPD_MAX_NAME
]; /* Main keyword (option name) */
97 char choice
[PPD_MAX_NAME
]; /* Option keyword (choice name) */
106 static void accept_jobs(client_t
*con
, ipp_attribute_t
*uri
);
107 static void add_class(client_t
*con
, ipp_attribute_t
*uri
);
108 static int add_file(client_t
*con
, job_t
*job
, mime_type_t
*filetype
,
110 static void add_job_state_reasons(client_t
*con
, job_t
*job
);
111 static void add_printer(client_t
*con
, ipp_attribute_t
*uri
);
112 static void add_printer_state_reasons(client_t
*con
, printer_t
*p
);
113 static void add_queued_job_count(client_t
*con
, printer_t
*p
);
114 static void cancel_all_jobs(client_t
*con
, ipp_attribute_t
*uri
);
115 static void cancel_job(client_t
*con
, ipp_attribute_t
*uri
);
116 static int check_quotas(client_t
*con
, printer_t
*p
);
117 static ipp_attribute_t
*copy_attribute(ipp_t
*to
, ipp_attribute_t
*attr
,
119 static void copy_attrs(ipp_t
*to
, ipp_t
*from
, ipp_attribute_t
*req
,
120 ipp_tag_t group
, int quickcopy
);
121 static int copy_banner(client_t
*con
, job_t
*job
, const char *name
);
122 static int copy_file(const char *from
, const char *to
);
123 static int copy_model(const char *from
, const char *to
);
124 static void create_job(client_t
*con
, ipp_attribute_t
*uri
);
125 static void delete_printer(client_t
*con
, ipp_attribute_t
*uri
);
126 static void get_default(client_t
*con
);
127 static void get_devices(client_t
*con
);
128 static void get_jobs(client_t
*con
, ipp_attribute_t
*uri
);
129 static void get_job_attrs(client_t
*con
, ipp_attribute_t
*uri
);
130 static void get_ppds(client_t
*con
);
131 static void get_printers(client_t
*con
, int type
);
132 static void get_printer_attrs(client_t
*con
, ipp_attribute_t
*uri
);
133 static void hold_job(client_t
*con
, ipp_attribute_t
*uri
);
134 static void move_job(client_t
*con
, ipp_attribute_t
*uri
);
135 static int ppd_add_default(const char *option
, const char *choice
,
136 int num_defaults
, ppd_default_t
**defaults
);
137 static int ppd_parse_line(const char *line
, char *option
, int olen
,
138 char *choice
, int clen
);
139 static void print_job(client_t
*con
, ipp_attribute_t
*uri
);
140 static void read_ps_job_ticket(client_t
*con
);
141 static void reject_jobs(client_t
*con
, ipp_attribute_t
*uri
);
142 static void release_job(client_t
*con
, ipp_attribute_t
*uri
);
143 static void restart_job(client_t
*con
, ipp_attribute_t
*uri
);
144 static void send_document(client_t
*con
, ipp_attribute_t
*uri
);
145 static void send_ipp_error(client_t
*con
, ipp_status_t status
);
146 static void set_default(client_t
*con
, ipp_attribute_t
*uri
);
147 static void set_job_attrs(client_t
*con
, ipp_attribute_t
*uri
);
148 static void start_printer(client_t
*con
, ipp_attribute_t
*uri
);
149 static void stop_printer(client_t
*con
, ipp_attribute_t
*uri
);
150 static void validate_job(client_t
*con
, ipp_attribute_t
*uri
);
151 static int validate_user(client_t
*con
, const char *owner
, char *username
,
156 * 'ProcessIPPRequest()' - Process an incoming IPP request...
159 int /* O - 1 on success, 0 on failure */
160 ProcessIPPRequest(client_t
*con
) /* I - Client connection */
162 ipp_tag_t group
; /* Current group tag */
163 ipp_attribute_t
*attr
; /* Current attribute */
164 ipp_attribute_t
*charset
; /* Character set attribute */
165 ipp_attribute_t
*language
; /* Language attribute */
166 ipp_attribute_t
*uri
; /* Printer URI attribute */
167 ipp_attribute_t
*username
; /* requesting-user-name attr */
170 DEBUG_printf(("ProcessIPPRequest(%08x)\n", con
));
171 DEBUG_printf(("ProcessIPPRequest: operation_id = %04x\n",
172 con
->request
->request
.op
.operation_id
));
175 * First build an empty response message for this request...
178 con
->response
= ippNew();
180 con
->response
->request
.status
.version
[0] = con
->request
->request
.op
.version
[0];
181 con
->response
->request
.status
.version
[1] = con
->request
->request
.op
.version
[1];
182 con
->response
->request
.status
.request_id
= con
->request
->request
.op
.request_id
;
185 * Then validate the request header and required attributes...
188 if (con
->request
->request
.any
.version
[0] != 1)
191 * Return an error, since we only support IPP 1.x.
194 LogMessage(L_ERROR
, "ProcessIPPRequest: bad request version (%d.%d)!",
195 con
->request
->request
.any
.version
[0],
196 con
->request
->request
.any
.version
[1]);
198 send_ipp_error(con
, IPP_VERSION_NOT_SUPPORTED
);
200 else if (con
->request
->attrs
== NULL
)
202 LogMessage(L_ERROR
, "ProcessIPPRequest: no attributes in request!");
203 send_ipp_error(con
, IPP_BAD_REQUEST
);
208 * Make sure that the attributes are provided in the correct order and
209 * don't repeat groups...
212 for (attr
= con
->request
->attrs
, group
= attr
->group_tag
;
215 if (attr
->group_tag
< group
)
218 * Out of order; return an error...
221 LogMessage(L_ERROR
, "ProcessIPPRequest: attribute groups are out of order!");
222 send_ipp_error(con
, IPP_BAD_REQUEST
);
226 group
= attr
->group_tag
;
231 * Then make sure that the first three attributes are:
234 * attributes-natural-language
235 * printer-uri/job-uri
238 attr
= con
->request
->attrs
;
239 if (attr
!= NULL
&& strcmp(attr
->name
, "attributes-charset") == 0 &&
240 attr
->value_tag
== IPP_TAG_CHARSET
)
247 if (attr
!= NULL
&& strcmp(attr
->name
, "attributes-natural-language") == 0 &&
248 attr
->value_tag
== IPP_TAG_LANGUAGE
)
253 if ((attr
= ippFindAttribute(con
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
255 else if ((attr
= ippFindAttribute(con
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
261 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
262 "attributes-charset", NULL
, charset
->values
[0].string
.text
);
264 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
265 "attributes-charset", NULL
, DefaultCharset
);
268 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
269 "attributes-natural-language", NULL
,
270 language
->values
[0].string
.text
);
272 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
273 "attributes-natural-language", NULL
, DefaultLanguage
);
275 if (charset
== NULL
|| language
== NULL
||
277 con
->request
->request
.op
.operation_id
!= CUPS_GET_DEFAULT
&&
278 con
->request
->request
.op
.operation_id
!= CUPS_GET_PRINTERS
&&
279 con
->request
->request
.op
.operation_id
!= CUPS_GET_CLASSES
&&
280 con
->request
->request
.op
.operation_id
!= CUPS_GET_DEVICES
&&
281 con
->request
->request
.op
.operation_id
!= CUPS_GET_PPDS
))
284 * Return an error, since attributes-charset,
285 * attributes-natural-language, and printer-uri/job-uri are required
286 * for all operations.
290 LogMessage(L_ERROR
, "ProcessIPPRequest: missing attributes-charset attribute!");
292 if (language
== NULL
)
293 LogMessage(L_ERROR
, "ProcessIPPRequest: missing attributes-natural-language attribute!");
296 LogMessage(L_ERROR
, "ProcessIPPRequest: missing printer-uri or job-uri attribute!");
298 send_ipp_error(con
, IPP_BAD_REQUEST
);
303 * OK, all the checks pass so far; make sure requesting-user-name is
304 * not "root" from a remote host...
307 if ((username
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
310 * Check for root user...
313 if (strcmp(username
->values
[0].string
.text
, "root") == 0 &&
314 strcasecmp(con
->http
.hostname
, "localhost") != 0 &&
315 strcmp(con
->username
, "root") != 0)
318 * Remote unauthenticated user masquerading as local root...
321 free(username
->values
[0].string
.text
);
322 username
->values
[0].string
.text
= strdup(RemoteRoot
);
327 * Then try processing the operation...
330 switch (con
->request
->request
.op
.operation_id
)
336 case IPP_VALIDATE_JOB
:
337 validate_job(con
, uri
);
340 case IPP_CREATE_JOB
:
341 create_job(con
, uri
);
344 case IPP_SEND_DOCUMENT
:
345 send_document(con
, uri
);
348 case IPP_CANCEL_JOB
:
349 cancel_job(con
, uri
);
352 case IPP_GET_JOB_ATTRIBUTES
:
353 get_job_attrs(con
, uri
);
360 case IPP_GET_PRINTER_ATTRIBUTES
:
361 get_printer_attrs(con
, uri
);
368 case IPP_RELEASE_JOB
:
369 release_job(con
, uri
);
372 case IPP_RESTART_JOB
:
373 restart_job(con
, uri
);
376 case IPP_PAUSE_PRINTER
:
377 stop_printer(con
, uri
);
380 case IPP_RESUME_PRINTER
:
381 start_printer(con
, uri
);
384 case IPP_PURGE_JOBS
:
385 cancel_all_jobs(con
, uri
);
388 case IPP_SET_JOB_ATTRIBUTES
:
389 set_job_attrs(con
, uri
);
392 case CUPS_GET_DEFAULT
:
396 case CUPS_GET_PRINTERS
:
397 get_printers(con
, 0);
400 case CUPS_GET_CLASSES
:
401 get_printers(con
, CUPS_PRINTER_CLASS
);
404 case CUPS_ADD_PRINTER
:
405 add_printer(con
, uri
);
408 case CUPS_DELETE_PRINTER
:
409 delete_printer(con
, uri
);
412 case CUPS_ADD_CLASS
:
416 case CUPS_DELETE_CLASS
:
417 delete_printer(con
, uri
);
420 case CUPS_ACCEPT_JOBS
:
421 case IPP_ENABLE_PRINTER
:
422 accept_jobs(con
, uri
);
425 case CUPS_REJECT_JOBS
:
426 case IPP_DISABLE_PRINTER
:
427 reject_jobs(con
, uri
);
430 case CUPS_SET_DEFAULT
:
431 set_default(con
, uri
);
434 case CUPS_GET_DEVICES
:
447 send_ipp_error(con
, IPP_OPERATION_NOT_SUPPORTED
);
453 LogMessage(L_DEBUG
, "ProcessIPPRequest: %d status_code=%x",
454 con
->http
.fd
, con
->response
->request
.status
.status_code
);
456 if (SendHeader(con
, HTTP_OK
, "application/ipp"))
459 if (con
->http
.version
== HTTP_1_1
)
461 con
->http
.data_encoding
= HTTP_ENCODE_CHUNKED
;
463 httpPrintf(HTTP(con
), "Transfer-Encoding: chunked\r\n\r\n");
468 con
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
469 con
->http
.data_remaining
= ippLength(con
->response
);
471 httpPrintf(HTTP(con
), "Content-Length: %d\r\n\r\n",
472 con
->http
.data_remaining
);
475 LogMessage(L_DEBUG2
, "ProcessIPPRequest: Adding fd %d to OutputSet...",
478 FD_SET(con
->http
.fd
, OutputSet
);
481 * Tell the caller the response header was sent successfully...
489 * Tell the caller the response header could not be sent...
498 * 'accept_jobs()' - Accept print jobs to a printer.
502 accept_jobs(client_t
*con
, /* I - Client connection */
503 ipp_attribute_t
*uri
) /* I - Printer or class URI */
505 cups_ptype_t dtype
; /* Destination type (printer or class) */
506 char method
[HTTP_MAX_URI
],
507 /* Method portion of URI */
508 username
[HTTP_MAX_URI
],
509 /* Username portion of URI */
511 /* Host portion of URI */
512 resource
[HTTP_MAX_URI
];
513 /* Resource portion of URI */
514 int port
; /* Port portion of URI */
515 const char *name
; /* Printer name */
516 printer_t
*printer
; /* Printer data */
519 LogMessage(L_DEBUG2
, "accept_jobs(%d, %s)\n", con
->http
.fd
,
520 uri
->values
[0].string
.text
);
523 * Was this operation called from the correct URI?
526 if (strncmp(con
->uri
, "/admin/", 7) != 0)
528 LogMessage(L_ERROR
, "accept_jobs: admin request on bad resource \'%s\'!",
530 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
535 * Is the destination valid?
538 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
540 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
546 LogMessage(L_ERROR
, "accept_jobs: resource name \'%s\' no good!", resource
);
547 send_ipp_error(con
, IPP_NOT_FOUND
);
552 * Accept jobs sent to the printer...
555 if (dtype
& CUPS_PRINTER_CLASS
)
556 printer
= FindClass(name
);
558 printer
= FindPrinter(name
);
560 printer
->accepting
= 1;
561 printer
->state_message
[0] = '\0';
563 AddPrinterHistory(printer
);
565 if (dtype
& CUPS_PRINTER_CLASS
)
570 LogMessage(L_INFO
, "Printer \'%s\' now accepting jobs (\'%s\').", name
,
574 * Everything was ok, so return OK status...
577 con
->response
->request
.status
.status_code
= IPP_OK
;
582 * 'add_class()' - Add a class to the system.
586 add_class(client_t
*con
, /* I - Client connection */
587 ipp_attribute_t
*uri
) /* I - URI of class */
589 int i
; /* Looping var */
590 char method
[HTTP_MAX_URI
],
591 /* Method portion of URI */
592 username
[HTTP_MAX_URI
],
593 /* Username portion of URI */
595 /* Host portion of URI */
596 resource
[HTTP_MAX_URI
];
597 /* Resource portion of URI */
598 int port
; /* Port portion of URI */
599 printer_t
*pclass
; /* Class */
600 cups_ptype_t dtype
; /* Destination type */
601 const char *dest
; /* Printer or class name */
602 ipp_attribute_t
*attr
; /* Printer attribute */
603 int modify
; /* Non-zero if we just modified */
606 LogMessage(L_DEBUG2
, "add_class(%d, %s)\n", con
->http
.fd
,
607 uri
->values
[0].string
.text
);
610 * Was this operation called from the correct URI?
613 if (strncmp(con
->uri
, "/admin/", 7) != 0)
615 LogMessage(L_ERROR
, "add_class: admin request on bad resource \'%s\'!",
617 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
621 DEBUG_printf(("add_class(%08x, %08x)\n", con
, uri
));
624 * Do we have a valid URI?
627 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
629 if (strncmp(resource
, "/classes/", 9) != 0 || strlen(resource
) == 9)
632 * No, return an error...
635 send_ipp_error(con
, IPP_BAD_REQUEST
);
640 * See if the class already exists; if not, create a new class...
643 if ((pclass
= FindClass(resource
+ 9)) == NULL
)
646 * Class doesn't exist; see if we have a printer of the same name...
649 if ((pclass
= FindPrinter(resource
+ 9)) != NULL
&&
650 !(pclass
->type
& CUPS_PRINTER_REMOTE
))
653 * Yes, return an error...
656 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
661 * No, add the pclass...
664 pclass
= AddClass(resource
+ 9);
667 else if (pclass
->type
& CUPS_PRINTER_IMPLICIT
)
670 * Rename the implicit class to "AnyClass" or remove it...
673 if (ImplicitAnyClasses
)
675 snprintf(pclass
->name
, sizeof(pclass
->name
), "Any%s", resource
+ 9);
679 DeletePrinter(pclass
, 1);
682 * Add the class as a new local class...
685 pclass
= AddClass(resource
+ 9);
688 else if (pclass
->type
& CUPS_PRINTER_REMOTE
)
691 * Rename the remote class to "Class"...
694 DeletePrinterFilters(pclass
);
695 snprintf(pclass
->name
, sizeof(pclass
->name
), "%s@%s", resource
+ 9,
697 SetPrinterAttrs(pclass
);
701 * Add the class as a new local class...
704 pclass
= AddClass(resource
+ 9);
711 * Look for attributes and copy them over as needed...
714 if ((attr
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
715 SetString(&pclass
->location
, attr
->values
[0].string
.text
);
717 if ((attr
= ippFindAttribute(con
->request
, "printer-info", IPP_TAG_TEXT
)) != NULL
)
718 SetString(&pclass
->info
, attr
->values
[0].string
.text
);
720 if ((attr
= ippFindAttribute(con
->request
, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN
)) != NULL
)
722 LogMessage(L_INFO
, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
723 pclass
->name
, attr
->values
[0].boolean
, pclass
->accepting
);
725 pclass
->accepting
= attr
->values
[0].boolean
;
726 AddPrinterHistory(pclass
);
728 if ((attr
= ippFindAttribute(con
->request
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
730 if (attr
->values
[0].integer
!= IPP_PRINTER_IDLE
&&
731 attr
->values
[0].integer
== IPP_PRINTER_STOPPED
)
733 LogMessage(L_ERROR
, "Attempt to set %s printer-state to bad value %d!",
734 pclass
->name
, attr
->values
[0].integer
);
735 send_ipp_error(con
, IPP_BAD_REQUEST
);
739 LogMessage(L_INFO
, "Setting %s printer-state to %d (was %d.)", pclass
->name
,
740 attr
->values
[0].integer
, pclass
->state
);
742 SetPrinterState(pclass
, attr
->values
[0].integer
, 0);
744 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message", IPP_TAG_TEXT
)) != NULL
)
746 strlcpy(pclass
->state_message
, attr
->values
[0].string
.text
,
747 sizeof(pclass
->state_message
));
748 AddPrinterHistory(pclass
);
750 if ((attr
= ippFindAttribute(con
->request
, "job-sheets-default", IPP_TAG_ZERO
)) != NULL
&&
753 SetString(&pclass
->job_sheets
[0], attr
->values
[0].string
.text
);
754 if (attr
->num_values
> 1)
755 SetString(&pclass
->job_sheets
[1], attr
->values
[1].string
.text
);
757 SetString(&pclass
->job_sheets
[1], "none");
759 if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name-allowed",
760 IPP_TAG_ZERO
)) != NULL
)
762 FreePrinterUsers(pclass
);
764 pclass
->deny_users
= 0;
765 if (attr
->value_tag
== IPP_TAG_NAME
&&
766 (attr
->num_values
> 1 ||
767 strcmp(attr
->values
[0].string
.text
, "all") != 0))
768 for (i
= 0; i
< attr
->num_values
; i
++)
769 AddPrinterUser(pclass
, attr
->values
[i
].string
.text
);
771 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name-denied",
772 IPP_TAG_ZERO
)) != NULL
)
774 FreePrinterUsers(pclass
);
776 pclass
->deny_users
= 1;
777 if (attr
->value_tag
== IPP_TAG_NAME
&&
778 (attr
->num_values
> 1 ||
779 strcmp(attr
->values
[0].string
.text
, "none") != 0))
780 for (i
= 0; i
< attr
->num_values
; i
++)
781 AddPrinterUser(pclass
, attr
->values
[i
].string
.text
);
783 if ((attr
= ippFindAttribute(con
->request
, "job-quota-period",
784 IPP_TAG_INTEGER
)) != NULL
)
786 LogMessage(L_DEBUG
, "add_class: Setting job-quota-period to %d...",
787 attr
->values
[0].integer
);
789 pclass
->quota_period
= attr
->values
[0].integer
;
791 if ((attr
= ippFindAttribute(con
->request
, "job-k-limit",
792 IPP_TAG_INTEGER
)) != NULL
)
794 LogMessage(L_DEBUG
, "add_class: Setting job-k-limit to %d...",
795 attr
->values
[0].integer
);
797 pclass
->k_limit
= attr
->values
[0].integer
;
799 if ((attr
= ippFindAttribute(con
->request
, "job-page-limit",
800 IPP_TAG_INTEGER
)) != NULL
)
802 LogMessage(L_DEBUG
, "add_class: Setting job-page-limit to %d...",
803 attr
->values
[0].integer
);
805 pclass
->page_limit
= attr
->values
[0].integer
;
808 if ((attr
= ippFindAttribute(con
->request
, "member-uris", IPP_TAG_URI
)) != NULL
)
811 * Clear the printer array as needed...
814 if (pclass
->num_printers
> 0)
816 free(pclass
->printers
);
817 pclass
->num_printers
= 0;
821 * Add each printer or class that is listed...
824 for (i
= 0; i
< attr
->num_values
; i
++)
827 * Search for the printer or class URI...
830 httpSeparate(attr
->values
[i
].string
.text
, method
, username
, host
,
833 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
839 LogMessage(L_ERROR
, "add_class: resource name \'%s\' no good!", resource
);
840 send_ipp_error(con
, IPP_NOT_FOUND
);
845 * Add it to the class...
848 if (dtype
& CUPS_PRINTER_CLASS
)
849 AddPrinterToClass(pclass
, FindClass(dest
));
851 AddPrinterToClass(pclass
, FindPrinter(dest
));
856 * Update the printer class attributes and return...
859 SetPrinterAttrs(pclass
);
866 LogMessage(L_INFO
, "Class \'%s\' modified by \'%s\'.", pclass
->name
,
870 AddPrinterHistory(pclass
);
872 LogMessage(L_INFO
, "New class \'%s\' added by \'%s\'.", pclass
->name
,
876 con
->response
->request
.status
.status_code
= IPP_OK
;
881 * 'add_file()' - Add a file to a job.
884 static int /* O - 0 on success, -1 on error */
885 add_file(client_t
*con
, /* I - Connection to client */
886 job_t
*job
, /* I - Job to add to */
887 mime_type_t
*filetype
, /* I - Type of file */
888 int compression
) /* I - Compression */
890 mime_type_t
**filetypes
; /* New filetypes array... */
891 int *compressions
; /* New compressions array... */
894 LogMessage(L_DEBUG2
, "add_file(con=%p[%d], job=%d, filetype=%s/%s, compression=%d)\n",
895 con
, con
->http
.fd
, job
->id
, filetype
->super
, filetype
->type
,
899 * Add the file to the job...
902 if (job
->num_files
== 0)
904 compressions
= (int *)malloc(sizeof(int));
905 filetypes
= (mime_type_t
**)malloc(sizeof(mime_type_t
*));
909 compressions
= (int *)realloc(job
->compressions
,
910 (job
->num_files
+ 1) * sizeof(int));
911 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
912 (job
->num_files
+ 1) *
913 sizeof(mime_type_t
*));
916 if (compressions
== NULL
|| filetypes
== NULL
)
918 CancelJob(job
->id
, 1);
919 LogMessage(L_ERROR
, "add_file: unable to allocate memory for file types!");
920 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
924 job
->compressions
= compressions
;
925 job
->compressions
[job
->num_files
] = compression
;
926 job
->filetypes
= filetypes
;
927 job
->filetypes
[job
->num_files
] = filetype
;
936 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
937 * upon the job and printer state...
941 add_job_state_reasons(client_t
*con
, /* I - Client connection */
942 job_t
*job
) /* I - Job info */
944 printer_t
*dest
; /* Destination printer */
947 LogMessage(L_DEBUG2
, "add_job_state_reasons(%d, %d)\n", con
->http
.fd
,
950 switch (job
->state
->values
[0].integer
)
952 case IPP_JOB_PENDING
:
953 if (job
->dtype
& CUPS_PRINTER_CLASS
)
954 dest
= FindClass(job
->dest
);
956 dest
= FindPrinter(job
->dest
);
958 if (dest
!= NULL
&& dest
->state
== IPP_PRINTER_STOPPED
)
959 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
960 "job-state-reasons", NULL
, "printer-stopped");
962 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
963 "job-state-reasons", NULL
, "none");
967 if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
) != NULL
||
968 ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
) != NULL
)
969 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
970 "job-state-reasons", NULL
, "job-hold-until-specified");
972 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
973 "job-state-reasons", NULL
, "job-incoming");
976 case IPP_JOB_PROCESSING
:
977 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
978 "job-state-reasons", NULL
, "job-printing");
981 case IPP_JOB_STOPPED
:
982 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
983 "job-state-reasons", NULL
, "job-stopped");
986 case IPP_JOB_CANCELLED
:
987 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
988 "job-state-reasons", NULL
, "job-canceled-by-user");
991 case IPP_JOB_ABORTED
:
992 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
993 "job-state-reasons", NULL
, "aborted-by-system");
996 case IPP_JOB_COMPLETED
:
997 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
998 "job-state-reasons", NULL
, "job-completed-successfully");
1005 * 'add_printer()' - Add a printer to the system.
1009 add_printer(client_t
*con
, /* I - Client connection */
1010 ipp_attribute_t
*uri
) /* I - URI of printer */
1012 int i
; /* Looping var */
1013 char method
[HTTP_MAX_URI
],
1014 /* Method portion of URI */
1015 username
[HTTP_MAX_URI
],
1016 /* Username portion of URI */
1018 /* Host portion of URI */
1019 resource
[HTTP_MAX_URI
];
1020 /* Resource portion of URI */
1021 int port
; /* Port portion of URI */
1022 printer_t
*printer
; /* Printer/class */
1023 ipp_attribute_t
*attr
; /* Printer attribute */
1024 cups_file_t
*fp
; /* Script/PPD file */
1025 char line
[1024]; /* Line from file... */
1026 char srcfile
[1024], /* Source Script/PPD file */
1027 dstfile
[1024]; /* Destination Script/PPD file */
1028 int modify
; /* Non-zero if we are modifying */
1031 LogMessage(L_DEBUG2
, "add_printer(%d, %s)\n", con
->http
.fd
,
1032 uri
->values
[0].string
.text
);
1035 * Was this operation called from the correct URI?
1038 if (strncmp(con
->uri
, "/admin/", 7) != 0)
1040 LogMessage(L_ERROR
, "add_printer: admin request on bad resource \'%s\'!",
1042 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1047 * Do we have a valid URI?
1050 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1052 if (strncmp(resource
, "/printers/", 10) != 0 || strlen(resource
) == 10)
1055 * No, return an error...
1058 LogMessage(L_ERROR
, "add_printer: bad printer URI \"%s\"!",
1059 uri
->values
[0].string
.text
);
1060 send_ipp_error(con
, IPP_BAD_REQUEST
);
1065 * See if the printer already exists; if not, create a new printer...
1068 if ((printer
= FindPrinter(resource
+ 10)) == NULL
)
1071 * Printer doesn't exist; see if we have a class of the same name...
1074 if ((printer
= FindClass(resource
+ 10)) != NULL
&&
1075 !(printer
->type
& CUPS_PRINTER_REMOTE
))
1078 * Yes, return an error...
1081 LogMessage(L_ERROR
, "add_printer: \"%s\" already exists as a class!",
1083 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1088 * No, add the printer...
1091 printer
= AddPrinter(resource
+ 10);
1094 else if (printer
->type
& CUPS_PRINTER_IMPLICIT
)
1097 * Rename the implicit printer to "AnyPrinter" or delete it...
1100 if (ImplicitAnyClasses
)
1102 snprintf(printer
->name
, sizeof(printer
->name
), "Any%s", resource
+ 10);
1106 DeletePrinter(printer
, 1);
1109 * Add the printer as a new local printer...
1112 printer
= AddPrinter(resource
+ 10);
1115 else if (printer
->type
& CUPS_PRINTER_REMOTE
)
1118 * Rename the remote printer to "Printer@server"...
1121 DeletePrinterFilters(printer
);
1122 snprintf(printer
->name
, sizeof(printer
->name
), "%s@%s", resource
+ 10,
1124 SetPrinterAttrs(printer
);
1128 * Add the printer as a new local printer...
1131 printer
= AddPrinter(resource
+ 10);
1138 * Look for attributes and copy them over as needed...
1141 if ((attr
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
1142 SetString(&printer
->location
, attr
->values
[0].string
.text
);
1144 if ((attr
= ippFindAttribute(con
->request
, "printer-info", IPP_TAG_TEXT
)) != NULL
)
1145 SetString(&printer
->info
, attr
->values
[0].string
.text
);
1147 if ((attr
= ippFindAttribute(con
->request
, "device-uri", IPP_TAG_URI
)) != NULL
)
1149 ipp_attribute_t
*device
; /* Current device */
1150 int methodlen
; /* Length of method string */
1154 * Do we have a valid device URI?
1157 httpSeparate(attr
->values
[0].string
.text
, method
, username
, host
,
1159 methodlen
= strlen(method
);
1161 if (strcmp(method
, "file") == 0)
1164 * See if the administrator has enabled file devices...
1167 if (!FileDevice
&& strcmp(resource
, "/dev/null"))
1170 * File devices are disabled and the URL is not file:/dev/null...
1173 LogMessage(L_ERROR
, "add_printer: File device URIs have been disabled! "
1174 "To enable, see the FileDevice directive in cupsd.conf.");
1175 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1182 * See if the backend is listed as a device...
1185 for (device
= ippFindAttribute(Devices
, "device-uri", IPP_TAG_URI
);
1187 device
= ippFindNextAttribute(Devices
, "device-uri", IPP_TAG_URI
))
1188 if (strncmp(method
, device
->values
[0].string
.text
, methodlen
) == 0 &&
1189 (device
->values
[0].string
.text
[methodlen
] == ':' ||
1190 device
->values
[0].string
.text
[methodlen
] == '\0'))
1196 * Could not find device in list!
1199 LogMessage(L_ERROR
, "add_printer: bad device-uri attribute \'%s\'!",
1200 attr
->values
[0].string
.text
);
1201 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1206 LogMessage(L_INFO
, "Setting %s device-uri to \"%s\" (was \"%s\".)",
1207 printer
->name
, attr
->values
[0].string
.text
, printer
->device_uri
);
1209 SetString(&printer
->device_uri
, attr
->values
[0].string
.text
);
1212 if ((attr
= ippFindAttribute(con
->request
, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN
)) != NULL
)
1214 LogMessage(L_INFO
, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
1215 printer
->name
, attr
->values
[0].boolean
, printer
->accepting
);
1217 printer
->accepting
= attr
->values
[0].boolean
;
1218 AddPrinterHistory(printer
);
1220 if ((attr
= ippFindAttribute(con
->request
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
1222 if (attr
->values
[0].integer
!= IPP_PRINTER_IDLE
&&
1223 attr
->values
[0].integer
== IPP_PRINTER_STOPPED
)
1225 LogMessage(L_ERROR
, "Attempt to set %s printer-state to bad value %d!",
1226 printer
->name
, attr
->values
[0].integer
);
1227 send_ipp_error(con
, IPP_BAD_REQUEST
);
1231 LogMessage(L_INFO
, "Setting %s printer-state to %d (was %d.)", printer
->name
,
1232 attr
->values
[0].integer
, printer
->state
);
1234 SetPrinterState(printer
, attr
->values
[0].integer
, 0);
1236 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message", IPP_TAG_TEXT
)) != NULL
)
1238 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
1239 sizeof(printer
->state_message
));
1240 AddPrinterHistory(printer
);
1242 if ((attr
= ippFindAttribute(con
->request
, "job-sheets-default", IPP_TAG_ZERO
)) != NULL
&&
1245 SetString(&printer
->job_sheets
[0], attr
->values
[0].string
.text
);
1246 if (attr
->num_values
> 1)
1247 SetString(&printer
->job_sheets
[1], attr
->values
[1].string
.text
);
1249 SetString(&printer
->job_sheets
[1], "none");
1251 if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name-allowed",
1252 IPP_TAG_ZERO
)) != NULL
)
1254 FreePrinterUsers(printer
);
1256 printer
->deny_users
= 0;
1257 if (attr
->value_tag
== IPP_TAG_NAME
&&
1258 (attr
->num_values
> 1 ||
1259 strcmp(attr
->values
[0].string
.text
, "all") != 0))
1260 for (i
= 0; i
< attr
->num_values
; i
++)
1261 AddPrinterUser(printer
, attr
->values
[i
].string
.text
);
1263 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name-denied",
1264 IPP_TAG_ZERO
)) != NULL
)
1266 FreePrinterUsers(printer
);
1268 printer
->deny_users
= 1;
1269 if (attr
->value_tag
== IPP_TAG_NAME
&&
1270 (attr
->num_values
> 1 ||
1271 strcmp(attr
->values
[0].string
.text
, "none") != 0))
1272 for (i
= 0; i
< attr
->num_values
; i
++)
1273 AddPrinterUser(printer
, attr
->values
[i
].string
.text
);
1275 if ((attr
= ippFindAttribute(con
->request
, "job-quota-period",
1276 IPP_TAG_INTEGER
)) != NULL
)
1278 LogMessage(L_DEBUG
, "add_printer: Setting job-quota-period to %d...",
1279 attr
->values
[0].integer
);
1280 FreeQuotas(printer
);
1281 printer
->quota_period
= attr
->values
[0].integer
;
1283 if ((attr
= ippFindAttribute(con
->request
, "job-k-limit",
1284 IPP_TAG_INTEGER
)) != NULL
)
1286 LogMessage(L_DEBUG
, "add_printer: Setting job-k-limit to %d...",
1287 attr
->values
[0].integer
);
1288 FreeQuotas(printer
);
1289 printer
->k_limit
= attr
->values
[0].integer
;
1291 if ((attr
= ippFindAttribute(con
->request
, "job-page-limit",
1292 IPP_TAG_INTEGER
)) != NULL
)
1294 LogMessage(L_DEBUG
, "add_printer: Setting job-page-limit to %d...",
1295 attr
->values
[0].integer
);
1296 FreeQuotas(printer
);
1297 printer
->page_limit
= attr
->values
[0].integer
;
1301 * See if we have all required attributes...
1304 if (!printer
->device_uri
)
1305 SetString(&printer
->device_uri
, "file:/dev/null");
1308 * See if we have an interface script or PPD file attached to the request...
1313 strlcpy(srcfile
, con
->filename
, sizeof(srcfile
));
1315 if ((fp
= cupsFileOpen(srcfile
, "rb")) != NULL
)
1318 * Yes; get the first line from it...
1322 cupsFileGets(fp
, line
, sizeof(line
));
1326 * Then see what kind of file it is...
1329 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
1332 if (strncmp(line
, "*PPD-Adobe", 10) == 0)
1335 * The new file is a PPD file, so remove any old interface script
1336 * that might be lying around...
1344 * This must be an interface script, so move the file over to the
1345 * interfaces directory and make it executable...
1348 if (copy_file(srcfile
, dstfile
))
1350 LogMessage(L_ERROR
, "add_printer: Unable to copy interface script from %s to %s - %s!",
1351 srcfile
, dstfile
, strerror(errno
));
1352 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
1357 LogMessage(L_DEBUG
, "add_printer: Copied interface script successfully!");
1358 chmod(dstfile
, 0755);
1362 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
1365 if (strncmp(line
, "*PPD-Adobe", 10) == 0)
1368 * The new file is a PPD file, so move the file over to the
1369 * ppd directory and make it readable by all...
1372 if (copy_file(srcfile
, dstfile
))
1374 LogMessage(L_ERROR
, "add_printer: Unable to copy PPD file from %s to %s - %s!",
1375 srcfile
, dstfile
, strerror(errno
));
1376 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
1381 LogMessage(L_DEBUG
, "add_printer: Copied PPD file successfully!");
1382 chmod(dstfile
, 0644);
1388 * This must be an interface script, so remove any old PPD file that
1389 * may be lying around...
1396 else if ((attr
= ippFindAttribute(con
->request
, "ppd-name", IPP_TAG_NAME
)) != NULL
)
1398 if (strcmp(attr
->values
[0].string
.text
, "raw") == 0)
1401 * Raw driver, remove any existing PPD or interface script files.
1404 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
1408 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
1418 snprintf(srcfile
, sizeof(srcfile
), "%s/model/%s", DataDir
,
1419 attr
->values
[0].string
.text
);
1421 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
1425 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
1428 if (copy_model(srcfile
, dstfile
))
1430 LogMessage(L_ERROR
, "add_printer: Unable to copy PPD file from %s to %s - %s!",
1431 srcfile
, dstfile
, strerror(errno
));
1432 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
1437 LogMessage(L_DEBUG
, "add_printer: Copied PPD file successfully!");
1438 chmod(dstfile
, 0644);
1444 * Make this printer the default if there is none...
1447 if (DefaultPrinter
== NULL
)
1448 DefaultPrinter
= printer
;
1451 * Update the printer attributes and return...
1454 SetPrinterAttrs(printer
);
1457 if (printer
->job
!= NULL
)
1462 * Stop the current job and then restart it below...
1465 job
= (job_t
*)printer
->job
;
1467 StopJob(job
->id
, 1);
1468 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1476 LogMessage(L_INFO
, "Printer \'%s\' modified by \'%s\'.", printer
->name
,
1480 AddPrinterHistory(printer
);
1482 LogMessage(L_INFO
, "New printer \'%s\' added by \'%s\'.", printer
->name
,
1486 con
->response
->request
.status
.status_code
= IPP_OK
;
1491 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
1492 * based upon the printer state...
1496 add_printer_state_reasons(client_t
*con
, /* I - Client connection */
1497 printer_t
*p
) /* I - Printer info */
1499 LogMessage(L_DEBUG2
, "add_printer_state_reasons(%d, %s)\n", con
->http
.fd
,
1502 if (p
->num_reasons
== 0)
1503 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1504 "printer-state-reasons", NULL
,
1505 p
->state
== IPP_PRINTER_STOPPED
? "paused" : "none");
1507 ippAddStrings(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1508 "printer-state-reasons", p
->num_reasons
, NULL
,
1509 (const char * const *)p
->reasons
);
1514 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
1515 * the specified printer or class.
1519 add_queued_job_count(client_t
*con
, /* I - Client connection */
1520 printer_t
*p
) /* I - Printer or class */
1522 int count
; /* Number of jobs on destination */
1525 LogMessage(L_DEBUG2
, "add_queued_job_count(%d, %s)\n", con
->http
.fd
,
1528 count
= GetPrinterJobCount(p
->name
);
1530 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1531 "queued-job-count", count
);
1536 * 'cancel_all_jobs()' - Cancel all print jobs.
1540 cancel_all_jobs(client_t
*con
, /* I - Client connection */
1541 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
1543 const char *dest
; /* Destination */
1544 cups_ptype_t dtype
; /* Destination type */
1545 char method
[HTTP_MAX_URI
],
1546 /* Method portion of URI */
1547 userpass
[HTTP_MAX_URI
],
1548 /* Username portion of URI */
1550 /* Host portion of URI */
1551 resource
[HTTP_MAX_URI
];
1552 /* Resource portion of URI */
1553 int port
; /* Port portion of URI */
1554 ipp_attribute_t
*attr
; /* Attribute in request */
1555 const char *username
; /* Username */
1556 int purge
; /* Purge? */
1559 LogMessage(L_DEBUG2
, "cancel_all_jobs(%d, %s)\n", con
->http
.fd
,
1560 uri
->values
[0].string
.text
);
1563 * Was this operation called from the correct URI?
1566 if (strncmp(con
->uri
, "/admin/", 7) != 0)
1568 LogMessage(L_ERROR
, "cancel_all_jobs: admin request on bad resource \'%s\'!",
1570 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1575 * See if we have a printer URI...
1578 if (strcmp(uri
->name
, "printer-uri") != 0)
1580 LogMessage(L_ERROR
, "cancel_all_jobs: bad %s attribute \'%s\'!",
1581 uri
->name
, uri
->values
[0].string
.text
);
1582 send_ipp_error(con
, IPP_BAD_REQUEST
);
1587 * Get the username (if any) for the jobs we want to cancel (only if
1588 * "my-jobs" is specified...
1591 if ((attr
= ippFindAttribute(con
->request
, "my-jobs", IPP_TAG_BOOLEAN
)) != NULL
&&
1592 attr
->values
[0].boolean
)
1594 if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1595 username
= attr
->values
[0].string
.text
;
1598 LogMessage(L_ERROR
, "cancel_all_jobs: missing requesting-user-name attribute!");
1599 send_ipp_error(con
, IPP_BAD_REQUEST
);
1607 * Look for the "purge-jobs" attribute...
1610 if ((attr
= ippFindAttribute(con
->request
, "purge-jobs", IPP_TAG_BOOLEAN
)) != NULL
)
1611 purge
= attr
->values
[0].boolean
;
1616 * And if the destination is valid...
1619 httpSeparate(uri
->values
[0].string
.text
, method
, userpass
, host
, &port
,
1622 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
1628 if (strcmp(resource
, "/printers/") != 0)
1630 LogMessage(L_ERROR
, "cancel_all_jobs: resource name \'%s\' no good!", resource
);
1631 send_ipp_error(con
, IPP_NOT_FOUND
);
1636 * Cancel all jobs on all printers...
1639 CancelJobs(NULL
, username
, purge
);
1641 LogMessage(L_INFO
, "All jobs were %s by \'%s\'.",
1642 purge
? "purged" : "cancelled", con
->username
);
1647 * Cancel all of the jobs on the named printer...
1650 CancelJobs(dest
, username
, purge
);
1652 LogMessage(L_INFO
, "All jobs on \'%s\' were %s by \'%s\'.", dest
,
1653 purge
? "purged" : "cancelled", con
->username
);
1656 con
->response
->request
.status
.status_code
= IPP_OK
;
1661 * 'cancel_job()' - Cancel a print job.
1665 cancel_job(client_t
*con
, /* I - Client connection */
1666 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
1668 ipp_attribute_t
*attr
; /* Current attribute */
1669 int jobid
; /* Job ID */
1670 char method
[HTTP_MAX_URI
],
1671 /* Method portion of URI */
1672 username
[HTTP_MAX_URI
],
1673 /* Username portion of URI */
1675 /* Host portion of URI */
1676 resource
[HTTP_MAX_URI
];
1677 /* Resource portion of URI */
1678 int port
; /* Port portion of URI */
1679 job_t
*job
; /* Job information */
1680 const char *dest
; /* Destination */
1681 cups_ptype_t dtype
; /* Destination type (printer or class) */
1682 printer_t
*printer
; /* Printer data */
1685 LogMessage(L_DEBUG2
, "cancel_job(%d, %s)\n", con
->http
.fd
,
1686 uri
->values
[0].string
.text
);
1689 * Verify that the POST operation was done to a valid URI.
1692 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
1693 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
1694 strncmp(con
->uri
, "/printers/", 10) != 0)
1696 LogMessage(L_ERROR
, "cancel_job: cancel request on bad resource \'%s\'!",
1698 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1703 * See if we have a job URI or a printer URI...
1706 if (strcmp(uri
->name
, "printer-uri") == 0)
1709 * Got a printer URI; see if we also have a job-id attribute...
1712 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
1714 LogMessage(L_ERROR
, "cancel_job: got a printer-uri attribute but no job-id!");
1715 send_ipp_error(con
, IPP_BAD_REQUEST
);
1719 if ((jobid
= attr
->values
[0].integer
) == 0)
1722 * Find the current job on the specified printer...
1725 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1727 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
1733 LogMessage(L_ERROR
, "cancel_job: resource name \'%s\' no good!", resource
);
1734 send_ipp_error(con
, IPP_NOT_FOUND
);
1738 if (dtype
& CUPS_PRINTER_CLASS
)
1739 printer
= FindClass(dest
);
1741 printer
= FindPrinter(dest
);
1744 * See if the printer is currently printing a job...
1748 jobid
= ((job_t
*)printer
->job
)->id
;
1752 * No, see if there are any pending jobs...
1755 for (job
= Jobs
; job
!= NULL
; job
= job
->next
)
1756 if (job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
&&
1757 strcasecmp(job
->dest
, dest
) == 0)
1764 LogMessage(L_ERROR
, "cancel_job: No active jobs on %s!", dest
);
1765 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1774 * Got a job URI; parse it to get the job ID...
1777 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1779 if (strncmp(resource
, "/jobs/", 6) != 0)
1785 LogMessage(L_ERROR
, "cancel_job: bad job-uri attribute \'%s\'!",
1786 uri
->values
[0].string
.text
);
1787 send_ipp_error(con
, IPP_BAD_REQUEST
);
1791 jobid
= atoi(resource
+ 6);
1795 * See if the job exists...
1798 if ((job
= FindJob(jobid
)) == NULL
)
1801 * Nope - return a "not found" error...
1804 LogMessage(L_ERROR
, "cancel_job: job #%d doesn't exist!", jobid
);
1805 send_ipp_error(con
, IPP_NOT_FOUND
);
1810 * See if the job is owned by the requesting user...
1813 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
1815 LogMessage(L_ERROR
, "cancel_job: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
1816 username
, jobid
, job
->username
);
1817 send_ipp_error(con
, IPP_FORBIDDEN
);
1822 * See if the job is already completed, cancelled, or aborted; if so,
1823 * we can't cancel...
1826 if (job
->state
->values
[0].integer
>= IPP_JOB_CANCELLED
)
1828 LogMessage(L_ERROR
, "cancel_job: job id %d is %s - can't cancel!",
1830 job
->state
->values
[0].integer
== IPP_JOB_CANCELLED
? "cancelled" :
1831 job
->state
->values
[0].integer
== IPP_JOB_ABORTED
? "aborted" :
1833 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
1838 * Cancel the job and return...
1841 CancelJob(jobid
, 0);
1844 LogMessage(L_INFO
, "Job %d was cancelled by \'%s\'.", jobid
, username
);
1846 con
->response
->request
.status
.status_code
= IPP_OK
;
1851 * 'check_quotas()' - Check quotas for a printer and user.
1854 static int /* O - 1 if OK, 0 if not */
1855 check_quotas(client_t
*con
, /* I - Client connection */
1856 printer_t
*p
) /* I - Printer or class */
1858 int i
, j
; /* Looping vars */
1859 ipp_attribute_t
*attr
; /* Current attribute */
1860 char username
[33]; /* Username */
1861 quota_t
*q
; /* Quota data */
1862 struct passwd
*pw
; /* User password data */
1863 struct group
*grp
; /* Group data */
1866 LogMessage(L_DEBUG2
, "check_quotas(%d, %s)\n", con
->http
.fd
, p
->name
);
1872 if (con
== NULL
|| p
== NULL
)
1876 * Figure out who is printing...
1879 attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
);
1881 if (con
->username
[0])
1882 strlcpy(username
, con
->username
, sizeof(username
));
1883 else if (attr
!= NULL
)
1885 LogMessage(L_DEBUG
, "check_quotas: requesting-user-name = \'%s\'",
1886 attr
->values
[0].string
.text
);
1888 strlcpy(username
, attr
->values
[0].string
.text
, sizeof(username
));
1891 strcpy(username
, "anonymous");
1894 * Check global active job limits for printers and users...
1897 if (MaxJobsPerPrinter
)
1900 * Check if there are too many pending jobs on this printer...
1903 if (GetPrinterJobCount(p
->name
) >= MaxJobsPerPrinter
)
1905 LogMessage(L_INFO
, "Too many jobs for printer \"%s\"...", p
->name
);
1913 * Check if there are too many pending jobs for this user...
1916 if (GetUserJobCount(username
) >= MaxJobsPerUser
)
1918 LogMessage(L_INFO
, "Too many jobs for user \"%s\"...", username
);
1924 * Check against users...
1927 if (p
->num_users
== 0 && p
->k_limit
== 0 && p
->page_limit
== 0)
1932 pw
= getpwnam(username
);
1935 for (i
= 0; i
< p
->num_users
; i
++)
1936 if (p
->users
[i
][0] == '@')
1939 * Check group membership...
1942 grp
= getgrnam(p
->users
[i
] + 1);
1948 * Check primary group...
1951 if (pw
&& grp
->gr_gid
== pw
->pw_gid
)
1955 * Check usernames in group...
1958 for (j
= 0; grp
->gr_mem
[j
]; j
++)
1959 if (!strcmp(username
, grp
->gr_mem
[j
]))
1966 else if (!strcasecmp(username
, p
->users
[i
]))
1969 if ((i
< p
->num_users
) == p
->deny_users
)
1971 LogMessage(L_INFO
, "Denying user \"%s\" access to printer \"%s\"...",
1981 if (p
->k_limit
|| p
->page_limit
)
1983 if ((q
= UpdateQuota(p
, username
, 0, 0)) == NULL
)
1985 LogMessage(L_ERROR
, "Unable to allocate quota data for user \"%s\"!",
1990 if ((q
->k_count
>= p
->k_limit
&& p
->k_limit
) ||
1991 (q
->page_count
>= p
->page_limit
&& p
->page_limit
))
1993 LogMessage(L_INFO
, "User \"%s\" is over the quota limit...",
2000 * If we have gotten this far, we're done!
2008 * 'copy_attribute()' - Copy a single attribute.
2011 static ipp_attribute_t
* /* O - New attribute */
2012 copy_attribute(ipp_t
*to
, /* O - Destination request/response */
2013 ipp_attribute_t
*attr
, /* I - Attribute to copy */
2014 int quickcopy
)/* I - Do a quick copy? */
2016 int i
; /* Looping var */
2017 ipp_attribute_t
*toattr
; /* Destination attribute */
2020 LogMessage(L_DEBUG2
, "copy_attribute(%p, %s)\n", to
,
2021 attr
->name
? attr
->name
: "(null)");
2023 switch (attr
->value_tag
& ~IPP_TAG_COPY
)
2026 toattr
= ippAddSeparator(to
);
2029 case IPP_TAG_INTEGER
:
2031 toattr
= ippAddIntegers(to
, attr
->group_tag
, attr
->value_tag
,
2032 attr
->name
, attr
->num_values
, NULL
);
2034 for (i
= 0; i
< attr
->num_values
; i
++)
2035 toattr
->values
[i
].integer
= attr
->values
[i
].integer
;
2038 case IPP_TAG_BOOLEAN
:
2039 toattr
= ippAddBooleans(to
, attr
->group_tag
, attr
->name
,
2040 attr
->num_values
, NULL
);
2042 for (i
= 0; i
< attr
->num_values
; i
++)
2043 toattr
->values
[i
].boolean
= attr
->values
[i
].boolean
;
2046 case IPP_TAG_STRING
:
2049 case IPP_TAG_KEYWORD
:
2051 case IPP_TAG_URISCHEME
:
2052 case IPP_TAG_CHARSET
:
2053 case IPP_TAG_LANGUAGE
:
2054 case IPP_TAG_MIMETYPE
:
2055 toattr
= ippAddStrings(to
, attr
->group_tag
,
2056 (ipp_tag_t
)(attr
->value_tag
| quickcopy
),
2057 attr
->name
, attr
->num_values
, NULL
, NULL
);
2061 for (i
= 0; i
< attr
->num_values
; i
++)
2062 toattr
->values
[i
].string
.text
= attr
->values
[i
].string
.text
;
2066 for (i
= 0; i
< attr
->num_values
; i
++)
2067 toattr
->values
[i
].string
.text
= strdup(attr
->values
[i
].string
.text
);
2072 toattr
= ippAddDate(to
, attr
->group_tag
, attr
->name
,
2073 attr
->values
[0].date
);
2076 case IPP_TAG_RESOLUTION
:
2077 toattr
= ippAddResolutions(to
, attr
->group_tag
, attr
->name
,
2078 attr
->num_values
, IPP_RES_PER_INCH
,
2081 for (i
= 0; i
< attr
->num_values
; i
++)
2083 toattr
->values
[i
].resolution
.xres
= attr
->values
[i
].resolution
.xres
;
2084 toattr
->values
[i
].resolution
.yres
= attr
->values
[i
].resolution
.yres
;
2085 toattr
->values
[i
].resolution
.units
= attr
->values
[i
].resolution
.units
;
2089 case IPP_TAG_RANGE
:
2090 toattr
= ippAddRanges(to
, attr
->group_tag
, attr
->name
,
2091 attr
->num_values
, NULL
, NULL
);
2093 for (i
= 0; i
< attr
->num_values
; i
++)
2095 toattr
->values
[i
].range
.lower
= attr
->values
[i
].range
.lower
;
2096 toattr
->values
[i
].range
.upper
= attr
->values
[i
].range
.upper
;
2100 case IPP_TAG_TEXTLANG
:
2101 case IPP_TAG_NAMELANG
:
2102 toattr
= ippAddStrings(to
, attr
->group_tag
,
2103 (ipp_tag_t
)(attr
->value_tag
| quickcopy
),
2104 attr
->name
, attr
->num_values
, NULL
, NULL
);
2108 for (i
= 0; i
< attr
->num_values
; i
++)
2110 toattr
->values
[i
].string
.charset
= attr
->values
[i
].string
.charset
;
2111 toattr
->values
[i
].string
.text
= attr
->values
[i
].string
.text
;
2116 for (i
= 0; i
< attr
->num_values
; i
++)
2119 toattr
->values
[i
].string
.charset
=
2120 strdup(attr
->values
[i
].string
.charset
);
2122 toattr
->values
[i
].string
.charset
=
2123 toattr
->values
[0].string
.charset
;
2125 toattr
->values
[i
].string
.text
= strdup(attr
->values
[i
].string
.text
);
2130 case IPP_TAG_BEGIN_COLLECTION
:
2131 toattr
= ippAddCollections(to
, attr
->group_tag
, attr
->name
,
2132 attr
->num_values
, NULL
);
2134 for (i
= 0; i
< attr
->num_values
; i
++)
2136 toattr
->values
[i
].collection
= ippNew();
2137 copy_attrs(toattr
->values
[i
].collection
, attr
->values
[i
].collection
,
2138 NULL
, IPP_TAG_ZERO
, 0);
2143 toattr
= ippAddIntegers(to
, attr
->group_tag
, attr
->value_tag
,
2144 attr
->name
, attr
->num_values
, NULL
);
2146 for (i
= 0; i
< attr
->num_values
; i
++)
2148 toattr
->values
[i
].unknown
.length
= attr
->values
[i
].unknown
.length
;
2150 if (toattr
->values
[i
].unknown
.length
> 0)
2152 if ((toattr
->values
[i
].unknown
.data
= malloc(toattr
->values
[i
].unknown
.length
)) == NULL
)
2153 toattr
->values
[i
].unknown
.length
= 0;
2155 memcpy(toattr
->values
[i
].unknown
.data
,
2156 attr
->values
[i
].unknown
.data
,
2157 toattr
->values
[i
].unknown
.length
);
2160 break; /* anti-compiler-warning-code */
2168 * 'copy_attrs()' - Copy attributes from one request to another.
2172 copy_attrs(ipp_t
*to
, /* I - Destination request */
2173 ipp_t
*from
, /* I - Source request */
2174 ipp_attribute_t
*req
, /* I - Requested attributes */
2175 ipp_tag_t group
, /* I - Group to copy */
2176 int quickcopy
) /* I - Do a quick copy? */
2178 int i
; /* Looping var */
2179 ipp_attribute_t
*fromattr
; /* Source attribute */
2182 LogMessage(L_DEBUG2
, "copy_attrs(%p, %p, %p, %x)\n", to
, from
, req
, group
);
2184 if (to
== NULL
|| from
== NULL
)
2187 if (req
!= NULL
&& strcmp(req
->values
[0].string
.text
, "all") == 0)
2188 req
= NULL
; /* "all" means no filter... */
2190 for (fromattr
= from
->attrs
; fromattr
!= NULL
; fromattr
= fromattr
->next
)
2193 * Filter attributes as needed...
2196 if (group
!= IPP_TAG_ZERO
&& fromattr
->group_tag
!= group
&&
2197 fromattr
->group_tag
!= IPP_TAG_ZERO
)
2200 if (req
!= NULL
&& fromattr
->name
!= NULL
)
2202 for (i
= 0; i
< req
->num_values
; i
++)
2203 if (strcmp(fromattr
->name
, req
->values
[i
].string
.text
) == 0)
2206 if (i
== req
->num_values
)
2210 copy_attribute(to
, fromattr
, quickcopy
);
2216 * 'copy_banner()' - Copy a banner file to the requests directory for the
2220 static int /* O - Size of banner file in kbytes */
2221 copy_banner(client_t
*con
, /* I - Client connection */
2222 job_t
*job
, /* I - Job information */
2223 const char *name
) /* I - Name of banner */
2225 int i
; /* Looping var */
2226 int kbytes
; /* Size of banner file in kbytes */
2227 char filename
[1024]; /* Job filename */
2228 banner_t
*banner
; /* Pointer to banner */
2229 cups_file_t
*in
; /* Input file */
2230 cups_file_t
*out
; /* Output file */
2231 int ch
; /* Character from file */
2232 char attrname
[255], /* Name of attribute */
2233 *s
; /* Pointer into name */
2234 ipp_attribute_t
*attr
; /* Attribute */
2237 LogMessage(L_DEBUG2
, "copy_banner(%d, %d, %s)\n", con
->http
.fd
, job
->id
,
2241 * Find the banner; return if not found or "none"...
2244 LogMessage(L_DEBUG
, "copy_banner(%p, %d, \"%s\")", con
, job
->id
,
2245 name
? name
: "(null)");
2248 strcmp(name
, "none") == 0 ||
2249 (banner
= FindBanner(name
)) == NULL
)
2253 * Open the banner and job files...
2256 if (add_file(con
, job
, banner
->filetype
, 0))
2259 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
2261 if ((out
= cupsFileOpen(filename
, "w")) == NULL
)
2263 LogMessage(L_ERROR
, "copy_banner: Unable to create banner job file %s - %s",
2264 filename
, strerror(errno
));
2269 fchmod(cupsFileNumber(out
), 0640);
2270 fchown(cupsFileNumber(out
), User
, Group
);
2275 * Try the localized banner file under the subdirectory...
2278 snprintf(filename
, sizeof(filename
), "%s/banners/%s/%s", DataDir
,
2279 con
->language
->language
, name
);
2281 if (access(filename
, 0) && con
->language
->language
[2])
2284 * Wasn't able to find "ll_CC" locale file; try the non-national
2285 * localization banner directory.
2288 attrname
[0] = con
->language
->language
[0];
2289 attrname
[1] = con
->language
->language
[1];
2292 snprintf(filename
, sizeof(filename
), "%s/banners/%s/%s", DataDir
,
2296 if (access(filename
, 0))
2299 * Use the non-localized banner file.
2302 snprintf(filename
, sizeof(filename
), "%s/banners/%s", DataDir
, name
);
2308 * Use the non-localized banner file.
2311 snprintf(filename
, sizeof(filename
), "%s/banners/%s", DataDir
, name
);
2314 if ((in
= cupsFileOpen(filename
, "r")) == NULL
)
2318 LogMessage(L_ERROR
, "copy_banner: Unable to open banner template file %s - %s",
2319 filename
, strerror(errno
));
2325 * Parse the file to the end...
2328 while ((ch
= cupsFileGetChar(in
)) != EOF
)
2332 * Get an attribute name...
2335 for (s
= attrname
; (ch
= cupsFileGetChar(in
)) != EOF
;)
2336 if (!isalpha(ch
) && ch
!= '-' && ch
!= '?')
2338 else if (s
< (attrname
+ sizeof(attrname
) - 1))
2348 * Ignore { followed by stuff that is not an attribute name...
2351 cupsFilePrintf(out
, "{%s%c", attrname
, ch
);
2356 * See if it is defined...
2359 if (attrname
[0] == '?')
2364 if (strcmp(s
, "printer-name") == 0)
2366 cupsFilePuts(out
, job
->dest
);
2369 else if ((attr
= ippFindAttribute(job
->attrs
, s
, IPP_TAG_ZERO
)) == NULL
)
2372 * See if we have a leading question mark...
2375 if (attrname
[0] != '?')
2378 * Nope, write to file as-is; probably a PostScript procedure...
2381 cupsFilePrintf(out
, "{%s}", attrname
);
2388 * Output value(s)...
2391 for (i
= 0; i
< attr
->num_values
; i
++)
2394 cupsFilePutChar(out
, ',');
2396 switch (attr
->value_tag
)
2398 case IPP_TAG_INTEGER
:
2400 if (strncmp(s
, "time-at-", 8) == 0)
2401 cupsFilePuts(out
, GetDateTime(attr
->values
[i
].integer
));
2403 cupsFilePrintf(out
, "%d", attr
->values
[i
].integer
);
2406 case IPP_TAG_BOOLEAN
:
2407 cupsFilePrintf(out
, "%d", attr
->values
[i
].boolean
);
2410 case IPP_TAG_NOVALUE
:
2411 cupsFilePuts(out
, "novalue");
2414 case IPP_TAG_RANGE
:
2415 cupsFilePrintf(out
, "%d-%d", attr
->values
[i
].range
.lower
,
2416 attr
->values
[i
].range
.upper
);
2419 case IPP_TAG_RESOLUTION
:
2420 cupsFilePrintf(out
, "%dx%d%s", attr
->values
[i
].resolution
.xres
,
2421 attr
->values
[i
].resolution
.yres
,
2422 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
2427 case IPP_TAG_STRING
:
2430 case IPP_TAG_KEYWORD
:
2431 case IPP_TAG_CHARSET
:
2432 case IPP_TAG_LANGUAGE
:
2433 if (strcasecmp(banner
->filetype
->type
, "postscript") == 0)
2436 * Need to quote strings for PS banners...
2441 for (p
= attr
->values
[i
].string
.text
; *p
; p
++)
2443 if (*p
== '(' || *p
== ')' || *p
== '\\')
2445 cupsFilePutChar(out
, '\\');
2446 cupsFilePutChar(out
, *p
);
2448 else if (*p
< 32 || *p
> 126)
2449 cupsFilePrintf(out
, "\\%03o", *p
);
2451 cupsFilePutChar(out
, *p
);
2455 cupsFilePuts(out
, attr
->values
[i
].string
.text
);
2459 break; /* anti-compiler-warning-code */
2463 else if (ch
== '\\') /* Quoted char */
2465 ch
= cupsFileGetChar(in
);
2467 if (ch
!= '{') /* Only do special handling for \{ */
2468 cupsFilePutChar(out
, '\\');
2470 cupsFilePutChar(out
, ch
);
2473 cupsFilePutChar(out
, ch
);
2477 kbytes
= (cupsFileTell(out
) + 1023) / 1024;
2479 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
2480 attr
->values
[0].integer
+= kbytes
;
2489 * 'copy_file()' - Copy a PPD file or interface script...
2492 static int /* O - 0 = success, -1 = error */
2493 copy_file(const char *from
, /* I - Source file */
2494 const char *to
) /* I - Destination file */
2496 cups_file_t
*src
, /* Source file */
2497 *dst
; /* Destination file */
2498 int bytes
; /* Bytes to read/write */
2499 char buffer
[2048]; /* Copy buffer */
2502 LogMessage(L_DEBUG2
, "copy_file(\"%s\", \"%s\")\n", from
, to
);
2505 * Open the source and destination file for a copy...
2508 if ((src
= cupsFileOpen(from
, "rb")) == NULL
)
2511 if ((dst
= cupsFileOpen(to
, "wb")) == NULL
)
2518 * Copy the source file to the destination...
2521 while ((bytes
= cupsFileRead(src
, buffer
, sizeof(buffer
))) > 0)
2522 if (cupsFileWrite(dst
, buffer
, bytes
) < bytes
)
2530 * Close both files and return...
2535 return (cupsFileClose(dst
));
2540 * 'copy_model()' - Copy a PPD model file, substituting default values
2544 static int /* O - 0 = success, -1 = error */
2545 copy_model(const char *from
, /* I - Source file */
2546 const char *to
) /* I - Destination file */
2548 cups_file_t
*src
, /* Source file */
2549 *dst
; /* Destination file */
2550 char buffer
[2048]; /* Copy buffer */
2551 int i
; /* Looping var */
2552 char option
[PPD_MAX_NAME
], /* Option name */
2553 choice
[PPD_MAX_NAME
]; /* Choice name */
2554 int num_defaults
; /* Number of default options */
2555 ppd_default_t
*defaults
; /* Default options */
2556 char cups_protocol
[PPD_MAX_LINE
];
2557 /* cupsProtocol attribute */
2558 #ifdef HAVE_LIBPAPER
2559 char *paper_result
; /* Paper size name from libpaper */
2560 char system_paper
[64]; /* Paper size name buffer */
2561 #endif /* HAVE_LIBPAPER */
2564 LogMessage(L_DEBUG2
, "copy_model(\"%s\", \"%s\")\n", from
, to
);
2567 * Open the destination (if possible) and set the default options...
2572 cups_protocol
[0] = '\0';
2574 if ((dst
= cupsFileOpen(to
, "rb")) != NULL
)
2577 * Read all of the default lines from the old PPD...
2580 while (cupsFileGets(dst
, buffer
, sizeof(buffer
)) != NULL
)
2581 if (!strncmp(buffer
, "*Default", 8))
2584 * Add the default option...
2587 if (!ppd_parse_line(buffer
, option
, sizeof(option
),
2588 choice
, sizeof(choice
)))
2589 num_defaults
= ppd_add_default(option
, choice
, num_defaults
,
2592 else if (!strncmp(buffer
, "*cupsProtocol:", 14))
2593 strlcpy(cups_protocol
, buffer
, sizeof(cups_protocol
));
2597 #ifdef HAVE_LIBPAPER
2598 else if ((paper_result
= systempapername()) != NULL
)
2601 * Set the default media sizes from the systemwide default...
2604 strlcpy(system_paper
, paper_result
, sizeof(system_paper
));
2605 system_paper
[0] = toupper(system_paper
[0]);
2607 num_defaults
= ppd_add_default("PageSize", system_paper
,
2608 num_defaults
, &defaults
);
2609 num_defaults
= ppd_add_default("PageRegion", system_paper
,
2610 num_defaults
, &defaults
);
2611 num_defaults
= ppd_add_default("PaperDimension", system_paper
,
2612 num_defaults
, &defaults
);
2613 num_defaults
= ppd_add_default("ImageableArea", system_paper
,
2614 num_defaults
, &defaults
);
2616 #endif /* HAVE_LIBPAPER */
2620 * Add the default media sizes...
2622 * Note: These values are generally not valid for large-format devices
2623 * like plotters, however it is probably safe to say that those
2624 * users will configure the media size after initially adding
2625 * the device anyways...
2628 if (!DefaultLanguage
||
2629 !strcasecmp(DefaultLanguage
, "C") ||
2630 !strcasecmp(DefaultLanguage
, "POSIX") ||
2631 !strcasecmp(DefaultLanguage
, "en") ||
2632 !strncasecmp(DefaultLanguage
, "en_US", 5) ||
2633 !strncasecmp(DefaultLanguage
, "en_CA", 5) ||
2634 !strncasecmp(DefaultLanguage
, "fr_CA", 5))
2637 * These are the only locales that will default to "letter" size...
2640 num_defaults
= ppd_add_default("PageSize", "Letter", num_defaults
,
2642 num_defaults
= ppd_add_default("PageRegion", "Letter", num_defaults
,
2644 num_defaults
= ppd_add_default("PaperDimension", "Letter", num_defaults
,
2646 num_defaults
= ppd_add_default("ImageableArea", "Letter", num_defaults
,
2652 * The rest default to "a4" size...
2655 num_defaults
= ppd_add_default("PageSize", "A4", num_defaults
,
2657 num_defaults
= ppd_add_default("PageRegion", "A4", num_defaults
,
2659 num_defaults
= ppd_add_default("PaperDimension", "A4", num_defaults
,
2661 num_defaults
= ppd_add_default("ImageableArea", "A4", num_defaults
,
2667 * Open the source and destination file for a copy...
2670 if ((src
= cupsFileOpen(from
, "rb")) == NULL
)
2672 if (num_defaults
> 0)
2678 if ((dst
= cupsFileOpen(to
, "wb")) == NULL
)
2680 if (num_defaults
> 0)
2688 * Copy the source file to the destination...
2691 while (cupsFileGets(src
, buffer
, sizeof(buffer
)) != NULL
)
2693 if (!strncmp(buffer
, "*Default", 8))
2696 * Check for an previous default option choice...
2699 if (!ppd_parse_line(buffer
, option
, sizeof(option
),
2700 choice
, sizeof(choice
)))
2702 for (i
= 0; i
< num_defaults
; i
++)
2703 if (!strcmp(option
, defaults
[i
].option
))
2706 * Substitute the previous choice...
2709 snprintf(buffer
, sizeof(buffer
), "*Default%s: %s", option
,
2710 defaults
[i
].choice
);
2716 cupsFilePrintf(dst
, "%s\n", buffer
);
2719 if (cups_protocol
[0])
2720 cupsFilePrintf(dst
, "%s\n", cups_protocol
);
2722 if (num_defaults
> 0)
2726 * Close both files and return...
2731 return (cupsFileClose(dst
));
2736 * 'create_job()' - Print a file to a printer or class.
2740 create_job(client_t
*con
, /* I - Client connection */
2741 ipp_attribute_t
*uri
) /* I - Printer URI */
2743 ipp_attribute_t
*attr
; /* Current attribute */
2744 const char *dest
; /* Destination */
2745 cups_ptype_t dtype
; /* Destination type (printer or class) */
2746 int priority
; /* Job priority */
2747 char *title
; /* Job name/title */
2748 job_t
*job
; /* Current job */
2749 char job_uri
[HTTP_MAX_URI
],
2751 printer_uri
[HTTP_MAX_URI
],
2753 method
[HTTP_MAX_URI
],
2754 /* Method portion of URI */
2755 username
[HTTP_MAX_URI
],
2756 /* Username portion of URI */
2758 /* Host portion of URI */
2759 resource
[HTTP_MAX_URI
];
2760 /* Resource portion of URI */
2761 int port
; /* Port portion of URI */
2762 printer_t
*printer
; /* Printer data */
2763 int kbytes
; /* Size of print file */
2764 int i
; /* Looping var */
2765 int lowerpagerange
; /* Page range bound */
2768 LogMessage(L_DEBUG2
, "create_job(%d, %s)\n", con
->http
.fd
,
2769 uri
->values
[0].string
.text
);
2772 * Verify that the POST operation was done to a valid URI.
2775 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
2776 strncmp(con
->uri
, "/printers/", 10) != 0)
2778 LogMessage(L_ERROR
, "create_job: cancel request on bad resource \'%s\'!",
2780 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
2785 * Is the destination valid?
2788 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
2790 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
2796 LogMessage(L_ERROR
, "create_job: resource name \'%s\' no good!", resource
);
2797 send_ipp_error(con
, IPP_NOT_FOUND
);
2802 * See if the printer is accepting jobs...
2805 if (dtype
& CUPS_PRINTER_CLASS
)
2807 printer
= FindClass(dest
);
2810 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
2811 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/classes/%s",
2812 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), dest
);
2814 #endif /* AF_INET6 */
2815 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/classes/%s",
2816 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), dest
);
2820 printer
= FindPrinter(dest
);
2823 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
2824 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/printers/%s",
2825 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), dest
);
2827 #endif /* AF_INET6 */
2828 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/printers/%s",
2829 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), dest
);
2832 if (!printer
->accepting
)
2834 LogMessage(L_INFO
, "create_job: destination \'%s\' is not accepting jobs.",
2836 send_ipp_error(con
, IPP_NOT_ACCEPTING
);
2841 * Validate job template attributes; for now just copies and page-ranges...
2844 if ((attr
= ippFindAttribute(con
->request
, "copies", IPP_TAG_INTEGER
)) != NULL
)
2846 if (attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> MaxCopies
)
2848 LogMessage(L_INFO
, "create_job: bad copies value %d.",
2849 attr
->values
[0].integer
);
2850 send_ipp_error(con
, IPP_BAD_REQUEST
);
2855 if ((attr
= ippFindAttribute(con
->request
, "page-ranges", IPP_TAG_RANGE
)) != NULL
)
2857 for (i
= 0, lowerpagerange
= 1; i
< attr
->num_values
; i
++)
2859 if (attr
->values
[i
].range
.lower
< lowerpagerange
||
2860 attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
2862 LogMessage(L_ERROR
, "create_job: bad page-ranges values %d-%d.",
2863 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
2864 send_ipp_error(con
, IPP_BAD_REQUEST
);
2868 lowerpagerange
= attr
->values
[i
].range
.upper
+ 1;
2873 * Make sure we aren't over our limit...
2876 if (NumJobs
>= MaxJobs
&& MaxJobs
)
2879 if (NumJobs
>= MaxJobs
&& MaxJobs
)
2881 LogMessage(L_INFO
, "create_job: too many jobs.");
2882 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
2886 if (!check_quotas(con
, printer
))
2888 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
2893 * Create the job and set things up...
2896 if ((attr
= ippFindAttribute(con
->request
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
2897 priority
= attr
->values
[0].integer
;
2899 ippAddInteger(con
->request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
2902 if ((attr
= ippFindAttribute(con
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
2903 title
= attr
->values
[0].string
.text
;
2905 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
2906 title
= "Untitled");
2908 if ((job
= AddJob(priority
, printer
->name
)) == NULL
)
2910 LogMessage(L_ERROR
, "create_job: unable to add job for destination \'%s\'!",
2912 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
2917 job
->attrs
= con
->request
;
2918 con
->request
= NULL
;
2920 attr
= ippFindAttribute(job
->attrs
, "requesting-user-name", IPP_TAG_NAME
);
2922 if (con
->username
[0])
2923 SetString(&job
->username
, con
->username
);
2924 else if (attr
!= NULL
)
2926 LogMessage(L_DEBUG
, "create_job: requesting-user-name = \'%s\'",
2927 attr
->values
[0].string
.text
);
2929 SetString(&job
->username
, attr
->values
[0].string
.text
);
2932 SetString(&job
->username
, "anonymous");
2935 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name",
2936 NULL
, job
->username
);
2939 attr
->group_tag
= IPP_TAG_JOB
;
2940 SetString(&attr
->name
, "job-originating-user-name");
2943 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-host-name",
2944 IPP_TAG_ZERO
)) != NULL
)
2947 * Request contains a job-originating-host-name attribute; validate it...
2950 if (attr
->value_tag
!= IPP_TAG_NAME
||
2951 attr
->num_values
!= 1 ||
2952 strcmp(con
->http
.hostname
, "localhost") != 0)
2955 * Can't override the value if we aren't connected via localhost.
2956 * Also, we can only have 1 value and it must be a name value.
2959 int i
; /* Looping var */
2961 switch (attr
->value_tag
)
2963 case IPP_TAG_STRING
:
2964 case IPP_TAG_TEXTLANG
:
2965 case IPP_TAG_NAMELANG
:
2968 case IPP_TAG_KEYWORD
:
2970 case IPP_TAG_URISCHEME
:
2971 case IPP_TAG_CHARSET
:
2972 case IPP_TAG_LANGUAGE
:
2973 case IPP_TAG_MIMETYPE
:
2975 * Free old strings...
2978 for (i
= 0; i
< attr
->num_values
; i
++)
2980 free(attr
->values
[i
].string
.text
);
2981 attr
->values
[i
].string
.text
= NULL
;
2982 if (attr
->values
[i
].string
.charset
)
2984 free(attr
->values
[i
].string
.charset
);
2985 attr
->values
[i
].string
.charset
= NULL
;
2994 * Use the default connection hostname instead...
2997 attr
->value_tag
= IPP_TAG_NAME
;
2998 attr
->num_values
= 1;
2999 attr
->values
[0].string
.text
= strdup(con
->http
.hostname
);
3002 attr
->group_tag
= IPP_TAG_JOB
;
3007 * No job-originating-host-name attribute, so use the hostname from
3011 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
,
3012 "job-originating-host-name", NULL
, con
->http
.hostname
);
3015 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
3017 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3018 "time-at-processing", 0);
3019 attr
->value_tag
= IPP_TAG_NOVALUE
;
3020 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3021 "time-at-completed", 0);
3022 attr
->value_tag
= IPP_TAG_NOVALUE
;
3025 * Add remaining job attributes...
3028 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
3029 job
->state
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
3030 "job-state", IPP_JOB_STOPPED
);
3031 job
->sheets
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3032 "job-media-sheets-completed", 0);
3033 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
3035 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
3038 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
3039 attr
->values
[0].integer
= 0;
3041 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3044 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
3045 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
3047 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
3048 "job-hold-until", NULL
, "no-hold");
3049 if (attr
!= NULL
&& strcmp(attr
->values
[0].string
.text
, "no-hold") != 0 &&
3050 !(printer
->type
& CUPS_PRINTER_REMOTE
))
3053 * Hold job until specified time...
3056 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
3059 job
->hold_until
= time(NULL
) + 60;
3061 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
3063 if (!(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)) ||
3067 * Add job sheets options...
3070 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) == NULL
)
3072 LogMessage(L_DEBUG
, "Adding default job-sheets values \"%s,%s\"...",
3073 printer
->job_sheets
[0], printer
->job_sheets
[1]);
3075 attr
= ippAddStrings(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-sheets",
3077 attr
->values
[0].string
.text
= strdup(printer
->job_sheets
[0]);
3078 attr
->values
[1].string
.text
= strdup(printer
->job_sheets
[1]);
3081 job
->job_sheets
= attr
;
3084 * Enforce classification level if set...
3089 if (ClassifyOverride
)
3091 if (strcmp(attr
->values
[0].string
.text
, "none") == 0 &&
3092 (attr
->num_values
== 1 ||
3093 strcmp(attr
->values
[1].string
.text
, "none") == 0))
3096 * Force the leading banner to have the classification on it...
3099 SetString(&attr
->values
[0].string
.text
, Classification
);
3101 else if (attr
->num_values
== 2 &&
3102 strcmp(attr
->values
[0].string
.text
, attr
->values
[1].string
.text
) != 0 &&
3103 strcmp(attr
->values
[0].string
.text
, "none") != 0 &&
3104 strcmp(attr
->values
[1].string
.text
, "none") != 0)
3107 * Can't put two different security markings on the same document!
3110 SetString(&attr
->values
[1].string
.text
, attr
->values
[0].string
.text
);
3113 else if (strcmp(attr
->values
[0].string
.text
, Classification
) != 0 &&
3114 (attr
->num_values
== 1 ||
3115 strcmp(attr
->values
[1].string
.text
, Classification
) != 0))
3118 * Force the leading banner to have the classification on it...
3121 SetString(&attr
->values
[0].string
.text
, Classification
);
3126 * See if we need to add the starting sheet...
3129 if (!(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)))
3131 LogMessage(L_INFO
, "Adding start banner page \"%s\" to job %d.",
3132 attr
->values
[0].string
.text
, job
->id
);
3134 kbytes
= copy_banner(con
, job
, attr
->values
[0].string
.text
);
3136 UpdateQuota(printer
, job
->username
, 0, kbytes
);
3139 else if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
3143 * Save and log the job...
3148 LogMessage(L_INFO
, "Job %d created on \'%s\' by \'%s\'.", job
->id
,
3149 job
->dest
, job
->username
);
3152 * Fill in the response info...
3156 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
3157 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
3158 ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), job
->id
);
3160 #endif /* AF_INET6 */
3161 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
3162 ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), job
->id
);
3164 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, job_uri
);
3166 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
3168 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
3169 job
->state
->values
[0].integer
);
3171 con
->response
->request
.status
.status_code
= IPP_OK
;
3176 * 'delete_printer()' - Remove a printer or class from the system.
3180 delete_printer(client_t
*con
, /* I - Client connection */
3181 ipp_attribute_t
*uri
) /* I - URI of printer or class */
3183 const char *dest
; /* Destination */
3184 cups_ptype_t dtype
; /* Destination type (printer or class) */
3185 char method
[HTTP_MAX_URI
],
3186 /* Method portion of URI */
3187 username
[HTTP_MAX_URI
],
3188 /* Username portion of URI */
3190 /* Host portion of URI */
3191 resource
[HTTP_MAX_URI
];
3192 /* Resource portion of URI */
3193 int port
; /* Port portion of URI */
3194 printer_t
*printer
; /* Printer/class */
3195 char filename
[1024]; /* Script/PPD filename */
3198 LogMessage(L_DEBUG2
, "delete_printer(%d, %s)\n", con
->http
.fd
,
3199 uri
->values
[0].string
.text
);
3202 * Was this operation called from the correct URI?
3205 if (strncmp(con
->uri
, "/admin/", 7) != 0)
3207 LogMessage(L_ERROR
, "delete_printer: admin request on bad resource \'%s\'!",
3209 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
3213 DEBUG_printf(("delete_printer(%08x, %08x)\n", con
, uri
));
3216 * Do we have a valid URI?
3219 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3221 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
3227 LogMessage(L_ERROR
, "delete_printer: resource name \'%s\' no good!", resource
);
3228 send_ipp_error(con
, IPP_NOT_FOUND
);
3233 * Find the printer or class and delete it...
3236 if (dtype
& CUPS_PRINTER_CLASS
)
3237 printer
= FindClass(dest
);
3239 printer
= FindPrinter(dest
);
3242 * Remove old jobs...
3245 CancelJobs(dest
, NULL
, 1);
3248 * Remove any old PPD or script files...
3251 snprintf(filename
, sizeof(filename
), "%s/interfaces/%s", ServerRoot
, dest
);
3254 snprintf(filename
, sizeof(filename
), "%s/ppd/%s.ppd", ServerRoot
, dest
);
3257 if (dtype
& CUPS_PRINTER_CLASS
)
3259 LogMessage(L_INFO
, "Class \'%s\' deleted by \'%s\'.", dest
,
3262 DeletePrinter(printer
, 0);
3267 LogMessage(L_INFO
, "Printer \'%s\' deleted by \'%s\'.", dest
,
3270 DeletePrinter(printer
, 0);
3275 * Return with no errors...
3278 con
->response
->request
.status
.status_code
= IPP_OK
;
3283 * 'get_default()' - Get the default destination.
3287 get_default(client_t
*con
) /* I - Client connection */
3289 LogMessage(L_DEBUG2
, "get_default(%d)\n", con
->http
.fd
);
3291 if (DefaultPrinter
!= NULL
)
3293 copy_attrs(con
->response
, DefaultPrinter
->attrs
,
3294 ippFindAttribute(con
->request
, "requested-attributes",
3295 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
, 0);
3297 con
->response
->request
.status
.status_code
= IPP_OK
;
3300 con
->response
->request
.status
.status_code
= IPP_NOT_FOUND
;
3305 * 'get_devices()' - Get the list of available devices on the local system.
3309 get_devices(client_t
*con
) /* I - Client connection */
3311 LogMessage(L_DEBUG2
, "get_devices(%d)\n", con
->http
.fd
);
3314 * Copy the device attributes to the response using the requested-attributes
3315 * attribute that may be provided by the client.
3318 copy_attrs(con
->response
, Devices
,
3319 ippFindAttribute(con
->request
, "requested-attributes",
3320 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
, IPP_TAG_COPY
);
3322 con
->response
->request
.status
.status_code
= IPP_OK
;
3327 * 'get_jobs()' - Get a list of jobs for the specified printer.
3331 get_jobs(client_t
*con
, /* I - Client connection */
3332 ipp_attribute_t
*uri
) /* I - Printer URI */
3334 ipp_attribute_t
*attr
, /* Current attribute */
3335 *requested
; /* Requested attributes */
3336 const char *dest
; /* Destination */
3337 cups_ptype_t dtype
; /* Destination type (printer or class) */
3338 cups_ptype_t dmask
; /* Destination type mask */
3339 char method
[HTTP_MAX_URI
],
3340 /* Method portion of URI */
3341 username
[HTTP_MAX_URI
],
3342 /* Username portion of URI */
3344 /* Host portion of URI */
3345 resource
[HTTP_MAX_URI
];
3346 /* Resource portion of URI */
3347 int port
; /* Port portion of URI */
3348 int completed
; /* Completed jobs? */
3349 int limit
; /* Maximum number of jobs to return */
3350 int count
; /* Number of jobs that match */
3351 job_t
*job
; /* Current job pointer */
3352 char job_uri
[HTTP_MAX_URI
];
3356 LogMessage(L_DEBUG2
, "get_jobs(%d, %s)\n", con
->http
.fd
,
3357 uri
->values
[0].string
.text
);
3360 * Is the destination valid?
3363 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3365 if (strcmp(resource
, "/") == 0 ||
3366 (strncmp(resource
, "/jobs", 5) == 0 && strlen(resource
) <= 6))
3369 dtype
= (cups_ptype_t
)0;
3370 dmask
= (cups_ptype_t
)0;
3372 else if (strncmp(resource
, "/printers", 9) == 0 && strlen(resource
) <= 10)
3375 dtype
= (cups_ptype_t
)0;
3376 dmask
= CUPS_PRINTER_CLASS
;
3378 else if (strncmp(resource
, "/classes", 8) == 0 && strlen(resource
) <= 9)
3381 dtype
= CUPS_PRINTER_CLASS
;
3382 dmask
= CUPS_PRINTER_CLASS
;
3384 else if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
3390 LogMessage(L_ERROR
, "get_jobs: resource name \'%s\' no good!", resource
);
3391 send_ipp_error(con
, IPP_NOT_FOUND
);
3395 dmask
= CUPS_PRINTER_CLASS
;
3398 * See if the "which-jobs" attribute have been specified...
3401 if ((attr
= ippFindAttribute(con
->request
, "which-jobs", IPP_TAG_KEYWORD
)) != NULL
&&
3402 strcmp(attr
->values
[0].string
.text
, "completed") == 0)
3408 * See if they want to limit the number of jobs reported...
3411 if ((attr
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
)) != NULL
)
3412 limit
= attr
->values
[0].integer
;
3417 * See if we only want to see jobs for a specific user...
3420 if ((attr
= ippFindAttribute(con
->request
, "my-jobs", IPP_TAG_BOOLEAN
)) != NULL
&&
3421 attr
->values
[0].boolean
)
3423 if (con
->username
[0])
3424 strlcpy(username
, con
->username
, sizeof(username
));
3425 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
3426 strlcpy(username
, attr
->values
[0].string
.text
, sizeof(username
));
3428 strcpy(username
, "anonymous");
3433 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3437 * OK, build a list of jobs for this printer...
3440 for (count
= 0, job
= Jobs
; count
< limit
&& job
!= NULL
; job
= job
->next
)
3443 * Filter out jobs that don't match...
3446 DEBUG_printf(("get_jobs: job->id = %d\n", job
->id
));
3448 if ((dest
!= NULL
&& strcmp(job
->dest
, dest
) != 0) &&
3449 (job
->printer
== NULL
|| dest
== NULL
||
3450 strcmp(job
->printer
->name
, dest
) != 0))
3452 if ((job
->dtype
& dmask
) != dtype
&&
3453 (job
->printer
== NULL
|| (job
->printer
->type
& dmask
) != dtype
))
3455 if (username
[0] != '\0' && strcmp(username
, job
->username
) != 0)
3458 if (completed
&& job
->state
->values
[0].integer
<= IPP_JOB_STOPPED
)
3460 if (!completed
&& job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
3465 DEBUG_printf(("get_jobs: count = %d\n", count
));
3468 * Send the requested attributes for each job...
3472 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
3473 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
3474 ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), job
->id
);
3476 #endif /* AF_INET6 */
3477 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
3478 ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), job
->id
);
3480 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
3481 "job-more-info", NULL
, job_uri
);
3483 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
3484 "job-uri", NULL
, job_uri
);
3486 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3487 "job-printer-up-time", time(NULL
));
3490 * Copy the job attributes to the response using the requested-attributes
3491 * attribute that may be provided by the client.
3494 copy_attrs(con
->response
, job
->attrs
, requested
, IPP_TAG_JOB
, 0);
3496 add_job_state_reasons(con
, job
);
3498 ippAddSeparator(con
->response
);
3501 if (requested
!= NULL
)
3502 con
->response
->request
.status
.status_code
= IPP_OK_SUBST
;
3504 con
->response
->request
.status
.status_code
= IPP_OK
;
3509 * 'get_job_attrs()' - Get job attributes.
3513 get_job_attrs(client_t
*con
, /* I - Client connection */
3514 ipp_attribute_t
*uri
) /* I - Job URI */
3516 ipp_attribute_t
*attr
, /* Current attribute */
3517 *requested
; /* Requested attributes */
3518 int jobid
; /* Job ID */
3519 job_t
*job
; /* Current job */
3520 char method
[HTTP_MAX_URI
],
3521 /* Method portion of URI */
3522 username
[HTTP_MAX_URI
],
3523 /* Username portion of URI */
3525 /* Host portion of URI */
3526 resource
[HTTP_MAX_URI
];
3527 /* Resource portion of URI */
3528 int port
; /* Port portion of URI */
3529 char job_uri
[HTTP_MAX_URI
];
3533 LogMessage(L_DEBUG2
, "get_job_attrs(%d, %s)\n", con
->http
.fd
,
3534 uri
->values
[0].string
.text
);
3537 * See if we have a job URI or a printer URI...
3540 if (strcmp(uri
->name
, "printer-uri") == 0)
3543 * Got a printer URI; see if we also have a job-id attribute...
3546 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
3548 LogMessage(L_ERROR
, "get_job_attrs: got a printer-uri attribute but no job-id!");
3549 send_ipp_error(con
, IPP_BAD_REQUEST
);
3553 jobid
= attr
->values
[0].integer
;
3558 * Got a job URI; parse it to get the job ID...
3561 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3563 if (strncmp(resource
, "/jobs/", 6) != 0)
3569 LogMessage(L_ERROR
, "get_job_attrs: bad job-uri attribute \'%s\'!\n",
3570 uri
->values
[0].string
.text
);
3571 send_ipp_error(con
, IPP_BAD_REQUEST
);
3575 jobid
= atoi(resource
+ 6);
3579 * See if the job exists...
3582 if ((job
= FindJob(jobid
)) == NULL
)
3585 * Nope - return a "not found" error...
3588 LogMessage(L_ERROR
, "get_job_attrs: job #%d doesn't exist!", jobid
);
3589 send_ipp_error(con
, IPP_NOT_FOUND
);
3594 * Put out the standard attributes...
3598 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
3599 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d",
3600 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
),
3603 #endif /* AF_INET6 */
3604 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d",
3605 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
),
3608 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
3610 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
3611 "job-more-info", NULL
, job_uri
);
3613 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
3614 "job-uri", NULL
, job_uri
);
3616 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
3617 "job-printer-up-time", time(NULL
));
3620 * Copy the job attributes to the response using the requested-attributes
3621 * attribute that may be provided by the client.
3624 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3627 copy_attrs(con
->response
, job
->attrs
, requested
, IPP_TAG_JOB
, 0);
3629 add_job_state_reasons(con
, job
);
3631 if (requested
!= NULL
)
3632 con
->response
->request
.status
.status_code
= IPP_OK_SUBST
;
3634 con
->response
->request
.status
.status_code
= IPP_OK
;
3639 * 'get_ppds()' - Get the list of PPD files on the local system.
3643 get_ppds(client_t
*con
) /* I - Client connection */
3645 LogMessage(L_DEBUG2
, "get_ppds(%d)\n", con
->http
.fd
);
3648 * Copy the PPD attributes to the response using the requested-attributes
3649 * attribute that may be provided by the client.
3652 copy_attrs(con
->response
, PPDs
,
3653 ippFindAttribute(con
->request
, "requested-attributes",
3654 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
, IPP_TAG_COPY
);
3656 con
->response
->request
.status
.status_code
= IPP_OK
;
3661 * 'get_printer_attrs()' - Get printer attributes.
3665 get_printer_attrs(client_t
*con
, /* I - Client connection */
3666 ipp_attribute_t
*uri
) /* I - Printer URI */
3668 const char *dest
; /* Destination */
3669 cups_ptype_t dtype
; /* Destination type (printer or class) */
3670 char method
[HTTP_MAX_URI
],
3671 /* Method portion of URI */
3672 username
[HTTP_MAX_URI
],
3673 /* Username portion of URI */
3675 /* Host portion of URI */
3676 resource
[HTTP_MAX_URI
];
3677 /* Resource portion of URI */
3678 int port
; /* Port portion of URI */
3679 printer_t
*printer
; /* Printer/class */
3680 time_t curtime
; /* Current time */
3681 int i
; /* Looping var */
3682 ipp_attribute_t
*requested
, /* requested-attributes */
3683 *history
; /* History collection */
3684 int need_history
; /* Need to send history collection? */
3687 LogMessage(L_DEBUG2
, "get_printer_attrs(%d, %s)\n", con
->http
.fd
,
3688 uri
->values
[0].string
.text
);
3691 * Is the destination valid?
3694 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3696 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
3702 LogMessage(L_ERROR
, "get_printer_attrs: resource name \'%s\' no good!", resource
);
3703 send_ipp_error(con
, IPP_NOT_FOUND
);
3707 if (dtype
& CUPS_PRINTER_CLASS
)
3708 printer
= FindClass(dest
);
3710 printer
= FindPrinter(dest
);
3712 curtime
= time(NULL
);
3715 * Copy the printer attributes to the response using requested-attributes
3716 * and document-format attributes that may be provided by the client.
3719 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
3722 add_printer_state_reasons(con
, printer
);
3724 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
3725 "printer-state-message", NULL
, printer
->state_message
);
3727 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
3728 printer
->accepting
);
3730 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3731 "printer-up-time", curtime
);
3732 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3733 "printer-state-time", printer
->state_time
);
3734 ippAddDate(con
->response
, IPP_TAG_PRINTER
, "printer-current-time",
3735 ippTimeToDate(curtime
));
3737 add_queued_job_count(con
, printer
);
3739 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3742 copy_attrs(con
->response
, printer
->attrs
, requested
, IPP_TAG_ZERO
, 0);
3743 copy_attrs(con
->response
, CommonData
, requested
, IPP_TAG_ZERO
, IPP_TAG_COPY
);
3747 if (MaxPrinterHistory
> 0 && printer
->num_history
> 0 && requested
)
3749 for (i
= 0; i
< requested
->num_values
; i
++)
3750 if (!strcmp(requested
->values
[i
].string
.text
, "all") ||
3751 !strcmp(requested
->values
[i
].string
.text
, "printer-state-history"))
3760 history
= ippAddCollections(con
->response
, IPP_TAG_PRINTER
,
3761 "printer-state-history",
3762 printer
->num_history
, NULL
);
3764 for (i
= 0; i
< printer
->num_history
; i
++)
3765 copy_attrs(history
->values
[i
].collection
= ippNew(), printer
->history
[i
],
3766 NULL
, IPP_TAG_ZERO
, 0);
3769 con
->response
->request
.status
.status_code
= requested
? IPP_OK_SUBST
: IPP_OK
;
3774 * 'get_printers()' - Get a list of printers or classes.
3778 get_printers(client_t
*con
, /* I - Client connection */
3779 int type
) /* I - 0 or CUPS_PRINTER_CLASS */
3781 int i
; /* Looping var */
3782 ipp_attribute_t
*requested
, /* requested-attributes */
3783 *history
, /* History collection */
3784 *attr
; /* Current attribute */
3785 int need_history
; /* Need to send history collection? */
3786 int limit
; /* Maximum number of printers to return */
3787 int count
; /* Number of printers that match */
3788 printer_t
*printer
; /* Current printer pointer */
3789 time_t curtime
; /* Current time */
3790 int printer_type
, /* printer-type attribute */
3791 printer_mask
; /* printer-type-mask attribute */
3792 char *location
; /* Location string */
3793 char name
[IPP_MAX_NAME
],
3795 *nameptr
; /* Pointer into name */
3796 printer_t
*iclass
; /* Implicit class */
3799 LogMessage(L_DEBUG2
, "get_printers(%d, %x)\n", con
->http
.fd
, type
);
3802 * See if they want to limit the number of printers reported...
3805 if ((attr
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
)) != NULL
)
3806 limit
= attr
->values
[0].integer
;
3811 * Support filtering...
3814 if ((attr
= ippFindAttribute(con
->request
, "printer-type", IPP_TAG_ENUM
)) != NULL
)
3815 printer_type
= attr
->values
[0].integer
;
3819 if ((attr
= ippFindAttribute(con
->request
, "printer-type-mask", IPP_TAG_ENUM
)) != NULL
)
3820 printer_mask
= attr
->values
[0].integer
;
3824 if ((attr
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
3825 location
= attr
->values
[0].string
.text
;
3829 requested
= ippFindAttribute(con
->request
, "requested-attributes",
3834 if (MaxPrinterHistory
> 0 && requested
)
3836 for (i
= 0; i
< requested
->num_values
; i
++)
3837 if (!strcmp(requested
->values
[i
].string
.text
, "all") ||
3838 !strcmp(requested
->values
[i
].string
.text
, "printer-state-history"))
3846 * OK, build a list of printers for this printer...
3849 curtime
= time(NULL
);
3851 for (count
= 0, printer
= Printers
;
3852 count
< limit
&& printer
!= NULL
;
3853 printer
= printer
->next
)
3854 if ((printer
->type
& CUPS_PRINTER_CLASS
) == type
&&
3855 (printer
->type
& printer_mask
) == printer_type
&&
3856 (location
== NULL
|| printer
->location
== NULL
||
3857 strcasecmp(printer
->location
, location
) == 0))
3860 * If HideImplicitMembers is enabled, see if this printer or class
3861 * is a member of an implicit class...
3864 if (ImplicitClasses
&& HideImplicitMembers
&&
3865 (printer
->type
& CUPS_PRINTER_REMOTE
))
3868 * Make a copy of the printer name...
3871 strlcpy(name
, printer
->name
, sizeof(name
));
3873 if ((nameptr
= strchr(name
, '@')) != NULL
)
3876 * Strip trailing @server...
3882 * Find the core printer, if any...
3885 if ((iclass
= FindPrinter(name
)) != NULL
&&
3886 (iclass
->type
& CUPS_PRINTER_IMPLICIT
))
3892 * Add the group separator as needed...
3896 ippAddSeparator(con
->response
);
3901 * Send the following attributes for each printer:
3904 * printer-state-message
3905 * printer-is-accepting-jobs
3906 * + all printer attributes
3909 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
3910 "printer-state", printer
->state
);
3912 add_printer_state_reasons(con
, printer
);
3914 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
3915 "printer-state-message", NULL
, printer
->state_message
);
3917 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
3918 printer
->accepting
);
3920 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3921 "printer-up-time", curtime
);
3922 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
3923 "printer-state-time", printer
->state_time
);
3924 ippAddDate(con
->response
, IPP_TAG_PRINTER
, "printer-current-time",
3925 ippTimeToDate(curtime
));
3927 add_queued_job_count(con
, printer
);
3929 copy_attrs(con
->response
, printer
->attrs
, requested
, IPP_TAG_ZERO
, 0);
3931 copy_attrs(con
->response
, CommonData
, requested
, IPP_TAG_ZERO
,
3934 if (need_history
&& printer
->num_history
> 0)
3936 history
= ippAddCollections(con
->response
, IPP_TAG_PRINTER
,
3937 "printer-state-history",
3938 printer
->num_history
, NULL
);
3940 for (i
= 0; i
< printer
->num_history
; i
++)
3941 copy_attrs(history
->values
[i
].collection
= ippNew(),
3942 printer
->history
[i
], NULL
, IPP_TAG_ZERO
, 0);
3946 con
->response
->request
.status
.status_code
= requested
? IPP_OK_SUBST
: IPP_OK
;
3951 * 'hold_job()' - Hold a print job.
3955 hold_job(client_t
*con
, /* I - Client connection */
3956 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
3958 ipp_attribute_t
*attr
, /* Current job-hold-until */
3959 *newattr
; /* New job-hold-until */
3960 int jobid
; /* Job ID */
3961 char method
[HTTP_MAX_URI
],
3962 /* Method portion of URI */
3963 username
[HTTP_MAX_URI
],
3964 /* Username portion of URI */
3966 /* Host portion of URI */
3967 resource
[HTTP_MAX_URI
];
3968 /* Resource portion of URI */
3969 int port
; /* Port portion of URI */
3970 job_t
*job
; /* Job information */
3973 LogMessage(L_DEBUG2
, "hold_job(%d, %s)\n", con
->http
.fd
,
3974 uri
->values
[0].string
.text
);
3977 * Verify that the POST operation was done to a valid URI.
3980 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
3981 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
3982 strncmp(con
->uri
, "/printers/", 10) != 0)
3984 LogMessage(L_ERROR
, "hold_job: hold request on bad resource \'%s\'!",
3986 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
3991 * See if we have a job URI or a printer URI...
3994 if (strcmp(uri
->name
, "printer-uri") == 0)
3997 * Got a printer URI; see if we also have a job-id attribute...
4000 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
4002 LogMessage(L_ERROR
, "hold_job: got a printer-uri attribute but no job-id!");
4003 send_ipp_error(con
, IPP_BAD_REQUEST
);
4007 jobid
= attr
->values
[0].integer
;
4012 * Got a job URI; parse it to get the job ID...
4015 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
4017 if (strncmp(resource
, "/jobs/", 6) != 0)
4023 LogMessage(L_ERROR
, "hold_job: bad job-uri attribute \'%s\'!",
4024 uri
->values
[0].string
.text
);
4025 send_ipp_error(con
, IPP_BAD_REQUEST
);
4029 jobid
= atoi(resource
+ 6);
4033 * See if the job exists...
4036 if ((job
= FindJob(jobid
)) == NULL
)
4039 * Nope - return a "not found" error...
4042 LogMessage(L_ERROR
, "hold_job: job #%d doesn't exist!", jobid
);
4043 send_ipp_error(con
, IPP_NOT_FOUND
);
4048 * See if the job is owned by the requesting user...
4051 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
4053 LogMessage(L_ERROR
, "hold_job: \"%s\" not authorized to hold job id %d owned by \"%s\"!",
4054 username
, jobid
, job
->username
);
4055 send_ipp_error(con
, IPP_FORBIDDEN
);
4060 * Hold the job and return...
4065 if ((newattr
= ippFindAttribute(con
->request
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
4066 newattr
= ippFindAttribute(con
->request
, "job-hold-until", IPP_TAG_NAME
);
4068 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
4069 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
4074 * Free the old hold value and copy the new one over...
4077 free(attr
->values
[0].string
.text
);
4079 if (newattr
!= NULL
)
4081 attr
->value_tag
= newattr
->value_tag
;
4082 attr
->values
[0].string
.text
= strdup(newattr
->values
[0].string
.text
);
4086 attr
->value_tag
= IPP_TAG_KEYWORD
;
4087 attr
->values
[0].string
.text
= strdup("indefinite");
4091 * Hold job until specified time...
4094 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
4097 LogMessage(L_INFO
, "Job %d was held by \'%s\'.", jobid
, username
);
4099 con
->response
->request
.status
.status_code
= IPP_OK
;
4104 * 'move_job()' - Move a job to a new destination.
4108 move_job(client_t
*con
, /* I - Client connection */
4109 ipp_attribute_t
*uri
) /* I - Job URI */
4111 ipp_attribute_t
*attr
; /* Current attribute */
4112 int jobid
; /* Job ID */
4113 job_t
*job
; /* Current job */
4114 const char *dest
; /* Destination */
4115 cups_ptype_t dtype
; /* Destination type (printer or class) */
4116 char method
[HTTP_MAX_URI
],
4117 /* Method portion of URI */
4118 username
[HTTP_MAX_URI
],
4119 /* Username portion of URI */
4121 /* Host portion of URI */
4122 resource
[HTTP_MAX_URI
];
4123 /* Resource portion of URI */
4124 int port
; /* Port portion of URI */
4127 LogMessage(L_DEBUG2
, "move_job(%d, %s)\n", con
->http
.fd
,
4128 uri
->values
[0].string
.text
);
4131 * See if we have a job URI or a printer URI...
4134 if (strcmp(uri
->name
, "printer-uri") == 0)
4137 * Got a printer URI; see if we also have a job-id attribute...
4140 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
4142 LogMessage(L_ERROR
, "move_job: got a printer-uri attribute but no job-id!");
4143 send_ipp_error(con
, IPP_BAD_REQUEST
);
4147 jobid
= attr
->values
[0].integer
;
4152 * Got a job URI; parse it to get the job ID...
4155 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
4157 if (strncmp(resource
, "/jobs/", 6) != 0)
4163 LogMessage(L_ERROR
, "move_job: bad job-uri attribute \'%s\'!\n",
4164 uri
->values
[0].string
.text
);
4165 send_ipp_error(con
, IPP_BAD_REQUEST
);
4169 jobid
= atoi(resource
+ 6);
4173 * See if the job exists...
4176 if ((job
= FindJob(jobid
)) == NULL
)
4179 * Nope - return a "not found" error...
4182 LogMessage(L_ERROR
, "move_job: job #%d doesn't exist!", jobid
);
4183 send_ipp_error(con
, IPP_NOT_FOUND
);
4188 * See if the job has been completed...
4191 if (job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
4194 * Return a "not-possible" error...
4197 LogMessage(L_ERROR
, "move_job: job #%d is finished and cannot be altered!", jobid
);
4198 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
4203 * See if the job is owned by the requesting user...
4206 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
4208 LogMessage(L_ERROR
, "move_job: \"%s\" not authorized to move job id %d owned by \"%s\"!",
4209 username
, jobid
, job
->username
);
4210 send_ipp_error(con
, IPP_FORBIDDEN
);
4214 if ((attr
= ippFindAttribute(con
->request
, "job-printer-uri", IPP_TAG_URI
)) == NULL
)
4217 * Need job-printer-uri...
4220 LogMessage(L_ERROR
, "move_job: job-printer-uri attribute missing!");
4221 send_ipp_error(con
, IPP_BAD_REQUEST
);
4226 * Move the job to a different printer or class...
4229 httpSeparate(attr
->values
[0].string
.text
, method
, username
, host
, &port
,
4231 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
4237 LogMessage(L_ERROR
, "move_job: resource name \'%s\' no good!", resource
);
4238 send_ipp_error(con
, IPP_NOT_FOUND
);
4242 MoveJob(jobid
, dest
);
4245 * Start jobs if possible...
4251 * Return with "everything is OK" status...
4254 con
->response
->request
.status
.status_code
= IPP_OK
;
4259 * 'ppd_add_default()' - Add a PPD default choice.
4262 static int /* O - Number of defaults */
4263 ppd_add_default(const char *option
, /* I - Option name */
4264 const char *choice
, /* I - Choice name */
4266 /* I - Number of defaults */
4267 ppd_default_t
**defaults
)
4270 int i
; /* Looping var */
4271 ppd_default_t
*temp
; /* Temporary defaults array */
4275 * First check if the option already has a default value; the PPD spec
4276 * says that the first one is used...
4279 for (i
= 0, temp
= *defaults
; i
< num_defaults
; i
++)
4280 if (!strcmp(option
, temp
[i
].option
))
4281 return (num_defaults
);
4284 * Now add the option...
4287 if (num_defaults
== 0)
4288 temp
= malloc(sizeof(ppd_default_t
));
4290 temp
= realloc(*defaults
, (num_defaults
+ 1) * sizeof(ppd_default_t
));
4294 LogMessage(L_ERROR
, "ppd_add_default: Unable to add default value for \"%s\" - %s",
4295 option
, strerror(errno
));
4296 return (num_defaults
);
4300 temp
+= num_defaults
;
4302 strlcpy(temp
->option
, option
, sizeof(temp
->option
));
4303 strlcpy(temp
->choice
, choice
, sizeof(temp
->choice
));
4305 return (num_defaults
+ 1);
4310 * 'ppd_parse_line()' - Parse a PPD default line.
4313 static int /* O - 0 on success, -1 on failure */
4314 ppd_parse_line(const char *line
, /* I - Line */
4315 char *option
, /* O - Option name */
4316 int olen
, /* I - Size of option name */
4317 char *choice
, /* O - Choice name */
4318 int clen
) /* I - Size of choice name */
4321 * Verify this is a default option line...
4324 if (strncmp(line
, "*Default", 8))
4328 * Read the option name...
4331 for (line
+= 8, olen
--; isalnum(*line
); line
++)
4341 * Skip everything else up to the colon (:)...
4344 while (*line
&& *line
!= ':')
4353 * Now grab the option choice, skipping leading whitespace...
4356 while (isspace(*line
))
4359 for (clen
--; isalnum(*line
); line
++)
4369 * Return with no errors...
4377 * 'print_job()' - Print a file to a printer or class.
4381 print_job(client_t
*con
, /* I - Client connection */
4382 ipp_attribute_t
*uri
) /* I - Printer URI */
4384 ipp_attribute_t
*attr
; /* Current attribute */
4385 ipp_attribute_t
*format
; /* Document-format attribute */
4386 const char *dest
; /* Destination */
4387 cups_ptype_t dtype
; /* Destination type (printer or class) */
4388 int priority
; /* Job priority */
4389 char *title
; /* Job name/title */
4390 job_t
*job
; /* Current job */
4391 char job_uri
[HTTP_MAX_URI
],
4393 printer_uri
[HTTP_MAX_URI
],
4395 method
[HTTP_MAX_URI
],
4396 /* Method portion of URI */
4397 username
[HTTP_MAX_URI
],
4398 /* Username portion of URI */
4400 /* Host portion of URI */
4401 resource
[HTTP_MAX_URI
],
4402 /* Resource portion of URI */
4403 filename
[1024]; /* Job filename */
4404 int port
; /* Port portion of URI */
4405 mime_type_t
*filetype
; /* Type of file */
4406 char super
[MIME_MAX_SUPER
],
4407 /* Supertype of file */
4408 type
[MIME_MAX_TYPE
],
4409 /* Subtype of file */
4410 mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
4411 /* Textual name of mime type */
4412 printer_t
*printer
; /* Printer data */
4413 struct stat fileinfo
; /* File information */
4414 int kbytes
; /* Size of file */
4415 int i
; /* Looping var */
4416 int lowerpagerange
; /* Page range bound */
4417 int compression
; /* Document compression */
4420 LogMessage(L_DEBUG2
, "print_job(%d, %s)\n", con
->http
.fd
,
4421 uri
->values
[0].string
.text
);
4424 * Verify that the POST operation was done to a valid URI.
4427 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
4428 strncmp(con
->uri
, "/printers/", 10) != 0)
4430 LogMessage(L_ERROR
, "print_job: cancel request on bad resource \'%s\'!",
4432 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
4437 * Validate job template attributes; for now just copies and page-ranges...
4440 if ((attr
= ippFindAttribute(con
->request
, "copies", IPP_TAG_INTEGER
)) != NULL
)
4442 if (attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> MaxCopies
)
4444 LogMessage(L_INFO
, "print_job: bad copies value %d.",
4445 attr
->values
[0].integer
);
4446 send_ipp_error(con
, IPP_BAD_REQUEST
);
4451 if ((attr
= ippFindAttribute(con
->request
, "page-ranges", IPP_TAG_RANGE
)) != NULL
)
4453 for (i
= 0, lowerpagerange
= 1; i
< attr
->num_values
; i
++)
4455 if (attr
->values
[i
].range
.lower
< lowerpagerange
||
4456 attr
->values
[i
].range
.lower
> attr
->values
[i
].range
.upper
)
4458 LogMessage(L_ERROR
, "print_job: bad page-ranges values %d-%d.",
4459 attr
->values
[i
].range
.lower
, attr
->values
[i
].range
.upper
);
4460 send_ipp_error(con
, IPP_BAD_REQUEST
);
4464 lowerpagerange
= attr
->values
[i
].range
.upper
+ 1;
4469 * OK, see if the client is sending the document compressed - CUPS
4470 * only supports "none" and "gzip".
4473 compression
= CUPS_FILE_NONE
;
4475 if ((attr
= ippFindAttribute(con
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
)
4477 if (strcmp(attr
->values
[0].string
.text
, "none")
4479 && strcmp(attr
->values
[0].string
.text
, "gzip")
4480 #endif /* HAVE_LIBZ */
4483 LogMessage(L_ERROR
, "print_job: Unsupported compression \"%s\"!",
4484 attr
->values
[0].string
.text
);
4485 send_ipp_error(con
, IPP_ATTRIBUTES
);
4486 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
4487 "compression", NULL
, attr
->values
[0].string
.text
);
4492 if (!strcmp(attr
->values
[0].string
.text
, "gzip"))
4493 compression
= CUPS_FILE_GZIP
;
4494 #endif /* HAVE_LIBZ */
4498 * Do we have a file to print?
4503 LogMessage(L_ERROR
, "print_job: No file!?!");
4504 send_ipp_error(con
, IPP_BAD_REQUEST
);
4509 * Is it a format we support?
4512 if ((format
= ippFindAttribute(con
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
4515 * Grab format from client...
4518 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
, type
) != 2)
4520 LogMessage(L_ERROR
, "print_job: could not scan type \'%s\'!",
4521 format
->values
[0].string
.text
);
4522 send_ipp_error(con
, IPP_BAD_REQUEST
);
4529 * No document format attribute? Auto-type it!
4532 strcpy(super
, "application");
4533 strcpy(type
, "octet-stream");
4536 if (strcmp(super
, "application") == 0 &&
4537 strcmp(type
, "octet-stream") == 0)
4540 * Auto-type the file...
4543 LogMessage(L_DEBUG
, "print_job: auto-typing file...");
4545 filetype
= mimeFileType(MimeDatabase
, con
->filename
, &compression
);
4547 if (filetype
!= NULL
)
4550 * Replace the document-format attribute value with the auto-typed one.
4553 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
4558 free(format
->values
[0].string
.text
);
4559 format
->values
[0].string
.text
= strdup(mimetype
);
4562 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
4563 "document-format", NULL
, mimetype
);
4566 filetype
= mimeType(MimeDatabase
, super
, type
);
4569 filetype
= mimeType(MimeDatabase
, super
, type
);
4571 if (filetype
== NULL
)
4573 LogMessage(L_ERROR
, "print_job: Unsupported format \'%s/%s\'!",
4575 LogMessage(L_INFO
, "Hint: Do you have the raw file printing rules enabled?");
4576 send_ipp_error(con
, IPP_DOCUMENT_FORMAT
);
4579 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
4580 "document-format", NULL
, format
->values
[0].string
.text
);
4585 LogMessage(L_DEBUG
, "print_job: request file type is %s/%s.",
4586 filetype
->super
, filetype
->type
);
4589 * Read any embedded job ticket info from PS files...
4592 if (strcasecmp(filetype
->super
, "application") == 0 &&
4593 strcasecmp(filetype
->type
, "postscript") == 0)
4594 read_ps_job_ticket(con
);
4597 * Is the destination valid?
4600 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
4602 if ((dest
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
4608 LogMessage(L_ERROR
, "print_job: resource name \'%s\' no good!", resource
);
4609 send_ipp_error(con
, IPP_NOT_FOUND
);
4614 * See if the printer is accepting jobs...
4617 if (dtype
& CUPS_PRINTER_CLASS
)
4619 printer
= FindClass(dest
);
4621 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
4622 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/classes/%s",
4623 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), dest
);
4625 #endif /* AF_INET6 */
4626 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/classes/%s",
4627 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), dest
);
4631 printer
= FindPrinter(dest
);
4634 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
4635 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/printers/%s",
4636 ServerName
, ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), dest
);
4638 #endif /* AF_INET6 */
4639 snprintf(printer_uri
, sizeof(printer_uri
), "http://%s:%d/printers/%s",
4640 ServerName
, ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), dest
);
4643 if (!printer
->accepting
)
4645 LogMessage(L_INFO
, "print_job: destination \'%s\' is not accepting jobs.",
4647 send_ipp_error(con
, IPP_NOT_ACCEPTING
);
4652 * Make sure we aren't over our limit...
4655 if (NumJobs
>= MaxJobs
&& MaxJobs
)
4658 if (NumJobs
>= MaxJobs
&& MaxJobs
)
4660 LogMessage(L_INFO
, "print_job: too many jobs - %d jobs, max jobs is %d.",
4662 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
4666 if (!check_quotas(con
, printer
))
4668 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
4673 * Create the job and set things up...
4676 if ((attr
= ippFindAttribute(con
->request
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
4677 priority
= attr
->values
[0].integer
;
4679 ippAddInteger(con
->request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
4682 if ((attr
= ippFindAttribute(con
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
4683 title
= attr
->values
[0].string
.text
;
4685 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
4686 title
= "Untitled");
4688 if ((job
= AddJob(priority
, printer
->name
)) == NULL
)
4690 LogMessage(L_ERROR
, "print_job: unable to add job for destination \'%s\'!",
4692 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
4697 job
->attrs
= con
->request
;
4698 con
->request
= NULL
;
4701 * Copy the rest of the job info...
4704 attr
= ippFindAttribute(job
->attrs
, "requesting-user-name", IPP_TAG_NAME
);
4706 if (con
->username
[0])
4707 SetString(&job
->username
, con
->username
);
4708 else if (attr
!= NULL
)
4710 LogMessage(L_DEBUG
, "print_job: requesting-user-name = \'%s\'",
4711 attr
->values
[0].string
.text
);
4713 SetString(&job
->username
, attr
->values
[0].string
.text
);
4716 SetString(&job
->username
, "anonymous");
4719 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name",
4720 NULL
, job
->username
);
4723 attr
->group_tag
= IPP_TAG_JOB
;
4724 SetString(&attr
->name
, "job-originating-user-name");
4728 * Add remaining job attributes...
4731 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-host-name",
4732 IPP_TAG_ZERO
)) != NULL
)
4735 * Request contains a job-originating-host-name attribute; validate it...
4738 if (attr
->value_tag
!= IPP_TAG_NAME
||
4739 attr
->num_values
!= 1 ||
4740 strcmp(con
->http
.hostname
, "localhost") != 0)
4743 * Can't override the value if we aren't connected via localhost.
4744 * Also, we can only have 1 value and it must be a name value.
4747 int i
; /* Looping var */
4749 switch (attr
->value_tag
)
4751 case IPP_TAG_STRING
:
4752 case IPP_TAG_TEXTLANG
:
4753 case IPP_TAG_NAMELANG
:
4756 case IPP_TAG_KEYWORD
:
4758 case IPP_TAG_URISCHEME
:
4759 case IPP_TAG_CHARSET
:
4760 case IPP_TAG_LANGUAGE
:
4761 case IPP_TAG_MIMETYPE
:
4763 * Free old strings...
4766 for (i
= 0; i
< attr
->num_values
; i
++)
4768 free(attr
->values
[i
].string
.text
);
4769 attr
->values
[i
].string
.text
= NULL
;
4770 if (attr
->values
[i
].string
.charset
)
4772 free(attr
->values
[i
].string
.charset
);
4773 attr
->values
[i
].string
.charset
= NULL
;
4782 * Use the default connection hostname instead...
4785 attr
->value_tag
= IPP_TAG_NAME
;
4786 attr
->num_values
= 1;
4787 attr
->values
[0].string
.text
= strdup(con
->http
.hostname
);
4790 attr
->group_tag
= IPP_TAG_JOB
;
4795 * No job-originating-host-name attribute, so use the hostname from
4799 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
,
4800 "job-originating-host-name", NULL
, con
->http
.hostname
);
4803 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
4804 job
->state
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
4805 "job-state", IPP_JOB_PENDING
);
4806 job
->sheets
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4807 "job-media-sheets-completed", 0);
4808 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
4810 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
4813 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) == NULL
)
4814 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4817 if (stat(con
->filename
, &fileinfo
))
4820 kbytes
= (fileinfo
.st_size
+ 1023) / 1024;
4822 UpdateQuota(printer
, job
->username
, 0, kbytes
);
4823 attr
->values
[0].integer
+= kbytes
;
4825 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
4827 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4828 "time-at-processing", 0);
4829 attr
->value_tag
= IPP_TAG_NOVALUE
;
4830 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
4831 "time-at-completed", 0);
4832 attr
->value_tag
= IPP_TAG_NOVALUE
;
4834 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
4835 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
4837 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
4838 "job-hold-until", NULL
, "no-hold");
4840 if (attr
!= NULL
&& strcmp(attr
->values
[0].string
.text
, "no-hold") != 0 &&
4841 !(printer
->type
& CUPS_PRINTER_REMOTE
))
4844 * Hold job until specified time...
4847 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
4848 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
4851 if (!(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)) ||
4855 * Add job sheets options...
4858 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) == NULL
)
4860 LogMessage(L_DEBUG
, "Adding default job-sheets values \"%s,%s\"...",
4861 printer
->job_sheets
[0], printer
->job_sheets
[1]);
4863 attr
= ippAddStrings(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-sheets",
4865 attr
->values
[0].string
.text
= strdup(printer
->job_sheets
[0]);
4866 attr
->values
[1].string
.text
= strdup(printer
->job_sheets
[1]);
4869 job
->job_sheets
= attr
;
4872 * Enforce classification level if set...
4877 if (ClassifyOverride
)
4879 if (strcmp(attr
->values
[0].string
.text
, "none") == 0 &&
4880 (attr
->num_values
== 1 ||
4881 strcmp(attr
->values
[1].string
.text
, "none") == 0))
4884 * Force the leading banner to have the classification on it...
4887 SetString(&attr
->values
[0].string
.text
, Classification
);
4889 else if (attr
->num_values
== 2 &&
4890 strcmp(attr
->values
[0].string
.text
, attr
->values
[1].string
.text
) != 0 &&
4891 strcmp(attr
->values
[0].string
.text
, "none") != 0 &&
4892 strcmp(attr
->values
[1].string
.text
, "none") != 0)
4895 * Can't put two different security markings on the same document!
4898 SetString(&attr
->values
[1].string
.text
, attr
->values
[0].string
.text
);
4901 else if (strcmp(attr
->values
[0].string
.text
, Classification
) != 0 &&
4902 (attr
->num_values
== 1 ||
4903 strcmp(attr
->values
[1].string
.text
, Classification
) != 0))
4906 * Force the leading banner to have the classification on it...
4909 SetString(&attr
->values
[0].string
.text
, Classification
);
4914 * Add the starting sheet...
4917 if (!(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)))
4919 LogMessage(L_INFO
, "Adding start banner page \"%s\" to job %d.",
4920 attr
->values
[0].string
.text
, job
->id
);
4922 kbytes
= copy_banner(con
, job
, attr
->values
[0].string
.text
);
4924 UpdateQuota(printer
, job
->username
, 0, kbytes
);
4927 else if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
4931 * Add the job file...
4934 if (add_file(con
, job
, filetype
, compression
))
4937 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
4939 rename(con
->filename
, filename
);
4940 ClearString(&con
->filename
);
4943 * See if we need to add the ending sheet...
4946 if (!(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)) &&
4947 attr
->num_values
> 1)
4953 LogMessage(L_INFO
, "Adding end banner page \"%s\" to job %d.",
4954 attr
->values
[1].string
.text
, job
->id
);
4956 kbytes
= copy_banner(con
, job
, attr
->values
[1].string
.text
);
4958 UpdateQuota(printer
, job
->username
, 0, kbytes
);
4962 * Log and save the job...
4965 LogMessage(L_INFO
, "Job %d queued on \'%s\' by \'%s\'.", job
->id
,
4966 job
->dest
, job
->username
);
4967 LogMessage(L_DEBUG
, "Job %d hold_until = %d", job
->id
, (int)job
->hold_until
);
4972 * Start the job if possible...
4978 * Fill in the response info...
4982 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
4983 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
4984 ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), job
->id
);
4986 #endif /* AF_INET6 */
4987 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
4988 ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), job
->id
);
4990 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, job_uri
);
4992 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
4994 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
4995 job
->state
->values
[0].integer
);
4996 add_job_state_reasons(con
, job
);
4998 con
->response
->request
.status
.status_code
= IPP_OK
;
5003 * 'read_ps_job_ticket()' - Reads a job ticket embedded in a PS file.
5005 * This function only gets called when printing a single PostScript
5006 * file using the Print-Job operation. It doesn't work for Create-Job +
5007 * Send-File, since the job attributes need to be set at job creation
5008 * time for banners to work. The embedded PS job ticket stuff is here
5009 * only to allow the Windows printer driver for CUPS to pass in JCL
5010 * options and IPP attributes which otherwise would be lost.
5012 * The format of a PS job ticket is simple:
5014 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
5016 * %cupsJobTicket: attr1=value1
5017 * %cupsJobTicket: attr2=value2
5019 * %cupsJobTicket: attrN=valueN
5021 * Job ticket lines must appear immediately after the first line that
5022 * specifies PostScript format (%!PS-Adobe-3.0), and CUPS will stop
5023 * looking for job ticket info when it finds a line that does not begin
5024 * with "%cupsJobTicket:".
5026 * The maximum length of a job ticket line, including the prefix, is
5027 * 255 characters to conform with the Adobe DSC.
5029 * Read-only attributes are rejected with a notice to the error log in
5030 * case a malicious user tries anything. Since the job ticket is read
5031 * prior to attribute validation in print_job(), job ticket attributes
5032 * will go through the same validation as IPP attributes...
5036 read_ps_job_ticket(client_t
*con
) /* I - Client connection */
5038 cups_file_t
*fp
; /* File to read from */
5039 char line
[256]; /* Line data */
5040 int num_options
; /* Number of options */
5041 cups_option_t
*options
; /* Options */
5042 ipp_t
*ticket
; /* New attributes */
5043 ipp_attribute_t
*attr
, /* Current attribute */
5044 *attr2
, /* Job attribute */
5045 *prev2
; /* Previous job attribute */
5049 * First open the print file...
5052 if ((fp
= cupsFileOpen(con
->filename
, "rb")) == NULL
)
5054 LogMessage(L_ERROR
, "read_ps_job_ticket: Unable to open PostScript print file - %s",
5060 * Skip the first line...
5063 if (cupsFileGets(fp
, line
, sizeof(line
)) == NULL
)
5065 LogMessage(L_ERROR
, "read_ps_job_ticket: Unable to read from PostScript print file - %s",
5071 if (strncmp(line
, "%!PS-Adobe-", 11) != 0)
5074 * Not a DSC-compliant file, so no job ticket info will be available...
5082 * Read job ticket info from the file...
5088 while (cupsFileGets(fp
, line
, sizeof(line
)) != NULL
)
5091 * Stop at the first non-ticket line...
5094 if (strncmp(line
, "%cupsJobTicket:", 15) != 0)
5098 * Add the options to the option array...
5101 num_options
= cupsParseOptions(line
+ 15, num_options
, &options
);
5105 * Done with the file; see if we have any options...
5110 if (num_options
== 0)
5114 * OK, convert the options to an attribute list, and apply them to
5119 cupsEncodeOptions(ticket
, num_options
, options
);
5122 * See what the user wants to change.
5125 for (attr
= ticket
->attrs
; attr
!= NULL
; attr
= attr
->next
)
5127 if (attr
->group_tag
!= IPP_TAG_JOB
|| !attr
->name
)
5130 if (strcmp(attr
->name
, "job-originating-host-name") == 0 ||
5131 strcmp(attr
->name
, "job-originating-user-name") == 0 ||
5132 strcmp(attr
->name
, "job-media-sheets-completed") == 0 ||
5133 strcmp(attr
->name
, "job-k-octets") == 0 ||
5134 strcmp(attr
->name
, "job-id") == 0 ||
5135 strncmp(attr
->name
, "job-state", 9) == 0 ||
5136 strncmp(attr
->name
, "time-at-", 8) == 0)
5137 continue; /* Read-only attrs */
5139 if ((attr2
= ippFindAttribute(con
->request
, attr
->name
, IPP_TAG_ZERO
)) != NULL
)
5142 * Some other value; first free the old value...
5145 if (con
->request
->attrs
== attr2
)
5147 con
->request
->attrs
= attr2
->next
;
5152 for (prev2
= con
->request
->attrs
; prev2
!= NULL
; prev2
= prev2
->next
)
5153 if (prev2
->next
== attr2
)
5155 prev2
->next
= attr2
->next
;
5160 if (con
->request
->last
== attr2
)
5161 con
->request
->last
= prev2
;
5163 _ipp_free_attr(attr2
);
5167 * Add new option by copying it...
5170 copy_attribute(con
->request
, attr
, 0);
5174 * Then free the attribute list and option array...
5178 cupsFreeOptions(num_options
, options
);
5183 * 'reject_jobs()' - Reject print jobs to a printer.
5187 reject_jobs(client_t
*con
, /* I - Client connection */
5188 ipp_attribute_t
*uri
) /* I - Printer or class URI */
5190 cups_ptype_t dtype
; /* Destination type (printer or class) */
5191 char method
[HTTP_MAX_URI
],
5192 /* Method portion of URI */
5193 username
[HTTP_MAX_URI
],
5194 /* Username portion of URI */
5196 /* Host portion of URI */
5197 resource
[HTTP_MAX_URI
];
5198 /* Resource portion of URI */
5199 int port
; /* Port portion of URI */
5200 const char *name
; /* Printer name */
5201 printer_t
*printer
; /* Printer data */
5202 ipp_attribute_t
*attr
; /* printer-state-message text */
5205 LogMessage(L_DEBUG2
, "reject_jobs(%d, %s)\n", con
->http
.fd
,
5206 uri
->values
[0].string
.text
);
5209 * Was this operation called from the correct URI?
5212 if (strncmp(con
->uri
, "/admin/", 7) != 0)
5214 LogMessage(L_ERROR
, "reject_jobs: admin request on bad resource \'%s\'!",
5216 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5221 * Is the destination valid?
5224 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5226 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
5232 LogMessage(L_ERROR
, "reject_jobs: resource name \'%s\' no good!", resource
);
5233 send_ipp_error(con
, IPP_NOT_FOUND
);
5238 * Reject jobs sent to the printer...
5241 if (dtype
& CUPS_PRINTER_CLASS
)
5242 printer
= FindClass(name
);
5244 printer
= FindPrinter(name
);
5246 printer
->accepting
= 0;
5248 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
5249 IPP_TAG_TEXT
)) == NULL
)
5250 strcpy(printer
->state_message
, "Rejecting Jobs");
5252 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
5253 sizeof(printer
->state_message
));
5255 AddPrinterHistory(printer
);
5257 if (dtype
& CUPS_PRINTER_CLASS
)
5261 LogMessage(L_INFO
, "Class \'%s\' rejecting jobs (\'%s\').", name
,
5268 LogMessage(L_INFO
, "Printer \'%s\' rejecting jobs (\'%s\').", name
,
5273 * Everything was ok, so return OK status...
5276 con
->response
->request
.status
.status_code
= IPP_OK
;
5281 * 'release_job()' - Release a held print job.
5285 release_job(client_t
*con
, /* I - Client connection */
5286 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
5288 ipp_attribute_t
*attr
; /* Current attribute */
5289 int jobid
; /* Job ID */
5290 char method
[HTTP_MAX_URI
],
5291 /* Method portion of URI */
5292 username
[HTTP_MAX_URI
],
5293 /* Username portion of URI */
5295 /* Host portion of URI */
5296 resource
[HTTP_MAX_URI
];
5297 /* Resource portion of URI */
5298 int port
; /* Port portion of URI */
5299 job_t
*job
; /* Job information */
5302 LogMessage(L_DEBUG2
, "release_job(%d, %s)\n", con
->http
.fd
,
5303 uri
->values
[0].string
.text
);
5306 * Verify that the POST operation was done to a valid URI.
5309 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
5310 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
5311 strncmp(con
->uri
, "/printers/", 10) != 0)
5313 LogMessage(L_ERROR
, "release_job: release request on bad resource \'%s\'!",
5315 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5320 * See if we have a job URI or a printer URI...
5323 if (strcmp(uri
->name
, "printer-uri") == 0)
5326 * Got a printer URI; see if we also have a job-id attribute...
5329 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
5331 LogMessage(L_ERROR
, "release_job: got a printer-uri attribute but no job-id!");
5332 send_ipp_error(con
, IPP_BAD_REQUEST
);
5336 jobid
= attr
->values
[0].integer
;
5341 * Got a job URI; parse it to get the job ID...
5344 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5346 if (strncmp(resource
, "/jobs/", 6) != 0)
5352 LogMessage(L_ERROR
, "release_job: bad job-uri attribute \'%s\'!",
5353 uri
->values
[0].string
.text
);
5354 send_ipp_error(con
, IPP_BAD_REQUEST
);
5358 jobid
= atoi(resource
+ 6);
5362 * See if the job exists...
5365 if ((job
= FindJob(jobid
)) == NULL
)
5368 * Nope - return a "not found" error...
5371 LogMessage(L_ERROR
, "release_job: job #%d doesn't exist!", jobid
);
5372 send_ipp_error(con
, IPP_NOT_FOUND
);
5377 * See if job is "held"...
5380 if (job
->state
->values
[0].integer
!= IPP_JOB_HELD
)
5383 * Nope - return a "not possible" error...
5386 LogMessage(L_ERROR
, "release_job: job #%d is not held!", jobid
);
5387 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
5392 * See if the job is owned by the requesting user...
5395 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
5397 LogMessage(L_ERROR
, "release_job: \"%s\" not authorized to release job id %d owned by \"%s\"!",
5398 username
, jobid
, job
->username
);
5399 send_ipp_error(con
, IPP_FORBIDDEN
);
5404 * Reset the job-hold-until value to "no-hold"...
5407 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
5408 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
5412 free(attr
->values
[0].string
.text
);
5413 attr
->value_tag
= IPP_TAG_KEYWORD
;
5414 attr
->values
[0].string
.text
= strdup("no-hold");
5418 * Release the job and return...
5423 LogMessage(L_INFO
, "Job %d was released by \'%s\'.", jobid
, username
);
5425 con
->response
->request
.status
.status_code
= IPP_OK
;
5430 * 'restart_job()' - Restart an old print job.
5434 restart_job(client_t
*con
, /* I - Client connection */
5435 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
5437 ipp_attribute_t
*attr
; /* Current attribute */
5438 int jobid
; /* Job ID */
5439 char method
[HTTP_MAX_URI
],
5440 /* Method portion of URI */
5441 username
[HTTP_MAX_URI
],
5442 /* Username portion of URI */
5444 /* Host portion of URI */
5445 resource
[HTTP_MAX_URI
];
5446 /* Resource portion of URI */
5447 int port
; /* Port portion of URI */
5448 job_t
*job
; /* Job information */
5451 LogMessage(L_DEBUG2
, "restart_job(%d, %s)\n", con
->http
.fd
,
5452 uri
->values
[0].string
.text
);
5455 * Verify that the POST operation was done to a valid URI.
5458 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
5459 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
5460 strncmp(con
->uri
, "/printers/", 10) != 0)
5462 LogMessage(L_ERROR
, "restart_job: restart request on bad resource \'%s\'!",
5464 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5469 * See if we have a job URI or a printer URI...
5472 if (strcmp(uri
->name
, "printer-uri") == 0)
5475 * Got a printer URI; see if we also have a job-id attribute...
5478 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
5480 LogMessage(L_ERROR
, "restart_job: got a printer-uri attribute but no job-id!");
5481 send_ipp_error(con
, IPP_BAD_REQUEST
);
5485 jobid
= attr
->values
[0].integer
;
5490 * Got a job URI; parse it to get the job ID...
5493 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5495 if (strncmp(resource
, "/jobs/", 6) != 0)
5501 LogMessage(L_ERROR
, "restart_job: bad job-uri attribute \'%s\'!",
5502 uri
->values
[0].string
.text
);
5503 send_ipp_error(con
, IPP_BAD_REQUEST
);
5507 jobid
= atoi(resource
+ 6);
5511 * See if the job exists...
5514 if ((job
= FindJob(jobid
)) == NULL
)
5517 * Nope - return a "not found" error...
5520 LogMessage(L_ERROR
, "restart_job: job #%d doesn't exist!", jobid
);
5521 send_ipp_error(con
, IPP_NOT_FOUND
);
5526 * See if job is in any of the "completed" states...
5529 if (job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
)
5532 * Nope - return a "not possible" error...
5535 LogMessage(L_ERROR
, "restart_job: job #%d is not complete!", jobid
);
5536 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
5541 * See if we have retained the job files...
5544 if (!JobFiles
&& job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
5547 * Nope - return a "not possible" error...
5550 LogMessage(L_ERROR
, "restart_job: job #%d cannot be restarted - no files!", jobid
);
5551 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
5556 * See if the job is owned by the requesting user...
5559 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
5561 LogMessage(L_ERROR
, "restart_job: \"%s\" not authorized to restart job id %d owned by \"%s\"!",
5562 username
, jobid
, job
->username
);
5563 send_ipp_error(con
, IPP_FORBIDDEN
);
5568 * Restart the job and return...
5573 LogMessage(L_INFO
, "Job %d was restarted by \'%s\'.", jobid
, username
);
5575 con
->response
->request
.status
.status_code
= IPP_OK
;
5580 * 'send_document()' - Send a file to a printer or class.
5584 send_document(client_t
*con
, /* I - Client connection */
5585 ipp_attribute_t
*uri
) /* I - Printer URI */
5587 ipp_attribute_t
*attr
; /* Current attribute */
5588 ipp_attribute_t
*format
; /* Document-format attribute */
5589 int jobid
; /* Job ID number */
5590 job_t
*job
; /* Current job */
5591 char job_uri
[HTTP_MAX_URI
],
5593 method
[HTTP_MAX_URI
],
5594 /* Method portion of URI */
5595 username
[HTTP_MAX_URI
],
5596 /* Username portion of URI */
5598 /* Host portion of URI */
5599 resource
[HTTP_MAX_URI
];
5600 /* Resource portion of URI */
5601 int port
; /* Port portion of URI */
5602 mime_type_t
*filetype
; /* Type of file */
5603 char super
[MIME_MAX_SUPER
],
5604 /* Supertype of file */
5605 type
[MIME_MAX_TYPE
],
5606 /* Subtype of file */
5607 mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
5608 /* Textual name of mime type */
5609 char filename
[1024]; /* Job filename */
5610 printer_t
*printer
; /* Current printer */
5611 struct stat fileinfo
; /* File information */
5612 int kbytes
; /* Size of file */
5613 int compression
; /* Type of compression */
5616 LogMessage(L_DEBUG2
, "send_document(%d, %s)\n", con
->http
.fd
,
5617 uri
->values
[0].string
.text
);
5620 * Verify that the POST operation was done to a valid URI.
5623 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
5624 strncmp(con
->uri
, "/jobs/", 6) != 0 &&
5625 strncmp(con
->uri
, "/printers/", 10) != 0)
5627 LogMessage(L_ERROR
, "send_document: print request on bad resource \'%s\'!",
5629 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5634 * See if we have a job URI or a printer URI...
5637 if (strcmp(uri
->name
, "printer-uri") == 0)
5640 * Got a printer URI; see if we also have a job-id attribute...
5643 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
5645 LogMessage(L_ERROR
, "send_document: got a printer-uri attribute but no job-id!");
5646 send_ipp_error(con
, IPP_BAD_REQUEST
);
5650 jobid
= attr
->values
[0].integer
;
5655 * Got a job URI; parse it to get the job ID...
5658 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
5660 if (strncmp(resource
, "/jobs/", 6) != 0)
5666 LogMessage(L_ERROR
, "send_document: bad job-uri attribute \'%s\'!",
5667 uri
->values
[0].string
.text
);
5668 send_ipp_error(con
, IPP_BAD_REQUEST
);
5672 jobid
= atoi(resource
+ 6);
5676 * See if the job exists...
5679 if ((job
= FindJob(jobid
)) == NULL
)
5682 * Nope - return a "not found" error...
5685 LogMessage(L_ERROR
, "send_document: job #%d doesn't exist!", jobid
);
5686 send_ipp_error(con
, IPP_NOT_FOUND
);
5691 * See if the job is owned by the requesting user...
5694 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
5696 LogMessage(L_ERROR
, "send_document: \"%s\" not authorized to send document for job id %d owned by \"%s\"!",
5697 username
, jobid
, job
->username
);
5698 send_ipp_error(con
, IPP_FORBIDDEN
);
5703 * OK, see if the client is sending the document compressed - CUPS
5704 * only supports "none" and "gzip".
5707 compression
= CUPS_FILE_NONE
;
5709 if ((attr
= ippFindAttribute(con
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
)
5711 if (strcmp(attr
->values
[0].string
.text
, "none")
5713 && strcmp(attr
->values
[0].string
.text
, "gzip")
5714 #endif /* HAVE_LIBZ */
5717 LogMessage(L_ERROR
, "print_job: Unsupported compression \"%s\"!",
5718 attr
->values
[0].string
.text
);
5719 send_ipp_error(con
, IPP_ATTRIBUTES
);
5720 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
5721 "compression", NULL
, attr
->values
[0].string
.text
);
5726 if (!strcmp(attr
->values
[0].string
.text
, "gzip"))
5727 compression
= CUPS_FILE_GZIP
;
5728 #endif /* HAVE_LIBZ */
5732 * Do we have a file to print?
5737 LogMessage(L_ERROR
, "send_document: No file!?!");
5738 send_ipp_error(con
, IPP_BAD_REQUEST
);
5743 * Is it a format we support?
5746 if ((format
= ippFindAttribute(con
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
5749 * Grab format from client...
5752 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
, type
) != 2)
5754 LogMessage(L_ERROR
, "send_document: could not scan type \'%s\'!",
5755 format
->values
[0].string
.text
);
5756 send_ipp_error(con
, IPP_BAD_REQUEST
);
5763 * No document format attribute? Auto-type it!
5766 strcpy(super
, "application");
5767 strcpy(type
, "octet-stream");
5770 if (strcmp(super
, "application") == 0 &&
5771 strcmp(type
, "octet-stream") == 0)
5774 * Auto-type the file...
5777 LogMessage(L_DEBUG
, "send_document: auto-typing file...");
5779 filetype
= mimeFileType(MimeDatabase
, con
->filename
, &compression
);
5781 if (filetype
!= NULL
)
5784 * Replace the document-format attribute value with the auto-typed one.
5787 snprintf(mimetype
, sizeof(mimetype
), "%s/%s", filetype
->super
,
5792 free(format
->values
[0].string
.text
);
5793 format
->values
[0].string
.text
= strdup(mimetype
);
5796 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
5797 "document-format", NULL
, mimetype
);
5800 filetype
= mimeType(MimeDatabase
, super
, type
);
5803 filetype
= mimeType(MimeDatabase
, super
, type
);
5805 if (filetype
== NULL
)
5807 LogMessage(L_ERROR
, "send_document: Unsupported format \'%s/%s\'!",
5809 LogMessage(L_INFO
, "Hint: Do you have the raw file printing rules enabled?");
5810 send_ipp_error(con
, IPP_DOCUMENT_FORMAT
);
5813 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
5814 "document-format", NULL
, format
->values
[0].string
.text
);
5819 LogMessage(L_DEBUG
, "send_document: request file type is %s/%s.",
5820 filetype
->super
, filetype
->type
);
5823 * Add the file to the job...
5826 if (add_file(con
, job
, filetype
, compression
))
5829 if (job
->dtype
& CUPS_PRINTER_CLASS
)
5830 printer
= FindClass(job
->dest
);
5832 printer
= FindPrinter(job
->dest
);
5834 if (stat(con
->filename
, &fileinfo
))
5837 kbytes
= (fileinfo
.st_size
+ 1023) / 1024;
5839 UpdateQuota(printer
, job
->username
, 0, kbytes
);
5841 if ((attr
= ippFindAttribute(job
->attrs
, "job-k-octets", IPP_TAG_INTEGER
)) != NULL
)
5842 attr
->values
[0].integer
+= kbytes
;
5844 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
, job
->id
,
5846 rename(con
->filename
, filename
);
5848 ClearString(&con
->filename
);
5850 LogMessage(L_INFO
, "File of type %s/%s queued in job #%d by \'%s\'.",
5851 filetype
->super
, filetype
->type
, job
->id
, job
->username
);
5854 * Start the job if this is the last document...
5857 if ((attr
= ippFindAttribute(con
->request
, "last-document", IPP_TAG_BOOLEAN
)) != NULL
&&
5858 attr
->values
[0].boolean
)
5861 * See if we need to add the ending sheet...
5864 if (printer
!= NULL
&&
5865 !(printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)) &&
5866 (attr
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
&&
5867 attr
->num_values
> 1)
5873 LogMessage(L_INFO
, "Adding end banner page \"%s\" to job %d.",
5874 attr
->values
[1].string
.text
, job
->id
);
5876 kbytes
= copy_banner(con
, job
, attr
->values
[1].string
.text
);
5878 UpdateQuota(printer
, job
->username
, 0, kbytes
);
5881 if (job
->state
->values
[0].integer
== IPP_JOB_STOPPED
)
5882 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
5883 else if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
5885 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
5886 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
5888 if (attr
== NULL
|| strcmp(attr
->values
[0].string
.text
, "no-hold") == 0)
5889 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
5897 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
5898 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
5900 if (attr
== NULL
|| strcmp(attr
->values
[0].string
.text
, "no-hold") == 0)
5902 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
5903 job
->hold_until
= time(NULL
) + 60;
5909 * Fill in the response info...
5913 if (con
->http
.hostaddr
.addr
.sa_family
== AF_INET6
)
5914 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
5915 ntohs(con
->http
.hostaddr
.ipv6
.sin6_port
), job
->id
);
5917 #endif /* AF_INET6 */
5918 snprintf(job_uri
, sizeof(job_uri
), "http://%s:%d/jobs/%d", ServerName
,
5919 ntohs(con
->http
.hostaddr
.ipv4
.sin_port
), job
->id
);
5921 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
,
5924 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
5926 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
5927 job
->state
->values
[0].integer
);
5928 add_job_state_reasons(con
, job
);
5930 con
->response
->request
.status
.status_code
= IPP_OK
;
5935 * 'send_ipp_error()' - Send an error status back to the IPP client.
5939 send_ipp_error(client_t
*con
, /* I - Client connection */
5940 ipp_status_t status
) /* I - IPP status code */
5942 LogMessage(L_DEBUG2
, "send_ipp_error(%d, %x)\n", con
->http
.fd
, status
);
5944 LogMessage(L_DEBUG
, "Sending error: %s", ippErrorString(status
));
5946 con
->response
->request
.status
.status_code
= status
;
5948 if (ippFindAttribute(con
->response
, "attributes-charset", IPP_TAG_ZERO
) == NULL
)
5949 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
5950 "attributes-charset", NULL
, DefaultCharset
);
5952 if (ippFindAttribute(con
->response
, "attributes-natural-language",
5953 IPP_TAG_ZERO
) == NULL
)
5954 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
5955 "attributes-natural-language", NULL
, DefaultLanguage
);
5960 * 'set_default()' - Set the default destination...
5964 set_default(client_t
*con
, /* I - Client connection */
5965 ipp_attribute_t
*uri
) /* I - Printer URI */
5967 cups_ptype_t dtype
; /* Destination type (printer or class) */
5968 char method
[HTTP_MAX_URI
],
5969 /* Method portion of URI */
5970 username
[HTTP_MAX_URI
],
5971 /* Username portion of URI */
5973 /* Host portion of URI */
5974 resource
[HTTP_MAX_URI
];
5975 /* Resource portion of URI */
5976 int port
; /* Port portion of URI */
5977 const char *name
; /* Printer name */
5980 LogMessage(L_DEBUG2
, "set_default(%d, %s)\n", con
->http
.fd
,
5981 uri
->values
[0].string
.text
);
5984 * Was this operation called from the correct URI?
5987 if (strncmp(con
->uri
, "/admin/", 7) != 0)
5989 LogMessage(L_ERROR
, "set_default: admin request on bad resource \'%s\'!",
5991 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
5996 * Is the destination valid?
5999 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6001 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
6007 LogMessage(L_ERROR
, "set_default: resource name \'%s\' no good!", resource
);
6008 send_ipp_error(con
, IPP_NOT_FOUND
);
6013 * Set it as the default...
6016 if (dtype
& CUPS_PRINTER_CLASS
)
6017 DefaultPrinter
= FindClass(name
);
6019 DefaultPrinter
= FindPrinter(name
);
6024 LogMessage(L_INFO
, "Default destination set to \'%s\' by \'%s\'.", name
,
6028 * Everything was ok, so return OK status...
6031 con
->response
->request
.status
.status_code
= IPP_OK
;
6036 * 'set_job_attrs()' - Set job attributes.
6040 set_job_attrs(client_t
*con
, /* I - Client connection */
6041 ipp_attribute_t
*uri
) /* I - Job URI */
6043 ipp_attribute_t
*attr
, /* Current attribute */
6044 *attr2
, /* Job attribute */
6045 *prev2
; /* Previous job attribute */
6046 int jobid
; /* Job ID */
6047 job_t
*job
; /* Current job */
6048 char method
[HTTP_MAX_URI
],
6049 /* Method portion of URI */
6050 username
[HTTP_MAX_URI
],
6051 /* Username portion of URI */
6053 /* Host portion of URI */
6054 resource
[HTTP_MAX_URI
];
6055 /* Resource portion of URI */
6056 int port
; /* Port portion of URI */
6059 LogMessage(L_DEBUG2
, "set_job_attrs(%d, %s)\n", con
->http
.fd
,
6060 uri
->values
[0].string
.text
);
6063 * Start with "everything is OK" status...
6066 con
->response
->request
.status
.status_code
= IPP_OK
;
6069 * See if we have a job URI or a printer URI...
6072 if (strcmp(uri
->name
, "printer-uri") == 0)
6075 * Got a printer URI; see if we also have a job-id attribute...
6078 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
6080 LogMessage(L_ERROR
, "set_job_attrs: got a printer-uri attribute but no job-id!");
6081 send_ipp_error(con
, IPP_BAD_REQUEST
);
6085 jobid
= attr
->values
[0].integer
;
6090 * Got a job URI; parse it to get the job ID...
6093 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6095 if (strncmp(resource
, "/jobs/", 6) != 0)
6101 LogMessage(L_ERROR
, "set_job_attrs: bad job-uri attribute \'%s\'!\n",
6102 uri
->values
[0].string
.text
);
6103 send_ipp_error(con
, IPP_BAD_REQUEST
);
6107 jobid
= atoi(resource
+ 6);
6111 * See if the job exists...
6114 if ((job
= FindJob(jobid
)) == NULL
)
6117 * Nope - return a "not found" error...
6120 LogMessage(L_ERROR
, "set_job_attrs: job #%d doesn't exist!", jobid
);
6121 send_ipp_error(con
, IPP_NOT_FOUND
);
6126 * See if the job has been completed...
6129 if (job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
6132 * Return a "not-possible" error...
6135 LogMessage(L_ERROR
, "set_job_attrs: job #%d is finished and cannot be altered!", jobid
);
6136 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
6141 * See if the job is owned by the requesting user...
6144 if (!validate_user(con
, job
->username
, username
, sizeof(username
)))
6146 LogMessage(L_ERROR
, "set_job_attrs: \"%s\" not authorized to alter job id %d owned by \"%s\"!",
6147 username
, jobid
, job
->username
);
6148 send_ipp_error(con
, IPP_FORBIDDEN
);
6153 * See what the user wants to change.
6156 for (attr
= con
->request
->attrs
; attr
!= NULL
; attr
= attr
->next
)
6158 if (attr
->group_tag
!= IPP_TAG_JOB
|| !attr
->name
)
6161 if (!strcmp(attr
->name
, "attributes-charset") ||
6162 !strcmp(attr
->name
, "attributes-natural-language") ||
6163 !strcmp(attr
->name
, "document-compression") ||
6164 !strcmp(attr
->name
, "document-format") ||
6165 !strcmp(attr
->name
, "job-detailed-status-messages") ||
6166 !strcmp(attr
->name
, "job-document-access-errors") ||
6167 !strcmp(attr
->name
, "job-id") ||
6168 !strcmp(attr
->name
, "job-k-octets") ||
6169 !strcmp(attr
->name
, "job-originating-host-name") ||
6170 !strcmp(attr
->name
, "job-originating-user-name") ||
6171 !strcmp(attr
->name
, "job-printer-up-time") ||
6172 !strcmp(attr
->name
, "job-printer-uri") ||
6173 !strcmp(attr
->name
, "job-sheets") ||
6174 !strcmp(attr
->name
, "job-state-message") ||
6175 !strcmp(attr
->name
, "job-state-reasons") ||
6176 !strcmp(attr
->name
, "job-uri") ||
6177 !strcmp(attr
->name
, "number-of-documents") ||
6178 !strcmp(attr
->name
, "number-of-intervening-jobs") ||
6179 !strcmp(attr
->name
, "output-device-assigned") ||
6180 !strncmp(attr
->name
, "date-time-at-", 13) ||
6181 !strncmp(attr
->name
, "job-impressions", 15) ||
6182 !strncmp(attr
->name
, "job-k-octets", 12) ||
6183 !strncmp(attr
->name
, "job-media-sheets", 16) ||
6184 !strncmp(attr
->name
, "time-at-", 8))
6190 send_ipp_error(con
, IPP_ATTRIBUTES_NOT_SETTABLE
);
6192 if ((attr2
= copy_attribute(con
->response
, attr
, 0)) != NULL
)
6193 attr2
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
6198 if (!strcmp(attr
->name
, "job-priority"))
6201 * Change the job priority...
6204 if (attr
->value_tag
!= IPP_TAG_INTEGER
)
6206 send_ipp_error(con
, IPP_REQUEST_VALUE
);
6208 if ((attr2
= copy_attribute(con
->response
, attr
, 0)) != NULL
)
6209 attr2
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
6211 else if (job
->state
->values
[0].integer
>= IPP_JOB_PROCESSING
)
6213 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
6216 else if (con
->response
->request
.status
.status_code
== IPP_OK
)
6217 SetJobPriority(jobid
, attr
->values
[0].integer
);
6219 else if (!strcmp(attr
->name
, "job-state"))
6222 * Change the job state...
6225 if (attr
->value_tag
!= IPP_TAG_ENUM
)
6227 send_ipp_error(con
, IPP_REQUEST_VALUE
);
6229 if ((attr2
= copy_attribute(con
->response
, attr
, 0)) != NULL
)
6230 attr2
->group_tag
= IPP_TAG_UNSUPPORTED_GROUP
;
6234 switch (attr
->values
[0].integer
)
6236 case IPP_JOB_PENDING
:
6238 if (job
->state
->values
[0].integer
> IPP_JOB_HELD
)
6240 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
6243 else if (con
->response
->request
.status
.status_code
== IPP_OK
)
6244 job
->state
->values
[0].integer
= attr
->values
[0].integer
;
6247 case IPP_JOB_PROCESSING
:
6248 case IPP_JOB_STOPPED
:
6249 if (job
->state
->values
[0].integer
!= attr
->values
[0].integer
)
6251 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
6256 case IPP_JOB_CANCELLED
:
6257 case IPP_JOB_ABORTED
:
6258 case IPP_JOB_COMPLETED
:
6259 if (job
->state
->values
[0].integer
> IPP_JOB_PROCESSING
)
6261 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
6264 else if (con
->response
->request
.status
.status_code
== IPP_OK
)
6266 CancelJob(job
->id
, 0);
6270 job
->state
->values
[0].integer
= attr
->values
[0].integer
;
6278 else if (con
->response
->request
.status
.status_code
!= IPP_OK
)
6280 else if ((attr2
= ippFindAttribute(job
->attrs
, attr
->name
, IPP_TAG_ZERO
)) != NULL
)
6283 * Some other value; first free the old value...
6286 if (job
->attrs
->attrs
== attr2
)
6288 job
->attrs
->attrs
= attr2
->next
;
6293 for (prev2
= job
->attrs
->attrs
; prev2
!= NULL
; prev2
= prev2
->next
)
6294 if (prev2
->next
== attr2
)
6296 prev2
->next
= attr2
->next
;
6301 if (job
->attrs
->last
== attr2
)
6302 job
->attrs
->last
= prev2
;
6304 _ipp_free_attr(attr2
);
6307 * Then copy the attribute...
6310 copy_attribute(job
->attrs
, attr
, 0);
6313 * See if the job-name or job-hold-until is being changed.
6316 if (strcmp(attr
->name
, "job-hold-until") == 0)
6318 SetJobHoldUntil(job
->id
, attr
->values
[0].string
.text
);
6320 if (strcmp(attr
->values
[0].string
.text
, "no-hold") == 0)
6321 ReleaseJob(job
->id
);
6326 else if (attr
->value_tag
== IPP_TAG_DELETEATTR
)
6329 * Delete the attribute...
6332 for (attr2
= job
->attrs
->attrs
, prev2
= NULL
;
6334 prev2
= attr2
, attr2
= attr2
->next
)
6335 if (attr2
->name
&& strcmp(attr2
->name
, attr
->name
) == 0)
6341 prev2
->next
= attr2
->next
;
6343 job
->attrs
->attrs
= attr2
->next
;
6345 if (attr2
== job
->attrs
->last
)
6346 job
->attrs
->last
= prev2
;
6348 _ipp_free_attr(attr2
);
6354 * Add new option by copying it...
6357 copy_attribute(job
->attrs
, attr
, 0);
6368 * Start jobs if possible...
6376 * 'start_printer()' - Start a printer.
6380 start_printer(client_t
*con
, /* I - Client connection */
6381 ipp_attribute_t
*uri
) /* I - Printer URI */
6383 cups_ptype_t dtype
; /* Destination type (printer or class) */
6384 char method
[HTTP_MAX_URI
],
6385 /* Method portion of URI */
6386 username
[HTTP_MAX_URI
],
6387 /* Username portion of URI */
6389 /* Host portion of URI */
6390 resource
[HTTP_MAX_URI
];
6391 /* Resource portion of URI */
6392 int port
; /* Port portion of URI */
6393 const char *name
; /* Printer name */
6394 printer_t
*printer
; /* Printer data */
6397 LogMessage(L_DEBUG2
, "start_printer(%d, %s)\n", con
->http
.fd
,
6398 uri
->values
[0].string
.text
);
6401 * Was this operation called from the correct URI?
6404 if (strncmp(con
->uri
, "/admin/", 7) != 0)
6406 LogMessage(L_ERROR
, "start_printer: admin request on bad resource \'%s\'!",
6408 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
6413 * Is the destination valid?
6416 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6418 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
6424 LogMessage(L_ERROR
, "start_printer: resource name \'%s\' no good!", resource
);
6425 send_ipp_error(con
, IPP_NOT_FOUND
);
6430 * Start the printer...
6433 if (dtype
& CUPS_PRINTER_CLASS
)
6434 printer
= FindClass(name
);
6436 printer
= FindPrinter(name
);
6438 printer
->state_message
[0] = '\0';
6440 StartPrinter(printer
, 1);
6442 if (dtype
& CUPS_PRINTER_CLASS
)
6443 LogMessage(L_INFO
, "Class \'%s\' started by \'%s\'.", name
,
6445 LogMessage(L_INFO
, "Printer \'%s\' started by \'%s\'.", name
,
6451 * Everything was ok, so return OK status...
6454 con
->response
->request
.status
.status_code
= IPP_OK
;
6459 * 'stop_printer()' - Stop a printer.
6463 stop_printer(client_t
*con
, /* I - Client connection */
6464 ipp_attribute_t
*uri
) /* I - Printer URI */
6466 cups_ptype_t dtype
; /* Destination type (printer or class) */
6467 char method
[HTTP_MAX_URI
],
6468 /* Method portion of URI */
6469 username
[HTTP_MAX_URI
],
6470 /* Username portion of URI */
6472 /* Host portion of URI */
6473 resource
[HTTP_MAX_URI
];
6474 /* Resource portion of URI */
6475 int port
; /* Port portion of URI */
6476 const char *name
; /* Printer name */
6477 printer_t
*printer
; /* Printer data */
6478 ipp_attribute_t
*attr
; /* printer-state-message attribute */
6481 LogMessage(L_DEBUG2
, "stop_printer(%d, %s)\n", con
->http
.fd
,
6482 uri
->values
[0].string
.text
);
6485 * Was this operation called from the correct URI?
6488 if (strncmp(con
->uri
, "/admin/", 7) != 0)
6490 LogMessage(L_ERROR
, "stop_printer: admin request on bad resource \'%s\'!",
6492 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
6497 * Is the destination valid?
6500 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6502 if ((name
= ValidateDest(host
, resource
, &dtype
)) == NULL
)
6508 LogMessage(L_ERROR
, "stop_printer: resource name \'%s\' no good!", resource
);
6509 send_ipp_error(con
, IPP_NOT_FOUND
);
6514 * Stop the printer...
6517 if (dtype
& CUPS_PRINTER_CLASS
)
6518 printer
= FindClass(name
);
6520 printer
= FindPrinter(name
);
6522 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
6523 IPP_TAG_TEXT
)) == NULL
)
6524 strcpy(printer
->state_message
, "Paused");
6527 strlcpy(printer
->state_message
, attr
->values
[0].string
.text
,
6528 sizeof(printer
->state_message
));
6531 StopPrinter(printer
, 1);
6533 if (dtype
& CUPS_PRINTER_CLASS
)
6534 LogMessage(L_INFO
, "Class \'%s\' stopped by \'%s\'.", name
,
6537 LogMessage(L_INFO
, "Printer \'%s\' stopped by \'%s\'.", name
,
6541 * Everything was ok, so return OK status...
6544 con
->response
->request
.status
.status_code
= IPP_OK
;
6549 * 'validate_job()' - Validate printer options and destination.
6553 validate_job(client_t
*con
, /* I - Client connection */
6554 ipp_attribute_t
*uri
) /* I - Printer URI */
6556 ipp_attribute_t
*attr
; /* Current attribute */
6557 ipp_attribute_t
*format
; /* Document-format attribute */
6558 cups_ptype_t dtype
; /* Destination type (printer or class) */
6559 char method
[HTTP_MAX_URI
],
6560 /* Method portion of URI */
6561 username
[HTTP_MAX_URI
],
6562 /* Username portion of URI */
6564 /* Host portion of URI */
6565 resource
[HTTP_MAX_URI
];
6566 /* Resource portion of URI */
6567 int port
; /* Port portion of URI */
6568 char super
[MIME_MAX_SUPER
],
6569 /* Supertype of file */
6570 type
[MIME_MAX_TYPE
];
6571 /* Subtype of file */
6574 LogMessage(L_DEBUG2
, "validate_job(%d, %s)\n", con
->http
.fd
,
6575 uri
->values
[0].string
.text
);
6578 * Verify that the POST operation was done to a valid URI.
6581 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
6582 strncmp(con
->uri
, "/printers/", 10) != 0)
6584 LogMessage(L_ERROR
, "validate_job: request on bad resource \'%s\'!",
6586 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
6591 * OK, see if the client is sending the document compressed - CUPS
6592 * doesn't support compression yet...
6595 if ((attr
= ippFindAttribute(con
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
&&
6596 strcmp(attr
->values
[0].string
.text
, "none") == 0)
6598 LogMessage(L_ERROR
, "validate_job: Unsupported compression attribute %s!",
6599 attr
->values
[0].string
.text
);
6600 send_ipp_error(con
, IPP_ATTRIBUTES
);
6601 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
6602 "compression", NULL
, attr
->values
[0].string
.text
);
6607 * Is it a format we support?
6610 if ((format
= ippFindAttribute(con
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
6612 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
, type
) != 2)
6614 LogMessage(L_ERROR
, "validate_job: could not scan type \'%s\'!\n",
6615 format
->values
[0].string
.text
);
6616 send_ipp_error(con
, IPP_BAD_REQUEST
);
6620 if ((strcmp(super
, "application") != 0 ||
6621 strcmp(type
, "octet-stream") != 0) &&
6622 mimeType(MimeDatabase
, super
, type
) == NULL
)
6624 LogMessage(L_ERROR
, "validate_job: Unsupported format \'%s\'!\n",
6625 format
->values
[0].string
.text
);
6626 LogMessage(L_INFO
, "Hint: Do you have the raw file printing rules enabled?");
6627 send_ipp_error(con
, IPP_DOCUMENT_FORMAT
);
6628 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
6629 "document-format", NULL
, format
->values
[0].string
.text
);
6635 * Is the destination valid?
6638 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
6640 if (ValidateDest(host
, resource
, &dtype
) == NULL
)
6646 LogMessage(L_ERROR
, "validate_job: resource name \'%s\' no good!", resource
);
6647 send_ipp_error(con
, IPP_NOT_FOUND
);
6652 * Everything was ok, so return OK status...
6655 con
->response
->request
.status
.status_code
= IPP_OK
;
6660 * 'validate_user()' - Validate the user for the request.
6663 static int /* O - 1 if permitted, 0 otherwise */
6664 validate_user(client_t
*con
, /* I - Client connection */
6665 const char *owner
, /* I - Owner of job/resource */
6666 char *username
, /* O - Authenticated username */
6667 int userlen
) /* I - Length of username */
6669 int i
, j
; /* Looping vars */
6670 ipp_attribute_t
*attr
; /* requesting-user-name attribute */
6671 struct passwd
*user
; /* User info */
6672 struct group
*group
; /* System group info */
6673 char junk
[33]; /* MD5 password (not used) */
6676 LogMessage(L_DEBUG2
, "validate_user(%d, %s, %s, %d)\n", con
->http
.fd
,
6677 owner
, username
, userlen
);
6683 if (con
== NULL
|| owner
== NULL
|| username
== NULL
|| userlen
<= 0)
6687 * Get the best authenticated username that is available.
6690 if (con
->username
[0])
6691 strlcpy(username
, con
->username
, userlen
);
6692 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
6693 strlcpy(username
, attr
->values
[0].string
.text
, userlen
);
6695 strlcpy(username
, "anonymous", userlen
);
6698 * Check the username against the owner...
6701 if (strcasecmp(username
, owner
) != 0 && strcasecmp(username
, "root") != 0)
6704 * Not the owner or root; check to see if the user is a member of the
6708 user
= getpwnam(username
);
6711 for (i
= 0, j
= 0, group
= NULL
; i
< NumSystemGroups
; i
++)
6713 group
= getgrnam(SystemGroups
[i
]);
6718 for (j
= 0; group
->gr_mem
[j
]; j
++)
6719 if (strcasecmp(username
, group
->gr_mem
[j
]) == 0)
6722 if (group
->gr_mem
[j
])
6729 if (user
== NULL
|| group
== NULL
||
6730 (group
->gr_mem
[j
] == NULL
&& group
->gr_gid
!= user
->pw_gid
))
6733 * Username not found, group not found, or user is not part of the
6734 * system group... Check for a user and group in the MD5 password
6738 for (i
= 0; i
< NumSystemGroups
; i
++)
6739 if (GetMD5Passwd(username
, SystemGroups
[i
], junk
) != NULL
)
6743 * Nope, not an MD5 user, either. Return 0 indicating no-go...
6755 * End of "$Id: ipp.c,v 1.127.2.71 2003/07/20 12:51:47 mike Exp $".