2 * "$Id: ipp.c,v 1.59 2000/03/21 04:03:34 mike Exp $"
4 * IPP routines for the Common UNIX Printing System (CUPS) scheduler.
6 * Copyright 1997-2000 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_job_state_reasons() - Add the "job-state-reasons" attribute based
30 * upon the job and printer state...
31 * add_printer() - Add a printer to the system.
32 * add_printer_state_reasons() - Add the "printer-state-reasons" attribute
33 * based upon the printer state...
34 * add_queued_job_count() - Add the "queued-job-count" attribute for
35 * cancel_all_jobs() - Cancel all print jobs.
36 * cancel_job() - Cancel a print job.
37 * copy_attrs() - Copy attributes from one request to another.
38 * create_job() - Print a file to a printer or class.
39 * copy_file() - Copy a PPD file or interface script...
40 * delete_printer() - Remove a printer or class from the system.
41 * get_default() - Get the default destination.
42 * get_devices() - Get the list of available devices on the
44 * get_jobs() - Get a list of jobs for the specified printer.
45 * get_job_attrs() - Get job attributes.
46 * get_ppds() - Get the list of PPD files on the local
48 * get_printer_attrs() - Get printer attributes.
49 * get_printers() - Get a list of printers.
50 * hold_job() - Hold a print job.
51 * print_job() - Print a file to a printer or class.
52 * reject_jobs() - Reject print jobs to a printer.
53 * release_job() - Release a held print job.
54 * restart_job() - Restart an old print job.
55 * send_document() - Send a file to a printer or class.
56 * send_ipp_error() - Send an error status back to the IPP client.
57 * set_default() - Set the default destination...
58 * set_job_attrs() - Set job attributes.
59 * start_printer() - Start a printer.
60 * stop_printer() - Stop a printer.
61 * validate_job() - Validate printer options and destination.
65 * Include necessary headers...
73 #endif /* HAVE_LIBZ */
80 static void accept_jobs(client_t
*con
, ipp_attribute_t
*uri
);
81 static void add_class(client_t
*con
, ipp_attribute_t
*uri
);
82 static void add_job_state_reasons(client_t
*con
, job_t
*job
);
83 static void add_printer(client_t
*con
, ipp_attribute_t
*uri
);
84 static void add_printer_state_reasons(client_t
*con
, printer_t
*p
);
85 static void add_queued_job_count(client_t
*con
, printer_t
*p
);
86 static void cancel_all_jobs(client_t
*con
, ipp_attribute_t
*uri
);
87 static void cancel_job(client_t
*con
, ipp_attribute_t
*uri
);
88 static void copy_attrs(ipp_t
*to
, ipp_t
*from
, ipp_attribute_t
*req
,
90 static int copy_file(const char *from
, const char *to
);
91 static void create_job(client_t
*con
, ipp_attribute_t
*uri
);
92 static void delete_printer(client_t
*con
, ipp_attribute_t
*uri
);
93 static void get_default(client_t
*con
);
94 static void get_devices(client_t
*con
);
95 static void get_jobs(client_t
*con
, ipp_attribute_t
*uri
);
96 static void get_job_attrs(client_t
*con
, ipp_attribute_t
*uri
);
97 static void get_ppds(client_t
*con
);
98 static void get_printers(client_t
*con
, int type
);
99 static void get_printer_attrs(client_t
*con
, ipp_attribute_t
*uri
);
100 static void hold_job(client_t
*con
, ipp_attribute_t
*uri
);
101 static void print_job(client_t
*con
, ipp_attribute_t
*uri
);
102 static void reject_jobs(client_t
*con
, ipp_attribute_t
*uri
);
103 static void release_job(client_t
*con
, ipp_attribute_t
*uri
);
104 static void restart_job(client_t
*con
, ipp_attribute_t
*uri
);
105 static void send_document(client_t
*con
, ipp_attribute_t
*uri
);
106 static void send_ipp_error(client_t
*con
, ipp_status_t status
);
107 static void set_default(client_t
*con
, ipp_attribute_t
*uri
);
108 static void set_job_attrs(client_t
*con
, ipp_attribute_t
*uri
);
109 static void start_printer(client_t
*con
, ipp_attribute_t
*uri
);
110 static void stop_printer(client_t
*con
, ipp_attribute_t
*uri
);
111 static void validate_job(client_t
*con
, ipp_attribute_t
*uri
);
115 * 'ProcessIPPRequest()' - Process an incoming IPP request...
119 ProcessIPPRequest(client_t
*con
) /* I - Client connection */
121 ipp_tag_t group
; /* Current group tag */
122 ipp_attribute_t
*attr
; /* Current attribute */
123 ipp_attribute_t
*charset
; /* Character set attribute */
124 ipp_attribute_t
*language
; /* Language attribute */
125 ipp_attribute_t
*uri
; /* Printer URI attribute */
128 DEBUG_printf(("ProcessIPPRequest(%08x)\n", con
));
129 DEBUG_printf(("ProcessIPPRequest: operation_id = %04x\n",
130 con
->request
->request
.op
.operation_id
));
133 * First build an empty response message for this request...
136 con
->response
= ippNew();
138 con
->response
->request
.status
.version
[0] = con
->request
->request
.op
.version
[0];
139 con
->response
->request
.status
.version
[1] = con
->request
->request
.op
.version
[1];
140 con
->response
->request
.status
.request_id
= con
->request
->request
.op
.request_id
;
143 * Then validate the request header and required attributes...
146 if (con
->request
->request
.any
.version
[0] != 1)
149 * Return an error, since we only support IPP 1.x.
152 send_ipp_error(con
, IPP_VERSION_NOT_SUPPORTED
);
157 * Make sure that the attributes are provided in the correct order and
158 * don't repeat groups...
161 for (attr
= con
->request
->attrs
, group
= attr
->group_tag
;
164 if (attr
->group_tag
< group
)
167 * Out of order; return an error...
170 LogMessage(L_ERROR
, "ProcessIPPRequest: attribute groups are out of order!");
171 send_ipp_error(con
, IPP_BAD_REQUEST
);
175 group
= attr
->group_tag
;
180 * Then make sure that the first three attributes are:
183 * attributes-natural-language
184 * printer-uri/job-uri
187 attr
= con
->request
->attrs
;
188 if (attr
!= NULL
&& strcmp(attr
->name
, "attributes-charset") == 0 &&
189 attr
->value_tag
== IPP_TAG_CHARSET
)
196 if (attr
!= NULL
&& strcmp(attr
->name
, "attributes-natural-language") == 0 &&
197 attr
->value_tag
== IPP_TAG_LANGUAGE
)
202 if ((attr
= ippFindAttribute(con
->request
, "printer-uri", IPP_TAG_URI
)) != NULL
)
204 else if ((attr
= ippFindAttribute(con
->request
, "job-uri", IPP_TAG_URI
)) != NULL
)
210 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
211 "attributes-charset", NULL
, charset
->values
[0].string
.text
);
213 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
214 "attributes-charset", NULL
, DefaultCharset
);
217 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
218 "attributes-natural-language", NULL
,
219 language
->values
[0].string
.text
);
221 ippAddString(con
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
222 "attributes-natural-language", NULL
, DefaultLanguage
);
224 if (charset
== NULL
|| language
== NULL
||
225 (uri
== NULL
&& con
->request
->request
.op
.operation_id
< IPP_PRIVATE
))
228 * Return an error, since attributes-charset,
229 * attributes-natural-language, and printer-uri/job-uri are required
230 * for all operations.
234 LogMessage(L_ERROR
, "ProcessIPPRequest: missing attributes-charset attribute!");
236 if (language
== NULL
)
237 LogMessage(L_ERROR
, "ProcessIPPRequest: missing attributes-natural-language attribute!");
240 LogMessage(L_ERROR
, "ProcessIPPRequest: missing printer-uri or job-uri attribute!");
242 send_ipp_error(con
, IPP_BAD_REQUEST
);
247 * OK, all the checks pass so far; try processing the operation...
250 switch (con
->request
->request
.op
.operation_id
)
256 case IPP_VALIDATE_JOB
:
257 validate_job(con
, uri
);
260 case IPP_CREATE_JOB
:
261 create_job(con
, uri
);
264 case IPP_SEND_DOCUMENT
:
265 send_document(con
, uri
);
268 case IPP_CANCEL_JOB
:
269 cancel_job(con
, uri
);
272 case IPP_GET_JOB_ATTRIBUTES
:
273 get_job_attrs(con
, uri
);
280 case IPP_GET_PRINTER_ATTRIBUTES
:
281 get_printer_attrs(con
, uri
);
288 case IPP_RELEASE_JOB
:
289 release_job(con
, uri
);
292 case IPP_RESTART_JOB
:
293 restart_job(con
, uri
);
296 case IPP_PAUSE_PRINTER
:
297 stop_printer(con
, uri
);
300 case IPP_RESUME_PRINTER
:
301 start_printer(con
, uri
);
304 case IPP_PURGE_JOBS
:
305 cancel_all_jobs(con
, uri
);
308 case IPP_SET_JOB_ATTRIBUTES
:
309 set_job_attrs(con
, uri
);
312 case CUPS_GET_DEFAULT
:
316 case CUPS_GET_PRINTERS
:
317 get_printers(con
, 0);
320 case CUPS_GET_CLASSES
:
321 get_printers(con
, CUPS_PRINTER_CLASS
);
324 case CUPS_ADD_PRINTER
:
325 add_printer(con
, uri
);
328 case CUPS_DELETE_PRINTER
:
329 delete_printer(con
, uri
);
332 case CUPS_ADD_CLASS
:
336 case CUPS_DELETE_CLASS
:
337 delete_printer(con
, uri
);
340 case CUPS_ACCEPT_JOBS
:
341 accept_jobs(con
, uri
);
344 case CUPS_REJECT_JOBS
:
345 reject_jobs(con
, uri
);
348 case CUPS_SET_DEFAULT
:
349 set_default(con
, uri
);
352 case CUPS_GET_DEVICES
:
361 send_ipp_error(con
, IPP_OPERATION_NOT_SUPPORTED
);
367 SendHeader(con
, HTTP_OK
, "application/ipp");
369 con
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
370 con
->http
.data_remaining
= ippLength(con
->response
);
372 httpPrintf(HTTP(con
), "Content-Length: %d\r\n\r\n",
373 con
->http
.data_remaining
);
375 FD_SET(con
->http
.fd
, &OutputSet
);
380 * 'accept_jobs()' - Accept print jobs to a printer.
384 accept_jobs(client_t
*con
, /* I - Client connection */
385 ipp_attribute_t
*uri
) /* I - Printer or class URI */
387 cups_ptype_t dtype
; /* Destination type (printer or class) */
388 char method
[HTTP_MAX_URI
],
389 /* Method portion of URI */
390 username
[HTTP_MAX_URI
],
391 /* Username portion of URI */
393 /* Host portion of URI */
394 resource
[HTTP_MAX_URI
];
395 /* Resource portion of URI */
396 int port
; /* Port portion of URI */
397 const char *name
; /* Printer name */
398 printer_t
*printer
; /* Printer data */
401 DEBUG_printf(("accept_jobs(%08x, %08x)\n", con
, uri
));
404 * Was this operation called from the correct URI?
407 if (strncmp(con
->uri
, "/admin/", 7) != 0)
409 LogMessage(L_ERROR
, "accept_jobs: admin request on bad resource \'%s\'!",
411 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
416 * Is the destination valid?
419 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
421 if ((name
= ValidateDest(resource
, &dtype
)) == NULL
)
427 LogMessage(L_ERROR
, "accept_jobs: resource name \'%s\' no good!", resource
);
428 send_ipp_error(con
, IPP_NOT_FOUND
);
433 * Accept jobs sent to the printer...
436 printer
= FindPrinter(name
);
437 printer
->accepting
= 1;
438 printer
->state_message
[0] = '\0';
440 if (dtype
== CUPS_PRINTER_CLASS
)
445 LogMessage(L_INFO
, "Printer \'%s\' now accepting jobs (\'%s\').", name
,
449 * Everything was ok, so return OK status...
452 con
->response
->request
.status
.status_code
= IPP_OK
;
457 * 'add_class()' - Add a class to the system.
461 add_class(client_t
*con
, /* I - Client connection */
462 ipp_attribute_t
*uri
) /* I - URI of class */
464 int i
; /* Looping var */
465 char method
[HTTP_MAX_URI
],
466 /* Method portion of URI */
467 username
[HTTP_MAX_URI
],
468 /* Username portion of URI */
470 /* Host portion of URI */
471 resource
[HTTP_MAX_URI
];
472 /* Resource portion of URI */
473 int port
; /* Port portion of URI */
474 printer_t
*pclass
; /* Class */
475 cups_ptype_t dtype
; /* Destination type */
476 const char *dest
; /* Printer or class name */
477 ipp_attribute_t
*attr
; /* Printer attribute */
481 * Was this operation called from the correct URI?
484 if (strncmp(con
->uri
, "/admin/", 7) != 0)
486 LogMessage(L_ERROR
, "add_class: admin request on bad resource \'%s\'!",
488 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
492 DEBUG_printf(("add_class(%08x, %08x)\n", con
, uri
));
495 * Do we have a valid URI?
498 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
500 if (strncmp(resource
, "/classes/", 9) != 0)
503 * No, return an error...
506 send_ipp_error(con
, IPP_BAD_REQUEST
);
511 * See if the class already exists; if not, create a new class...
514 if ((pclass
= FindClass(resource
+ 9)) == NULL
)
517 * Class doesn't exist; see if we have a printer of the same name...
520 if ((pclass
= FindPrinter(resource
+ 9)) != NULL
&&
521 !(pclass
->type
& CUPS_PRINTER_REMOTE
))
524 * Yes, return an error...
527 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
532 * No, add the pclass...
535 pclass
= AddClass(resource
+ 9);
539 * Look for attributes and copy them over as needed...
542 if ((attr
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
544 strncpy(pclass
->location
, attr
->values
[0].string
.text
, sizeof(pclass
->location
) - 1);
545 pclass
->location
[sizeof(pclass
->location
) - 1] = '\0';
548 if ((attr
= ippFindAttribute(con
->request
, "printer-info", IPP_TAG_TEXT
)) != NULL
)
550 strncpy(pclass
->info
, attr
->values
[0].string
.text
, sizeof(pclass
->info
) - 1);
551 pclass
->info
[sizeof(pclass
->info
) - 1] = '\0';
554 if ((attr
= ippFindAttribute(con
->request
, "printer-more-info", IPP_TAG_URI
)) != NULL
)
556 strncpy(pclass
->more_info
, attr
->values
[0].string
.text
, sizeof(pclass
->more_info
) - 1);
557 pclass
->more_info
[sizeof(pclass
->more_info
) - 1] = '\0';
560 if ((attr
= ippFindAttribute(con
->request
, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN
)) != NULL
)
562 LogMessage(L_INFO
, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
563 pclass
->name
, attr
->values
[0].boolean
, pclass
->accepting
);
565 pclass
->accepting
= attr
->values
[0].boolean
;
567 if ((attr
= ippFindAttribute(con
->request
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
569 LogMessage(L_INFO
, "Setting %s printer-state to %d (was %d.)", pclass
->name
,
570 attr
->values
[0].integer
, pclass
->state
);
572 if (pclass
->state
== IPP_PRINTER_STOPPED
&&
573 attr
->values
[0].integer
!= IPP_PRINTER_STOPPED
)
574 pclass
->state
= IPP_PRINTER_IDLE
;
575 else if (pclass
->state
!= IPP_PRINTER_STOPPED
&&
576 attr
->values
[0].integer
== IPP_PRINTER_STOPPED
)
578 if (pclass
->state
== IPP_PRINTER_PROCESSING
)
579 StopJob(((job_t
*)pclass
->job
)->id
);
581 pclass
->state
= IPP_PRINTER_STOPPED
;
584 pclass
->browse_time
= 0;
586 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message", IPP_TAG_TEXT
)) != NULL
)
588 strncpy(pclass
->state_message
, attr
->values
[0].string
.text
,
589 sizeof(pclass
->state_message
) - 1);
590 pclass
->state_message
[sizeof(pclass
->state_message
) - 1] = '\0';
593 if ((attr
= ippFindAttribute(con
->request
, "member-uris", IPP_TAG_URI
)) != NULL
)
596 * Clear the printer array as needed...
599 if (pclass
->num_printers
> 0)
601 free(pclass
->printers
);
602 pclass
->num_printers
= 0;
606 * Add each printer or class that is listed...
609 for (i
= 0; i
< attr
->num_values
; i
++)
612 * Search for the printer or class URI...
615 httpSeparate(attr
->values
[i
].string
.text
, method
, username
, host
,
618 if ((dest
= ValidateDest(resource
, &dtype
)) == NULL
)
624 LogMessage(L_ERROR
, "add_class: resource name \'%s\' no good!", resource
);
625 send_ipp_error(con
, IPP_NOT_FOUND
);
630 * Add it to the class...
633 if (dtype
== CUPS_PRINTER_CLASS
)
634 AddPrinterToClass(pclass
, FindClass(dest
));
636 AddPrinterToClass(pclass
, FindPrinter(dest
));
641 * Update the printer class attributes and return...
644 SetPrinterAttrs(pclass
);
648 LogMessage(L_INFO
, "New class \'%s\' added by \'%s\'.", pclass
->name
,
651 con
->response
->request
.status
.status_code
= IPP_OK
;
656 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
657 * upon the job and printer state...
661 add_job_state_reasons(client_t
*con
, /* I - Client connection */
662 job_t
*job
) /* I - Job info */
664 printer_t
*dest
; /* Destination printer */
667 switch (job
->state
->values
[0].integer
)
669 case IPP_JOB_PENDING
:
670 if (job
->dtype
& CUPS_PRINTER_CLASS
)
671 dest
= FindClass(job
->dest
);
673 dest
= FindPrinter(job
->dest
);
675 if (dest
!= NULL
&& dest
->state
== IPP_PRINTER_STOPPED
)
676 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
677 "job-state-reasons", NULL
, "printer-stopped");
679 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
680 "job-state-reasons", NULL
, "none");
684 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
685 "job-state-reasons", NULL
, "job-hold-until-specified");
688 case IPP_JOB_PROCESSING
:
689 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
690 "job-state-reasons", NULL
, "job-printing");
693 case IPP_JOB_STOPPED
:
694 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
695 "job-state-reasons", NULL
, "job-stopped");
698 case IPP_JOB_CANCELLED
:
699 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
700 "job-state-reasons", NULL
, "job-canceled-by-user");
703 case IPP_JOB_ABORTED
:
704 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
705 "job-state-reasons", NULL
, "aborted-by-system");
708 case IPP_JOB_COMPLETED
:
709 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
710 "job-state-reasons", NULL
, "job-completed-successfully");
717 * 'add_printer()' - Add a printer to the system.
721 add_printer(client_t
*con
, /* I - Client connection */
722 ipp_attribute_t
*uri
) /* I - URI of printer */
724 char method
[HTTP_MAX_URI
],
725 /* Method portion of URI */
726 username
[HTTP_MAX_URI
],
727 /* Username portion of URI */
729 /* Host portion of URI */
730 resource
[HTTP_MAX_URI
];
731 /* Resource portion of URI */
732 int port
; /* Port portion of URI */
733 printer_t
*printer
; /* Printer/class */
734 ipp_attribute_t
*attr
; /* Printer attribute */
736 gzFile fp
; /* Script/PPD file */
738 FILE *fp
; /* Script/PPD file */
739 #endif /* HAVE_LIBZ */
740 char line
[1024]; /* Line from file... */
741 char srcfile
[1024], /* Source Script/PPD file */
742 dstfile
[1024]; /* Destination Script/PPD file */
746 * Was this operation called from the correct URI?
749 if (strncmp(con
->uri
, "/admin/", 7) != 0)
751 LogMessage(L_ERROR
, "add_printer: admin request on bad resource \'%s\'!",
753 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
757 DEBUG_printf(("add_printer(%08x, %08x)\n", con
, uri
));
760 * Do we have a valid URI?
763 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
765 if (strncmp(resource
, "/printers/", 10) != 0)
768 * No, return an error...
771 send_ipp_error(con
, IPP_BAD_REQUEST
);
776 * See if the printer already exists; if not, create a new printer...
779 if ((printer
= FindPrinter(resource
+ 10)) == NULL
)
782 * Printer doesn't exist; see if we have a class of the same name...
785 if ((printer
= FindClass(resource
+ 10)) != NULL
&&
786 !(printer
->type
& CUPS_PRINTER_REMOTE
))
789 * Yes, return an error...
792 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
797 * No, add the printer...
800 printer
= AddPrinter(resource
+ 10);
804 * Look for attributes and copy them over as needed...
807 if ((attr
= ippFindAttribute(con
->request
, "printer-location", IPP_TAG_TEXT
)) != NULL
)
809 strncpy(printer
->location
, attr
->values
[0].string
.text
, sizeof(printer
->location
) - 1);
810 printer
->location
[sizeof(printer
->location
) - 1] = '\0';
813 if ((attr
= ippFindAttribute(con
->request
, "printer-info", IPP_TAG_TEXT
)) != NULL
)
815 strncpy(printer
->info
, attr
->values
[0].string
.text
, sizeof(printer
->info
) - 1);
816 printer
->info
[sizeof(printer
->info
) - 1] = '\0';
819 if ((attr
= ippFindAttribute(con
->request
, "printer-more-info", IPP_TAG_URI
)) != NULL
)
821 strncpy(printer
->more_info
, attr
->values
[0].string
.text
, sizeof(printer
->more_info
) - 1);
822 printer
->more_info
[sizeof(printer
->more_info
) - 1] = '\0';
825 if ((attr
= ippFindAttribute(con
->request
, "device-uri", IPP_TAG_URI
)) != NULL
)
827 LogMessage(L_INFO
, "Setting %s device-uri to \"%s\" (was \"%s\".)",
828 printer
->name
, attr
->values
[0].string
.text
, printer
->device_uri
);
830 strncpy(printer
->device_uri
, attr
->values
[0].string
.text
,
831 sizeof(printer
->device_uri
) - 1);
832 printer
->device_uri
[sizeof(printer
->device_uri
) - 1] = '\0';
835 if ((attr
= ippFindAttribute(con
->request
, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN
)) != NULL
)
837 LogMessage(L_INFO
, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
838 printer
->name
, attr
->values
[0].boolean
, printer
->accepting
);
840 printer
->accepting
= attr
->values
[0].boolean
;
842 if ((attr
= ippFindAttribute(con
->request
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
844 LogMessage(L_INFO
, "Setting %s printer-state to %d (was %d.)", printer
->name
,
845 attr
->values
[0].integer
, printer
->state
);
847 if (printer
->state
== IPP_PRINTER_STOPPED
&&
848 attr
->values
[0].integer
!= IPP_PRINTER_STOPPED
)
849 printer
->state
= IPP_PRINTER_IDLE
;
850 else if (printer
->state
!= IPP_PRINTER_STOPPED
&&
851 attr
->values
[0].integer
== IPP_PRINTER_STOPPED
)
853 if (printer
->state
== IPP_PRINTER_PROCESSING
)
854 StopJob(((job_t
*)printer
->job
)->id
);
856 printer
->state
= IPP_PRINTER_STOPPED
;
859 printer
->browse_time
= 0;
861 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message", IPP_TAG_TEXT
)) != NULL
)
863 strncpy(printer
->state_message
, attr
->values
[0].string
.text
,
864 sizeof(printer
->state_message
) - 1);
865 printer
->state_message
[sizeof(printer
->state_message
) - 1] = '\0';
869 * See if we have all required attributes...
872 if (printer
->device_uri
[0] == '\0')
873 strcpy(printer
->device_uri
, "file:/dev/null");
876 * See if we have an interface script or PPD file attached to the request...
879 if (con
->filename
[0])
880 strcpy(srcfile
, con
->filename
);
881 else if ((attr
= ippFindAttribute(con
->request
, "ppd-name", IPP_TAG_NAME
)) != NULL
)
882 snprintf(srcfile
, sizeof(srcfile
), CUPS_DATADIR
"/model/%s",
883 attr
->values
[0].string
.text
);
887 LogMessage(L_DEBUG
, "add_printer: srcfile = \"%s\"", srcfile
);
890 if (srcfile
[0] && (fp
= gzopen(srcfile
, "rb")) != NULL
)
892 if (srcfile
[0] && (fp
= fopen(srcfile
, "rb")) != NULL
)
893 #endif /* HAVE_LIBZ */
896 * Yes; get the first line from it...
901 gzgets(fp
, line
, sizeof(line
));
904 fgets(line
, sizeof(line
), fp
);
906 #endif /* HAVE_LIBZ */
909 * Then see what kind of file it is...
912 snprintf(dstfile
, sizeof(dstfile
), "%s/interfaces/%s", ServerRoot
,
915 if (strncmp(line
, "*PPD-Adobe", 10) == 0)
918 * The new file is a PPD file, so remove any old interface script
919 * that might be lying around...
927 * This must be an interface script, so move the file over to the
928 * interfaces directory and make it executable...
931 if (copy_file(srcfile
, dstfile
))
933 LogMessage(L_ERROR
, "add_printer: Unable to copy interface script - %s!",
935 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
940 LogMessage(L_DEBUG
, "add_printer: Copied interface script successfully!");
941 chmod(dstfile
, 0755);
945 snprintf(dstfile
, sizeof(dstfile
), "%s/ppd/%s.ppd", ServerRoot
,
948 if (strncmp(line
, "*PPD-Adobe", 10) == 0)
951 * The new file is a PPD file, so move the file over to the
952 * ppd directory and make it readable by all...
955 if (copy_file(srcfile
, dstfile
))
957 LogMessage(L_ERROR
, "add_printer: Unable to copy PPD file - %s!",
959 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
964 LogMessage(L_DEBUG
, "add_printer: Copied PPD file successfully!");
965 chmod(dstfile
, 0644);
971 * This must be an interface script, so remove any old PPD file that
972 * may be lying around...
980 * Make this printer the default if there is none...
983 if (DefaultPrinter
== NULL
)
984 DefaultPrinter
= printer
;
987 * Update the printer attributes and return...
990 SetPrinterAttrs(printer
);
993 if (printer
->job
!= NULL
)
996 * Stop the current job and then restart it below...
999 StopJob(((job_t
*)printer
->job
)->id
);
1004 LogMessage(L_INFO
, "New printer \'%s\' added by \'%s\'.", printer
->name
,
1007 con
->response
->request
.status
.status_code
= IPP_OK
;
1012 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
1013 * based upon the printer state...
1017 add_printer_state_reasons(client_t
*con
, /* I - Client connection */
1018 printer_t
*p
) /* I - Printer info */
1022 case IPP_PRINTER_STOPPED
:
1023 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1024 "printer-state-reasons", NULL
, "paused");
1028 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1029 "printer-state-reasons", NULL
, "none");
1036 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
1037 * the specified printer or class.
1041 add_queued_job_count(client_t
*con
, /* I - Client connection */
1042 printer_t
*p
) /* I - Printer or class */
1044 job_t
*job
; /* Current job */
1045 cups_ptype_t dtype
; /* Destination type */
1046 int count
; /* Number of jobs on destination */
1049 dtype
= p
->type
& CUPS_PRINTER_CLASS
;
1051 for (count
= 0, job
= Jobs
; job
!= NULL
; job
= job
->next
)
1052 if (strcmp(job
->dest
, p
->name
) == 0 && job
->dtype
== dtype
&&
1053 job
->state
->values
[0].integer
< IPP_JOB_STOPPED
)
1056 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1057 "queued-job-count", count
);
1062 * 'cancel_all_jobs()' - Cancel all print jobs.
1066 cancel_all_jobs(client_t
*con
, /* I - Client connection */
1067 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
1069 const char *dest
; /* Destination */
1070 cups_ptype_t dtype
; /* Destination type */
1071 char method
[HTTP_MAX_URI
],
1072 /* Method portion of URI */
1073 username
[HTTP_MAX_URI
],
1074 /* Username portion of URI */
1076 /* Host portion of URI */
1077 resource
[HTTP_MAX_URI
];
1078 /* Resource portion of URI */
1079 int port
; /* Port portion of URI */
1082 DEBUG_printf(("cancel_all_jobs(%08x, %08x)\n", con
, uri
));
1085 * Was this operation called from the correct URI?
1088 if (strncmp(con
->uri
, "/admin/", 7) != 0)
1090 LogMessage(L_ERROR
, "cancel_all_jobs: admin request on bad resource \'%s\'!",
1092 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1097 * See if we have a printer URI...
1100 if (strcmp(uri
->name
, "printer-uri") != 0)
1102 LogMessage(L_ERROR
, "cancel_all_jobs: bad %s attribute \'%s\'!",
1103 uri
->name
, uri
->values
[0].string
.text
);
1104 send_ipp_error(con
, IPP_BAD_REQUEST
);
1109 * And if the destination is valid...
1112 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
,
1115 if ((dest
= ValidateDest(resource
, &dtype
)) == NULL
)
1121 LogMessage(L_ERROR
, "cancel_all_jobs: resource name \'%s\' no good!", resource
);
1122 send_ipp_error(con
, IPP_NOT_FOUND
);
1127 * Cancel all of the jobs and return...
1131 LogMessage(L_INFO
, "All jobs on \'%s\' were cancelled by \'%s\'.", dest
,
1134 con
->response
->request
.status
.status_code
= IPP_OK
;
1139 * 'cancel_job()' - Cancel a print job.
1143 cancel_job(client_t
*con
, /* I - Client connection */
1144 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
1146 int i
; /* Looping var */
1147 ipp_attribute_t
*attr
; /* Current attribute */
1148 int jobid
; /* Job ID */
1149 char method
[HTTP_MAX_URI
],
1150 /* Method portion of URI */
1151 username
[HTTP_MAX_URI
],
1152 /* Username portion of URI */
1154 /* Host portion of URI */
1155 resource
[HTTP_MAX_URI
];
1156 /* Resource portion of URI */
1157 int port
; /* Port portion of URI */
1158 job_t
*job
; /* Job information */
1159 struct passwd
*user
; /* User info */
1160 struct group
*group
; /* System group info */
1163 DEBUG_printf(("cancel_job(%08x, %08x)\n", con
, uri
));
1166 * Verify that the POST operation was done to a valid URI.
1169 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
1170 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
1171 strncmp(con
->uri
, "/printers/", 10) != 0)
1173 LogMessage(L_ERROR
, "cancel_job: cancel request on bad resource \'%s\'!",
1175 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1180 * See if we have a job URI or a printer URI...
1183 if (strcmp(uri
->name
, "printer-uri") == 0)
1186 * Got a printer URI; see if we also have a job-id attribute...
1189 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
1191 LogMessage(L_ERROR
, "cancel_job: got a printer-uri attribute but no job-id!");
1192 send_ipp_error(con
, IPP_BAD_REQUEST
);
1196 jobid
= attr
->values
[0].integer
;
1201 * Got a job URI; parse it to get the job ID...
1204 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1206 if (strncmp(resource
, "/jobs/", 6) != 0)
1212 LogMessage(L_ERROR
, "cancel_job: bad job-uri attribute \'%s\'!",
1213 uri
->values
[0].string
.text
);
1214 send_ipp_error(con
, IPP_BAD_REQUEST
);
1218 jobid
= atoi(resource
+ 6);
1222 * See if the job exists...
1225 if ((job
= FindJob(jobid
)) == NULL
)
1228 * Nope - return a "not found" error...
1231 LogMessage(L_ERROR
, "cancel_job: job #%d doesn't exist!", jobid
);
1232 send_ipp_error(con
, IPP_NOT_FOUND
);
1237 * See if the job is owned by the requesting user...
1240 if (con
->username
[0])
1241 strcpy(username
, con
->username
);
1242 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1244 strncpy(username
, attr
->values
[0].string
.text
, sizeof(username
) - 1);
1245 username
[sizeof(username
) - 1] = '\0';
1248 strcpy(username
, "anonymous");
1250 if (strcmp(username
, job
->username
) != 0 && strcmp(username
, "root") != 0)
1253 * Not the owner or root; check to see if the user is a member of the
1257 user
= getpwnam(username
);
1260 group
= getgrnam(SystemGroup
);
1265 for (i
= 0; group
->gr_mem
[i
]; i
++)
1266 if (strcmp(username
, group
->gr_mem
[i
]) == 0)
1272 if (user
== NULL
|| group
== NULL
||
1273 (group
->gr_mem
[i
] == NULL
&& group
->gr_gid
!= user
->pw_gid
))
1276 * Username not found, group not found, or user is not part of the
1280 LogMessage(L_ERROR
, "cancel_job: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
1281 username
, jobid
, job
->username
);
1282 send_ipp_error(con
, IPP_FORBIDDEN
);
1288 * Cancel the job and return...
1294 LogMessage(L_INFO
, "Job %d was cancelled by \'%s\'.", jobid
,
1295 con
->username
[0] ? con
->username
: "unknown");
1297 con
->response
->request
.status
.status_code
= IPP_OK
;
1302 * 'copy_attrs()' - Copy attributes from one request to another.
1306 copy_attrs(ipp_t
*to
, /* I - Destination request */
1307 ipp_t
*from
, /* I - Source request */
1308 ipp_attribute_t
*req
, /* I - Requested attributes */
1309 ipp_tag_t group
) /* I - Group to copy */
1311 int i
; /* Looping var */
1312 ipp_attribute_t
*toattr
, /* Destination attribute */
1313 *fromattr
; /* Source attribute */
1316 DEBUG_printf(("copy_attrs(%08x, %08x)\n", to
, from
));
1318 if (to
== NULL
|| from
== NULL
)
1321 if (req
!= NULL
&& strcmp(req
->values
[0].string
.text
, "all") == 0)
1322 req
= NULL
; /* "all" means no filter... */
1324 for (fromattr
= from
->attrs
; fromattr
!= NULL
; fromattr
= fromattr
->next
)
1327 * Filter attributes as needed...
1330 if (group
!= IPP_TAG_ZERO
&& fromattr
->group_tag
!= group
&&
1331 fromattr
->group_tag
!= IPP_TAG_ZERO
)
1334 if (req
!= NULL
&& fromattr
->name
!= NULL
)
1336 for (i
= 0; i
< req
->num_values
; i
++)
1337 if (strcmp(fromattr
->name
, req
->values
[i
].string
.text
) == 0)
1340 if (i
== req
->num_values
)
1344 DEBUG_printf(("copy_attrs: copying attribute \'%s\'...\n", fromattr
->name
));
1346 switch (fromattr
->value_tag
)
1349 ippAddSeparator(to
);
1352 case IPP_TAG_INTEGER
:
1354 toattr
= ippAddIntegers(to
, fromattr
->group_tag
, fromattr
->value_tag
,
1355 fromattr
->name
, fromattr
->num_values
, NULL
);
1357 for (i
= 0; i
< fromattr
->num_values
; i
++)
1358 toattr
->values
[i
].integer
= fromattr
->values
[i
].integer
;
1361 case IPP_TAG_BOOLEAN
:
1362 toattr
= ippAddBooleans(to
, fromattr
->group_tag
, fromattr
->name
,
1363 fromattr
->num_values
, NULL
);
1365 for (i
= 0; i
< fromattr
->num_values
; i
++)
1366 toattr
->values
[i
].boolean
= fromattr
->values
[i
].boolean
;
1369 case IPP_TAG_STRING
:
1372 case IPP_TAG_KEYWORD
:
1374 case IPP_TAG_URISCHEME
:
1375 case IPP_TAG_CHARSET
:
1376 case IPP_TAG_LANGUAGE
:
1377 case IPP_TAG_MIMETYPE
:
1378 toattr
= ippAddStrings(to
, fromattr
->group_tag
, fromattr
->value_tag
,
1379 fromattr
->name
, fromattr
->num_values
, NULL
,
1382 for (i
= 0; i
< fromattr
->num_values
; i
++)
1383 toattr
->values
[i
].string
.text
= strdup(fromattr
->values
[i
].string
.text
);
1387 toattr
= ippAddDate(to
, fromattr
->group_tag
, fromattr
->name
,
1388 fromattr
->values
[0].date
);
1391 case IPP_TAG_RESOLUTION
:
1392 toattr
= ippAddResolutions(to
, fromattr
->group_tag
, fromattr
->name
,
1393 fromattr
->num_values
, IPP_RES_PER_INCH
,
1396 for (i
= 0; i
< fromattr
->num_values
; i
++)
1398 toattr
->values
[i
].resolution
.xres
= fromattr
->values
[i
].resolution
.xres
;
1399 toattr
->values
[i
].resolution
.yres
= fromattr
->values
[i
].resolution
.yres
;
1400 toattr
->values
[i
].resolution
.units
= fromattr
->values
[i
].resolution
.units
;
1404 case IPP_TAG_RANGE
:
1405 toattr
= ippAddRanges(to
, fromattr
->group_tag
, fromattr
->name
,
1406 fromattr
->num_values
, NULL
, NULL
);
1408 for (i
= 0; i
< fromattr
->num_values
; i
++)
1410 toattr
->values
[i
].range
.lower
= fromattr
->values
[i
].range
.lower
;
1411 toattr
->values
[i
].range
.upper
= fromattr
->values
[i
].range
.upper
;
1415 case IPP_TAG_TEXTLANG
:
1416 case IPP_TAG_NAMELANG
:
1417 toattr
= ippAddStrings(to
, fromattr
->group_tag
, fromattr
->value_tag
,
1418 fromattr
->name
, fromattr
->num_values
, NULL
, NULL
);
1420 for (i
= 0; i
< fromattr
->num_values
; i
++)
1423 toattr
->values
[0].string
.charset
=
1424 strdup(fromattr
->values
[0].string
.charset
);
1426 toattr
->values
[i
].string
.charset
=
1427 toattr
->values
[0].string
.charset
;
1429 toattr
->values
[i
].string
.text
=
1430 strdup(fromattr
->values
[i
].string
.text
);
1435 break; /* anti-compiler-warning-code */
1442 * 'create_job()' - Print a file to a printer or class.
1446 create_job(client_t
*con
, /* I - Client connection */
1447 ipp_attribute_t
*uri
) /* I - Printer URI */
1449 ipp_attribute_t
*attr
; /* Current attribute */
1450 const char *dest
; /* Destination */
1451 cups_ptype_t dtype
; /* Destination type (printer or class) */
1452 int priority
; /* Job priority */
1453 char *title
; /* Job name/title */
1454 job_t
*job
; /* Current job */
1455 char job_uri
[HTTP_MAX_URI
],
1457 printer_uri
[HTTP_MAX_URI
],
1459 method
[HTTP_MAX_URI
],
1460 /* Method portion of URI */
1461 username
[HTTP_MAX_URI
],
1462 /* Username portion of URI */
1464 /* Host portion of URI */
1465 resource
[HTTP_MAX_URI
];
1466 /* Resource portion of URI */
1467 int port
; /* Port portion of URI */
1468 printer_t
*printer
; /* Printer data */
1471 DEBUG_printf(("create_job(%08x, %08x)\n", con
, uri
));
1474 * Verify that the POST operation was done to a valid URI.
1477 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
1478 strncmp(con
->uri
, "/printers/", 10) != 0)
1480 LogMessage(L_ERROR
, "create_job: cancel request on bad resource \'%s\'!",
1482 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1487 * Is the destination valid?
1490 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1492 if ((dest
= ValidateDest(resource
, &dtype
)) == NULL
)
1498 LogMessage(L_ERROR
, "create_job: resource name \'%s\' no good!", resource
);
1499 send_ipp_error(con
, IPP_NOT_FOUND
);
1504 * See if the printer is accepting jobs...
1507 if (dtype
== CUPS_PRINTER_CLASS
)
1509 printer
= FindClass(dest
);
1510 sprintf(printer_uri
, "http://%s:%d/classes/%s", ServerName
,
1511 ntohs(con
->http
.hostaddr
.sin_port
), dest
);
1515 printer
= FindPrinter(dest
);
1517 sprintf(printer_uri
, "http://%s:%d/printers/%s", ServerName
,
1518 ntohs(con
->http
.hostaddr
.sin_port
), dest
);
1521 if (!printer
->accepting
)
1523 LogMessage(L_INFO
, "create_job: destination \'%s\' is not accepting jobs.",
1525 send_ipp_error(con
, IPP_NOT_ACCEPTING
);
1530 * Create the job and set things up...
1533 if ((attr
= ippFindAttribute(con
->request
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
1534 priority
= attr
->values
[0].integer
;
1536 ippAddInteger(con
->request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
1539 if ((attr
= ippFindAttribute(con
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
1540 title
= attr
->values
[0].string
.text
;
1542 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
1543 title
= "Untitled");
1545 if ((job
= AddJob(priority
, printer
->name
)) == NULL
)
1547 LogMessage(L_ERROR
, "create_job: unable to add job for destination \'%s\'!",
1549 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
1554 job
->attrs
= con
->request
;
1555 con
->request
= NULL
;
1557 strncpy(job
->title
, title
, sizeof(job
->title
) - 1);
1559 attr
= ippFindAttribute(job
->attrs
, "requesting-user-name", IPP_TAG_NAME
);
1561 if (con
->username
[0])
1562 strcpy(job
->username
, con
->username
);
1563 else if (attr
!= NULL
)
1565 LogMessage(L_DEBUG
, "create_job: requesting-user-name = \'%s\'",
1566 attr
->values
[0].string
.text
);
1568 strncpy(job
->username
, attr
->values
[0].string
.text
, sizeof(job
->username
) - 1);
1569 job
->username
[sizeof(job
->username
) - 1] = '\0';
1572 strcpy(job
->username
, "anonymous");
1575 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name",
1576 NULL
, job
->username
);
1579 attr
->group_tag
= IPP_TAG_JOB
;
1581 attr
->name
= strdup("job-originating-user-name");
1584 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
1585 time(NULL
) - StartTime
);
1586 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
1587 "time-at-processing", 0);
1588 attr
->value_tag
= IPP_TAG_NOVALUE
;
1589 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
1590 "time-at-completed", 0);
1591 attr
->value_tag
= IPP_TAG_NOVALUE
;
1595 LogMessage(L_INFO
, "Job %d created on \'%s\' by \'%s\'.", job
->id
,
1596 job
->dest
, job
->username
);
1599 * Add remaining job attributes...
1602 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1603 job
->state
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
1604 "job-state", IPP_JOB_STOPPED
);
1605 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
1607 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
1611 * Fill in the response info...
1614 sprintf(job_uri
, "http://%s:%d/jobs/%d", ServerName
,
1615 ntohs(con
->http
.hostaddr
.sin_port
), job
->id
);
1616 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, job_uri
);
1618 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1620 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
1621 job
->state
->values
[0].integer
);
1623 con
->response
->request
.status
.status_code
= IPP_OK
;
1628 * 'copy_file()' - Copy a PPD file or interface script...
1631 static int /* O - 0 = success, -1 = error */
1632 copy_file(const char *from
, /* I - Source file */
1633 const char *to
) /* I - Destination file */
1636 gzFile src
; /* Source file */
1638 int src
; /* Source file */
1639 #endif /* HAVE_LIBZ */
1640 int dst
, /* Destination file */
1641 bytes
; /* Bytes to read/write */
1642 char buffer
[8192]; /* Copy buffer */
1646 if ((src
= gzopen(from
, "rb")) == NULL
)
1649 if ((src
= open(from
, O_RDONLY
)) < 0)
1651 #endif /* HAVE_LIBZ */
1653 if ((dst
= open(to
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0644)) < 0)
1659 #endif /* HAVE_LIBZ */
1664 while ((bytes
= gzread(src
, buffer
, sizeof(buffer
))) > 0)
1666 while ((bytes
= read(src
, buffer
, sizeof(buffer
))) > 0)
1667 #endif /* HAVE_LIBZ */
1668 if (write(dst
, buffer
, bytes
) < bytes
)
1674 #endif /* HAVE_LIBZ */
1683 #endif /* HAVE_LIBZ */
1691 * 'delete_printer()' - Remove a printer or class from the system.
1695 delete_printer(client_t
*con
, /* I - Client connection */
1696 ipp_attribute_t
*uri
) /* I - URI of printer or class */
1698 const char *dest
; /* Destination */
1699 cups_ptype_t dtype
; /* Destination type (printer or class) */
1700 char method
[HTTP_MAX_URI
],
1701 /* Method portion of URI */
1702 username
[HTTP_MAX_URI
],
1703 /* Username portion of URI */
1705 /* Host portion of URI */
1706 resource
[HTTP_MAX_URI
];
1707 /* Resource portion of URI */
1708 int port
; /* Port portion of URI */
1709 printer_t
*printer
; /* Printer/class */
1710 char filename
[1024]; /* Script/PPD filename */
1714 * Was this operation called from the correct URI?
1717 if (strncmp(con
->uri
, "/admin/", 7) != 0)
1719 LogMessage(L_ERROR
, "delete_printer: admin request on bad resource \'%s\'!",
1721 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
1725 DEBUG_printf(("delete_printer(%08x, %08x)\n", con
, uri
));
1728 * Do we have a valid URI?
1731 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1733 if ((dest
= ValidateDest(resource
, &dtype
)) == NULL
)
1739 LogMessage(L_ERROR
, "delete_printer: resource name \'%s\' no good!", resource
);
1740 send_ipp_error(con
, IPP_NOT_FOUND
);
1745 * Find the printer or class and delete it...
1748 if (dtype
== CUPS_PRINTER_CLASS
)
1749 printer
= FindClass(dest
);
1751 printer
= FindPrinter(dest
);
1753 DeletePrinter(printer
);
1756 * Remove any old PPD or script files...
1759 sprintf(filename
, "%s/interfaces/%s", ServerRoot
, dest
);
1762 sprintf(filename
, "%s/ppd/%s.ppd", ServerRoot
, dest
);
1767 if (dtype
== CUPS_PRINTER_CLASS
)
1768 LogMessage(L_INFO
, "Class \'%s\' deleted by \'%s\'.", dest
,
1771 LogMessage(L_INFO
, "Printer \'%s\' deleted by \'%s\'.", dest
,
1775 * Return with no errors...
1778 con
->response
->request
.status
.status_code
= IPP_OK
;
1783 * 'get_default()' - Get the default destination.
1787 get_default(client_t
*con
) /* I - Client connection */
1789 DEBUG_printf(("get_default(%08x)\n", con
));
1791 if (DefaultPrinter
!= NULL
)
1793 copy_attrs(con
->response
, DefaultPrinter
->attrs
,
1794 ippFindAttribute(con
->request
, "requested-attributes",
1795 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
);
1797 con
->response
->request
.status
.status_code
= IPP_OK
;
1800 con
->response
->request
.status
.status_code
= IPP_NOT_FOUND
;
1805 * 'get_devices()' - Get the list of available devices on the local system.
1809 get_devices(client_t
*con
) /* I - Client connection */
1812 * Copy the device attributes to the response using the requested-attributes
1813 * attribute that may be provided by the client.
1816 copy_attrs(con
->response
, Devices
,
1817 ippFindAttribute(con
->request
, "requested-attributes",
1818 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
);
1820 con
->response
->request
.status
.status_code
= IPP_OK
;
1825 * 'get_jobs()' - Get a list of jobs for the specified printer.
1829 get_jobs(client_t
*con
, /* I - Client connection */
1830 ipp_attribute_t
*uri
) /* I - Printer URI */
1832 int i
; /* Looping var */
1833 ipp_attribute_t
*attr
; /* Current attribute */
1834 const char *dest
; /* Destination */
1835 cups_ptype_t dtype
; /* Destination type (printer or class) */
1836 char method
[HTTP_MAX_URI
],
1837 /* Method portion of URI */
1838 username
[HTTP_MAX_URI
],
1839 /* Username portion of URI */
1841 /* Host portion of URI */
1842 resource
[HTTP_MAX_URI
];
1843 /* Resource portion of URI */
1844 int port
; /* Port portion of URI */
1845 int completed
; /* Completed jobs? */
1846 int limit
; /* Maximum number of jobs to return */
1847 int count
; /* Number of jobs that match */
1848 job_t
*job
; /* Current job pointer */
1849 char job_uri
[HTTP_MAX_URI
];
1851 char filename
[1024]; /* Job filename */
1852 struct stat filestats
; /* Print file information */
1853 size_t jobsize
; /* Total job sizes */
1856 DEBUG_printf(("get_jobs(%08x, %08x)\n", con
, uri
));
1859 * Is the destination valid?
1862 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
1864 if ((strncmp(resource
, "/jobs", 5) == 0 && strlen(resource
) <= 6) ||
1865 (strncmp(resource
, "/printers", 9) == 0 && strlen(resource
) <= 10))
1868 dtype
= (cups_ptype_t
)0;
1870 else if (strncmp(resource
, "/classes", 8) == 0 && strlen(resource
) <= 9)
1873 dtype
= CUPS_PRINTER_CLASS
;
1875 else if ((dest
= ValidateDest(resource
, &dtype
)) == NULL
)
1881 LogMessage(L_ERROR
, "get_jobs: resource name \'%s\' no good!", resource
);
1882 send_ipp_error(con
, IPP_NOT_FOUND
);
1887 * See if the "which-jobs" attribute have been specified; if so, return
1888 * right away if they specify "completed" - we don't keep old job records...
1891 if ((attr
= ippFindAttribute(con
->request
, "which-jobs", IPP_TAG_KEYWORD
)) != NULL
&&
1892 strcmp(attr
->values
[0].string
.text
, "completed") == 0)
1898 * See if they want to limit the number of jobs reported; if not, limit
1899 * the report to 1000 jobs to prevent swamping of the server...
1902 if ((attr
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
)) != NULL
)
1903 limit
= attr
->values
[0].integer
;
1908 * See if we only want to see jobs for a specific user...
1911 if ((attr
= ippFindAttribute(con
->request
, "my-jobs", IPP_TAG_BOOLEAN
)) != NULL
&&
1912 attr
->values
[0].boolean
)
1914 if (con
->username
[0])
1915 strcpy(username
, con
->username
);
1916 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
1918 strncpy(username
, attr
->values
[0].string
.text
, sizeof(username
) - 1);
1919 username
[sizeof(username
) - 1] = '\0';
1922 strcpy(username
, "anonymous");
1928 * OK, build a list of jobs for this printer...
1931 for (count
= 0, job
= Jobs
; count
< limit
&& job
!= NULL
; job
= job
->next
)
1934 * Filter out jobs that don't match...
1937 DEBUG_printf(("get_jobs: job->id = %d\n", job
->id
));
1939 if ((dest
!= NULL
&& strcmp(job
->dest
, dest
) != 0))
1941 if (job
->dtype
!= dtype
&&
1942 (username
[0] == '\0' || strncmp(resource
, "/jobs", 5) != 0))
1944 if (username
[0] != '\0' && strcmp(username
, job
->username
) != 0)
1947 if (completed
&& job
->state
->values
[0].integer
<= IPP_JOB_STOPPED
)
1949 if (!completed
&& job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
1954 DEBUG_printf(("get_jobs: count = %d\n", count
));
1957 * Send the following attributes for each job:
1962 * job-originating-user-name
1968 * job-printer-up-time
1970 * Note that we are supposed to look at the "requested-attributes"
1971 * attribute to determine what we send, however the IPP/1.0 spec also
1972 * doesn't state that the server must limit the attributes to those
1973 * requested. In other words, the server can either implement the
1974 * filtering or not, and if not it needs to send all attributes that
1978 sprintf(job_uri
, "http://%s:%d/jobs/%d", ServerName
,
1979 ntohs(con
->http
.hostaddr
.sin_port
), job
->id
);
1981 for (i
= 0, jobsize
= 0; i
< job
->num_files
; i
++)
1983 sprintf(filename
, "%s/d%05d-%03d", RequestRoot
, job
->id
, i
+ 1);
1984 stat(filename
, &filestats
);
1985 jobsize
+= filestats
.st_size
;
1988 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
1989 "job-k-octets", (jobsize
+ 1023) / 1024);
1991 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
1992 "job-more-info", NULL
, job_uri
);
1994 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
1995 "job-uri", NULL
, job_uri
);
1997 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
1998 "job-printer-up-time", time(NULL
) - StartTime
);
2001 * Copy the job attributes to the response using the requested-attributes
2002 * attribute that may be provided by the client.
2005 copy_attrs(con
->response
, job
->attrs
,
2006 ippFindAttribute(con
->request
, "requested-attributes",
2007 IPP_TAG_KEYWORD
), IPP_TAG_JOB
);
2009 add_job_state_reasons(con
, job
);
2011 ippAddSeparator(con
->response
);
2014 if (ippFindAttribute(con
->request
, "requested-attributes", IPP_TAG_KEYWORD
) != NULL
)
2015 con
->response
->request
.status
.status_code
= IPP_OK_SUBST
;
2017 con
->response
->request
.status
.status_code
= IPP_OK
;
2022 * 'get_job_attrs()' - Get job attributes.
2026 get_job_attrs(client_t
*con
, /* I - Client connection */
2027 ipp_attribute_t
*uri
) /* I - Job URI */
2029 int i
; /* Looping var */
2030 ipp_attribute_t
*attr
; /* Current attribute */
2031 int jobid
; /* Job ID */
2032 job_t
*job
; /* Current job */
2033 char method
[HTTP_MAX_URI
],
2034 /* Method portion of URI */
2035 username
[HTTP_MAX_URI
],
2036 /* Username portion of URI */
2038 /* Host portion of URI */
2039 resource
[HTTP_MAX_URI
];
2040 /* Resource portion of URI */
2041 int port
; /* Port portion of URI */
2042 char job_uri
[HTTP_MAX_URI
];
2044 char filename
[1024]; /* Job filename */
2045 struct stat filestats
; /* Print file information */
2046 size_t jobsize
; /* Total job sizes */
2049 DEBUG_printf(("get_job_attrs(%08x, %08x)\n", con
, uri
));
2052 * See if we have a job URI or a printer URI...
2055 if (strcmp(uri
->name
, "printer-uri") == 0)
2058 * Got a printer URI; see if we also have a job-id attribute...
2061 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
2063 LogMessage(L_ERROR
, "get_job_attrs: got a printer-uri attribute but no job-id!");
2064 send_ipp_error(con
, IPP_BAD_REQUEST
);
2068 jobid
= attr
->values
[0].integer
;
2073 * Got a job URI; parse it to get the job ID...
2076 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
2078 if (strncmp(resource
, "/jobs/", 6) != 0)
2084 LogMessage(L_ERROR
, "get_job_attrs: bad job-uri attribute \'%s\'!\n",
2085 uri
->values
[0].string
.text
);
2086 send_ipp_error(con
, IPP_BAD_REQUEST
);
2090 jobid
= atoi(resource
+ 6);
2094 * See if the job exists...
2097 if ((job
= FindJob(jobid
)) == NULL
)
2100 * Nope - return a "not found" error...
2103 LogMessage(L_ERROR
, "get_job_attrs: job #%d doesn't exist!", jobid
);
2104 send_ipp_error(con
, IPP_NOT_FOUND
);
2109 * Put out the standard attributes...
2112 sprintf(job_uri
, "http://%s:%d/jobs/%d", ServerName
,
2113 ntohs(con
->http
.hostaddr
.sin_port
), job
->id
);
2115 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
2117 for (i
= 0, jobsize
= 0; i
< job
->num_files
; i
++)
2119 sprintf(filename
, "%s/d%05d-%03d", RequestRoot
, job
->id
, i
+ 1);
2120 stat(filename
, &filestats
);
2121 jobsize
+= filestats
.st_size
;
2124 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
2125 "job-k-octets", (jobsize
+ 1023) / 1024);
2127 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
2128 "job-more-info", NULL
, job_uri
);
2130 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
,
2131 "job-uri", NULL
, job_uri
);
2133 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
2134 "job-printer-up-time", time(NULL
) - StartTime
);
2137 * Copy the job attributes to the response using the requested-attributes
2138 * attribute that may be provided by the client.
2141 copy_attrs(con
->response
, job
->attrs
,
2142 ippFindAttribute(con
->request
, "requested-attributes",
2143 IPP_TAG_KEYWORD
), IPP_TAG_JOB
);
2145 add_job_state_reasons(con
, job
);
2147 if (ippFindAttribute(con
->request
, "requested-attributes", IPP_TAG_KEYWORD
) != NULL
)
2148 con
->response
->request
.status
.status_code
= IPP_OK_SUBST
;
2150 con
->response
->request
.status
.status_code
= IPP_OK
;
2155 * 'get_ppds()' - Get the list of PPD files on the local system.
2159 get_ppds(client_t
*con
) /* I - Client connection */
2162 * Copy the PPD attributes to the response using the requested-attributes
2163 * attribute that may be provided by the client.
2166 copy_attrs(con
->response
, PPDs
,
2167 ippFindAttribute(con
->request
, "requested-attributes",
2168 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
);
2170 con
->response
->request
.status
.status_code
= IPP_OK
;
2175 * 'get_printer_attrs()' - Get printer attributes.
2179 get_printer_attrs(client_t
*con
, /* I - Client connection */
2180 ipp_attribute_t
*uri
) /* I - Printer URI */
2182 const char *dest
; /* Destination */
2183 cups_ptype_t dtype
; /* Destination type (printer or class) */
2184 char method
[HTTP_MAX_URI
],
2185 /* Method portion of URI */
2186 username
[HTTP_MAX_URI
],
2187 /* Username portion of URI */
2189 /* Host portion of URI */
2190 resource
[HTTP_MAX_URI
];
2191 /* Resource portion of URI */
2192 int port
; /* Port portion of URI */
2193 printer_t
*printer
; /* Printer/class */
2194 time_t curtime
; /* Current time */
2197 DEBUG_printf(("get_printer_attrs(%08x, %08x)\n", con
, uri
));
2198 LogMessage(L_DEBUG
, "get_printer_attrs(%08x, \"%s\")\n", con
,
2199 uri
->values
[0].string
.text
);
2202 * Is the destination valid?
2205 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
2207 if ((dest
= ValidateDest(resource
, &dtype
)) == NULL
)
2213 LogMessage(L_ERROR
, "get_printer_attrs: resource name \'%s\' no good!", resource
);
2214 send_ipp_error(con
, IPP_NOT_FOUND
);
2218 if (dtype
== CUPS_PRINTER_CLASS
)
2219 printer
= FindClass(dest
);
2221 printer
= FindPrinter(dest
);
2223 curtime
= time(NULL
);
2226 * Copy the printer attributes to the response using requested-attributes
2227 * and document-format attributes that may be provided by the client.
2230 copy_attrs(con
->response
, printer
->attrs
,
2231 ippFindAttribute(con
->request
, "requested-attributes",
2232 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
);
2234 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
2237 add_printer_state_reasons(con
, printer
);
2239 if (printer
->state_message
[0])
2240 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2241 "printer-state-message", NULL
, printer
->state_message
);
2243 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
2244 printer
->accepting
);
2246 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2247 "printer-up-time", curtime
- StartTime
);
2248 ippAddDate(con
->response
, IPP_TAG_PRINTER
, "printer-current-time",
2249 ippTimeToDate(curtime
));
2251 add_queued_job_count(con
, printer
);
2253 con
->response
->request
.status
.status_code
= IPP_OK
;
2258 * 'get_printers()' - Get a list of printers.
2262 get_printers(client_t
*con
, /* I - Client connection */
2263 int type
) /* I - 0 or CUPS_PRINTER_CLASS */
2265 ipp_attribute_t
*attr
; /* Current attribute */
2266 int limit
; /* Maximum number of printers to return */
2267 int count
; /* Number of printers that match */
2268 printer_t
*printer
; /* Current printer pointer */
2269 time_t curtime
; /* Current time */
2272 DEBUG_printf(("get_printers(%08x)\n", con
));
2275 * See if they want to limit the number of printers reported; if not, limit
2276 * the report to 1000 printers to prevent swamping of the server...
2279 if ((attr
= ippFindAttribute(con
->request
, "limit", IPP_TAG_INTEGER
)) != NULL
)
2280 limit
= attr
->values
[0].integer
;
2285 * OK, build a list of printers for this printer...
2288 curtime
= time(NULL
);
2290 for (count
= 0, printer
= Printers
;
2291 count
< limit
&& printer
!= NULL
;
2292 printer
= printer
->next
)
2293 if ((printer
->type
& CUPS_PRINTER_CLASS
) == type
)
2296 * Send the following attributes for each printer:
2299 * printer-state-message
2300 * printer-is-accepting-jobs
2301 * + all printer attributes
2304 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
2305 "printer-state", printer
->state
);
2307 add_printer_state_reasons(con
, printer
);
2309 if (printer
->state_message
[0])
2310 ippAddString(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
2311 "printer-state-message", NULL
, printer
->state_message
);
2313 ippAddBoolean(con
->response
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
2314 printer
->accepting
);
2316 ippAddInteger(con
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2317 "printer-up-time", curtime
- StartTime
);
2318 ippAddDate(con
->response
, IPP_TAG_PRINTER
, "printer-current-time",
2319 ippTimeToDate(curtime
));
2321 add_queued_job_count(con
, printer
);
2323 copy_attrs(con
->response
, printer
->attrs
,
2324 ippFindAttribute(con
->request
, "requested-attributes",
2325 IPP_TAG_KEYWORD
), IPP_TAG_ZERO
);
2327 ippAddSeparator(con
->response
);
2330 con
->response
->request
.status
.status_code
= IPP_OK
;
2335 * 'hold_job()' - Hold a print job.
2339 hold_job(client_t
*con
, /* I - Client connection */
2340 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
2342 int i
; /* Looping var */
2343 ipp_attribute_t
*attr
; /* Current attribute */
2344 int jobid
; /* Job ID */
2345 char method
[HTTP_MAX_URI
],
2346 /* Method portion of URI */
2347 username
[HTTP_MAX_URI
],
2348 /* Username portion of URI */
2350 /* Host portion of URI */
2351 resource
[HTTP_MAX_URI
];
2352 /* Resource portion of URI */
2353 int port
; /* Port portion of URI */
2354 job_t
*job
; /* Job information */
2355 struct passwd
*user
; /* User info */
2356 struct group
*group
; /* System group info */
2359 DEBUG_printf(("hold_job(%08x, %08x)\n", con
, uri
));
2362 * Verify that the POST operation was done to a valid URI.
2365 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
2366 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
2367 strncmp(con
->uri
, "/printers/", 10) != 0)
2369 LogMessage(L_ERROR
, "hold_job: hold request on bad resource \'%s\'!",
2371 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
2376 * See if we have a job URI or a printer URI...
2379 if (strcmp(uri
->name
, "printer-uri") == 0)
2382 * Got a printer URI; see if we also have a job-id attribute...
2385 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
2387 LogMessage(L_ERROR
, "hold_job: got a printer-uri attribute but no job-id!");
2388 send_ipp_error(con
, IPP_BAD_REQUEST
);
2392 jobid
= attr
->values
[0].integer
;
2397 * Got a job URI; parse it to get the job ID...
2400 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
2402 if (strncmp(resource
, "/jobs/", 6) != 0)
2408 LogMessage(L_ERROR
, "hold_job: bad job-uri attribute \'%s\'!",
2409 uri
->values
[0].string
.text
);
2410 send_ipp_error(con
, IPP_BAD_REQUEST
);
2414 jobid
= atoi(resource
+ 6);
2418 * See if the job exists...
2421 if ((job
= FindJob(jobid
)) == NULL
)
2424 * Nope - return a "not found" error...
2427 LogMessage(L_ERROR
, "hold_job: job #%d doesn't exist!", jobid
);
2428 send_ipp_error(con
, IPP_NOT_FOUND
);
2433 * See if the job is owned by the requesting user...
2436 if (con
->username
[0])
2437 strcpy(username
, con
->username
);
2438 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
2440 strncpy(username
, attr
->values
[0].string
.text
, sizeof(username
) - 1);
2441 username
[sizeof(username
) - 1] = '\0';
2444 strcpy(username
, "anonymous");
2446 if (strcmp(username
, job
->username
) != 0 && strcmp(username
, "root") != 0)
2449 * Not the owner or root; check to see if the user is a member of the
2453 user
= getpwnam(username
);
2456 group
= getgrnam(SystemGroup
);
2461 for (i
= 0; group
->gr_mem
[i
]; i
++)
2462 if (strcmp(username
, group
->gr_mem
[i
]) == 0)
2468 if (user
== NULL
|| group
== NULL
||
2469 (group
->gr_mem
[i
] == NULL
&& group
->gr_gid
!= user
->pw_gid
))
2472 * Username not found, group not found, or user is not part of the
2476 LogMessage(L_ERROR
, "hold_job: \"%s\" not authorized to hold job id %d owned by \"%s\"!",
2477 username
, jobid
, job
->username
);
2478 send_ipp_error(con
, IPP_FORBIDDEN
);
2484 * Hold the job and return...
2489 LogMessage(L_INFO
, "Job %d was held by \'%s\'.", jobid
,
2490 con
->username
[0] ? con
->username
: "unknown");
2492 con
->response
->request
.status
.status_code
= IPP_OK
;
2497 * 'print_job()' - Print a file to a printer or class.
2501 print_job(client_t
*con
, /* I - Client connection */
2502 ipp_attribute_t
*uri
) /* I - Printer URI */
2504 ipp_attribute_t
*attr
; /* Current attribute */
2505 ipp_attribute_t
*format
; /* Document-format attribute */
2506 const char *dest
; /* Destination */
2507 cups_ptype_t dtype
; /* Destination type (printer or class) */
2508 int priority
; /* Job priority */
2509 char *title
; /* Job name/title */
2510 job_t
*job
; /* Current job */
2511 char job_uri
[HTTP_MAX_URI
],
2513 printer_uri
[HTTP_MAX_URI
],
2515 method
[HTTP_MAX_URI
],
2516 /* Method portion of URI */
2517 username
[HTTP_MAX_URI
],
2518 /* Username portion of URI */
2520 /* Host portion of URI */
2521 resource
[HTTP_MAX_URI
],
2522 /* Resource portion of URI */
2523 filename
[1024]; /* Job filename */
2524 int port
; /* Port portion of URI */
2525 mime_type_t
*filetype
; /* Type of file */
2526 char super
[MIME_MAX_SUPER
],
2527 /* Supertype of file */
2528 type
[MIME_MAX_TYPE
],
2529 /* Subtype of file */
2530 mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
2531 /* Textual name of mime type */
2532 printer_t
*printer
; /* Printer data */
2535 DEBUG_printf(("print_job(%08x, %08x)\n", con
, uri
));
2538 * Verify that the POST operation was done to a valid URI.
2541 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
2542 strncmp(con
->uri
, "/printers/", 10) != 0)
2544 LogMessage(L_ERROR
, "print_job: cancel request on bad resource \'%s\'!",
2546 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
2551 * OK, see if the client is sending the document compressed - CUPS
2552 * doesn't support compression yet...
2555 if ((attr
= ippFindAttribute(con
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
&&
2556 strcmp(attr
->values
[0].string
.text
, "none") == 0)
2558 LogMessage(L_ERROR
, "print_job: Unsupported compression attribute %s!",
2559 attr
->values
[0].string
.text
);
2560 send_ipp_error(con
, IPP_ATTRIBUTES
);
2561 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2562 "compression", NULL
, attr
->values
[0].string
.text
);
2567 * Do we have a file to print?
2570 if (con
->filename
[0] == '\0')
2572 LogMessage(L_ERROR
, "print_job: No file!?!");
2573 send_ipp_error(con
, IPP_BAD_REQUEST
);
2578 * Is it a format we support?
2581 if ((format
= ippFindAttribute(con
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
2584 * Grab format from client...
2587 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
, type
) != 2)
2589 LogMessage(L_ERROR
, "print_job: could not scan type \'%s\'!",
2590 format
->values
[0].string
.text
);
2591 send_ipp_error(con
, IPP_BAD_REQUEST
);
2598 * No document format attribute? Auto-type it!
2601 strcpy(super
, "application");
2602 strcpy(type
, "octet-stream");
2605 if (strcmp(super
, "application") == 0 &&
2606 strcmp(type
, "octet-stream") == 0)
2609 * Auto-type the file...
2612 LogMessage(L_DEBUG
, "print_job: auto-typing file...");
2614 filetype
= mimeFileType(MimeDatabase
, con
->filename
);
2616 if (filetype
!= NULL
)
2619 * Replace the document-format attribute value with the auto-typed one.
2622 sprintf(mimetype
, "%s/%s", filetype
->super
, filetype
->type
);
2626 free(format
->values
[0].string
.text
);
2627 format
->values
[0].string
.text
= strdup(mimetype
);
2630 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
2631 "document-format", NULL
, mimetype
);
2635 filetype
= mimeType(MimeDatabase
, super
, type
);
2637 if (filetype
== NULL
)
2639 LogMessage(L_ERROR
, "print_job: Unsupported format \'%s\'!",
2640 format
->values
[0].string
.text
);
2641 send_ipp_error(con
, IPP_DOCUMENT_FORMAT
);
2642 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
2643 "document-format", NULL
, format
->values
[0].string
.text
);
2647 LogMessage(L_DEBUG
, "print_job: request file type is %s/%s.",
2648 filetype
->super
, filetype
->type
);
2651 * Is the destination valid?
2654 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
2656 if ((dest
= ValidateDest(resource
, &dtype
)) == NULL
)
2662 LogMessage(L_ERROR
, "print_job: resource name \'%s\' no good!", resource
);
2663 send_ipp_error(con
, IPP_NOT_FOUND
);
2668 * See if the printer is accepting jobs...
2671 if (dtype
== CUPS_PRINTER_CLASS
)
2673 printer
= FindClass(dest
);
2674 sprintf(printer_uri
, "http://%s:%d/classes/%s", ServerName
,
2675 ntohs(con
->http
.hostaddr
.sin_port
), dest
);
2679 printer
= FindPrinter(dest
);
2681 sprintf(printer_uri
, "http://%s:%d/printers/%s", ServerName
,
2682 ntohs(con
->http
.hostaddr
.sin_port
), dest
);
2685 if (!printer
->accepting
)
2687 LogMessage(L_INFO
, "print_job: destination \'%s\' is not accepting jobs.",
2689 send_ipp_error(con
, IPP_NOT_ACCEPTING
);
2694 * Create the job and set things up...
2697 if ((attr
= ippFindAttribute(con
->request
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
2698 priority
= attr
->values
[0].integer
;
2700 ippAddInteger(con
->request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
2703 if ((attr
= ippFindAttribute(con
->request
, "job-name", IPP_TAG_NAME
)) != NULL
)
2704 title
= attr
->values
[0].string
.text
;
2706 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
2707 title
= "Untitled");
2709 if ((job
= AddJob(priority
, printer
->name
)) == NULL
)
2711 LogMessage(L_ERROR
, "print_job: unable to add job for destination \'%s\'!",
2713 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
2718 job
->attrs
= con
->request
;
2719 con
->request
= NULL
;
2721 if ((job
->filetypes
= (mime_type_t
**)malloc(sizeof(mime_type_t
*))) == NULL
)
2724 LogMessage(L_ERROR
, "print_job: unable to allocate memory for file types!");
2725 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
2729 job
->filetypes
[0] = filetype
;
2732 sprintf(filename
, "%s/d%05d-%03d", RequestRoot
, job
->id
, job
->num_files
);
2733 rename(con
->filename
, filename
);
2735 strncpy(job
->title
, title
, sizeof(job
->title
) - 1);
2737 con
->filename
[0] = '\0';
2739 attr
= ippFindAttribute(job
->attrs
, "requesting-user-name", IPP_TAG_NAME
);
2741 if (con
->username
[0])
2742 strcpy(job
->username
, con
->username
);
2745 LogMessage(L_DEBUG
, "print_job: requesting-user-name = \'%s\'",
2746 attr
->values
[0].string
.text
);
2748 strncpy(job
->username
, attr
->values
[0].string
.text
, sizeof(job
->username
) - 1);
2749 job
->username
[sizeof(job
->username
) - 1] = '\0';
2752 strcpy(job
->username
, "anonymous");
2755 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-originating-user-name",
2756 NULL
, job
->username
);
2759 attr
->group_tag
= IPP_TAG_JOB
;
2761 attr
->name
= strdup("job-originating-user-name");
2764 LogMessage(L_INFO
, "Job %d queued on \'%s\' by \'%s\'.", job
->id
,
2765 job
->dest
, job
->username
);
2768 * Add remaining job attributes...
2771 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
2772 job
->state
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
2773 "job-state", IPP_JOB_PENDING
);
2774 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
2776 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
, "job-name", NULL
,
2779 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
2780 time(NULL
) - StartTime
);
2781 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
2782 "time-at-processing", 0);
2783 attr
->value_tag
= IPP_TAG_NOVALUE
;
2784 attr
= ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
2785 "time-at-completed", 0);
2786 attr
->value_tag
= IPP_TAG_NOVALUE
;
2791 * Start the job if possible...
2797 * Fill in the response info...
2800 sprintf(job_uri
, "http://%s:%d/jobs/%d", ServerName
,
2801 ntohs(con
->http
.hostaddr
.sin_port
), job
->id
);
2802 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, job_uri
);
2804 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
2806 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
2807 job
->state
->values
[0].integer
);
2808 add_job_state_reasons(con
, job
);
2810 con
->response
->request
.status
.status_code
= IPP_OK
;
2815 * 'reject_jobs()' - Reject print jobs to a printer.
2819 reject_jobs(client_t
*con
, /* I - Client connection */
2820 ipp_attribute_t
*uri
) /* I - Printer or class URI */
2822 cups_ptype_t dtype
; /* Destination type (printer or class) */
2823 char method
[HTTP_MAX_URI
],
2824 /* Method portion of URI */
2825 username
[HTTP_MAX_URI
],
2826 /* Username portion of URI */
2828 /* Host portion of URI */
2829 resource
[HTTP_MAX_URI
];
2830 /* Resource portion of URI */
2831 int port
; /* Port portion of URI */
2832 const char *name
; /* Printer name */
2833 printer_t
*printer
; /* Printer data */
2834 ipp_attribute_t
*attr
; /* printer-state-message text */
2837 DEBUG_printf(("reject_jobs(%08x, %08x)\n", con
, uri
));
2840 * Was this operation called from the correct URI?
2843 if (strncmp(con
->uri
, "/admin/", 7) != 0)
2845 LogMessage(L_ERROR
, "reject_jobs: admin request on bad resource \'%s\'!",
2847 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
2852 * Is the destination valid?
2855 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
2857 if ((name
= ValidateDest(resource
, &dtype
)) == NULL
)
2863 LogMessage(L_ERROR
, "reject_jobs: resource name \'%s\' no good!", resource
);
2864 send_ipp_error(con
, IPP_NOT_FOUND
);
2869 * Reject jobs sent to the printer...
2872 if (dtype
== CUPS_PRINTER_CLASS
)
2873 printer
= FindClass(name
);
2875 printer
= FindPrinter(name
);
2877 printer
->accepting
= 0;
2879 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
2880 IPP_TAG_TEXT
)) == NULL
)
2881 strcpy(printer
->state_message
, "Rejecting Jobs");
2884 strncpy(printer
->state_message
, attr
->values
[0].string
.text
,
2885 sizeof(printer
->state_message
) - 1);
2886 printer
->state_message
[sizeof(printer
->state_message
) - 1] = '\0';
2889 if (dtype
== CUPS_PRINTER_CLASS
)
2894 if (dtype
== CUPS_PRINTER_CLASS
)
2895 LogMessage(L_INFO
, "Class \'%s\' rejecting jobs (\'%s\').", name
,
2898 LogMessage(L_INFO
, "Printer \'%s\' rejecting jobs (\'%s\').", name
,
2902 * Everything was ok, so return OK status...
2905 con
->response
->request
.status
.status_code
= IPP_OK
;
2910 * 'release_job()' - Release a held print job.
2914 release_job(client_t
*con
, /* I - Client connection */
2915 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
2917 int i
; /* Looping var */
2918 ipp_attribute_t
*attr
; /* Current attribute */
2919 int jobid
; /* Job ID */
2920 char method
[HTTP_MAX_URI
],
2921 /* Method portion of URI */
2922 username
[HTTP_MAX_URI
],
2923 /* Username portion of URI */
2925 /* Host portion of URI */
2926 resource
[HTTP_MAX_URI
];
2927 /* Resource portion of URI */
2928 int port
; /* Port portion of URI */
2929 job_t
*job
; /* Job information */
2930 struct passwd
*user
; /* User info */
2931 struct group
*group
; /* System group info */
2934 DEBUG_printf(("release_job(%08x, %08x)\n", con
, uri
));
2937 * Verify that the POST operation was done to a valid URI.
2940 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
2941 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
2942 strncmp(con
->uri
, "/printers/", 10) != 0)
2944 LogMessage(L_ERROR
, "release_job: release request on bad resource \'%s\'!",
2946 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
2951 * See if we have a job URI or a printer URI...
2954 if (strcmp(uri
->name
, "printer-uri") == 0)
2957 * Got a printer URI; see if we also have a job-id attribute...
2960 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
2962 LogMessage(L_ERROR
, "release_job: got a printer-uri attribute but no job-id!");
2963 send_ipp_error(con
, IPP_BAD_REQUEST
);
2967 jobid
= attr
->values
[0].integer
;
2972 * Got a job URI; parse it to get the job ID...
2975 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
2977 if (strncmp(resource
, "/jobs/", 6) != 0)
2983 LogMessage(L_ERROR
, "release_job: bad job-uri attribute \'%s\'!",
2984 uri
->values
[0].string
.text
);
2985 send_ipp_error(con
, IPP_BAD_REQUEST
);
2989 jobid
= atoi(resource
+ 6);
2993 * See if the job exists...
2996 if ((job
= FindJob(jobid
)) == NULL
)
2999 * Nope - return a "not found" error...
3002 LogMessage(L_ERROR
, "release_job: job #%d doesn't exist!", jobid
);
3003 send_ipp_error(con
, IPP_NOT_FOUND
);
3008 * See if job is "held"...
3011 if (job
->state
->values
[0].integer
!= IPP_JOB_HELD
)
3014 * Nope - return a "not possible" error...
3017 LogMessage(L_ERROR
, "release_job: job #%d is not held!", jobid
);
3018 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
3023 * See if the job is owned by the requesting user...
3026 if (con
->username
[0])
3027 strcpy(username
, con
->username
);
3028 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
3030 strncpy(username
, attr
->values
[0].string
.text
, sizeof(username
) - 1);
3031 username
[sizeof(username
) - 1] = '\0';
3034 strcpy(username
, "anonymous");
3036 if (strcmp(username
, job
->username
) != 0 && strcmp(username
, "root") != 0)
3039 * Not the owner or root; check to see if the user is a member of the
3043 user
= getpwnam(username
);
3046 group
= getgrnam(SystemGroup
);
3051 for (i
= 0; group
->gr_mem
[i
]; i
++)
3052 if (strcmp(username
, group
->gr_mem
[i
]) == 0)
3058 if (user
== NULL
|| group
== NULL
||
3059 (group
->gr_mem
[i
] == NULL
&& group
->gr_gid
!= user
->pw_gid
))
3062 * Username not found, group not found, or user is not part of the
3066 LogMessage(L_ERROR
, "release_job: \"%s\" not authorized to release job id %d owned by \"%s\"!",
3067 username
, jobid
, job
->username
);
3068 send_ipp_error(con
, IPP_FORBIDDEN
);
3074 * Release the job and return...
3079 LogMessage(L_INFO
, "Job %d was released by \'%s\'.", jobid
,
3080 con
->username
[0] ? con
->username
: "unknown");
3082 con
->response
->request
.status
.status_code
= IPP_OK
;
3087 * 'restart_job()' - Restart an old print job.
3091 restart_job(client_t
*con
, /* I - Client connection */
3092 ipp_attribute_t
*uri
) /* I - Job or Printer URI */
3094 int i
; /* Looping var */
3095 ipp_attribute_t
*attr
; /* Current attribute */
3096 int jobid
; /* Job ID */
3097 char method
[HTTP_MAX_URI
],
3098 /* Method portion of URI */
3099 username
[HTTP_MAX_URI
],
3100 /* Username portion of URI */
3102 /* Host portion of URI */
3103 resource
[HTTP_MAX_URI
];
3104 /* Resource portion of URI */
3105 int port
; /* Port portion of URI */
3106 job_t
*job
; /* Job information */
3107 struct passwd
*user
; /* User info */
3108 struct group
*group
; /* System group info */
3111 DEBUG_printf(("restart_job(%08x, %08x)\n", con
, uri
));
3114 * Verify that the POST operation was done to a valid URI.
3117 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
3118 strncmp(con
->uri
, "/jobs/", 5) != 0 &&
3119 strncmp(con
->uri
, "/printers/", 10) != 0)
3121 LogMessage(L_ERROR
, "restart_job: restart request on bad resource \'%s\'!",
3123 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
3128 * See if we have a job URI or a printer URI...
3131 if (strcmp(uri
->name
, "printer-uri") == 0)
3134 * Got a printer URI; see if we also have a job-id attribute...
3137 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
3139 LogMessage(L_ERROR
, "restart_job: got a printer-uri attribute but no job-id!");
3140 send_ipp_error(con
, IPP_BAD_REQUEST
);
3144 jobid
= attr
->values
[0].integer
;
3149 * Got a job URI; parse it to get the job ID...
3152 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3154 if (strncmp(resource
, "/jobs/", 6) != 0)
3160 LogMessage(L_ERROR
, "restart_job: bad job-uri attribute \'%s\'!",
3161 uri
->values
[0].string
.text
);
3162 send_ipp_error(con
, IPP_BAD_REQUEST
);
3166 jobid
= atoi(resource
+ 6);
3170 * See if the job exists...
3173 if ((job
= FindJob(jobid
)) == NULL
)
3176 * Nope - return a "not found" error...
3179 LogMessage(L_ERROR
, "restart_job: job #%d doesn't exist!", jobid
);
3180 send_ipp_error(con
, IPP_NOT_FOUND
);
3185 * See if job is in any of the "completed" states...
3188 if (job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
)
3191 * Nope - return a "not possible" error...
3194 LogMessage(L_ERROR
, "restart_job: job #%d is not complete!", jobid
);
3195 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
3200 * See if we have retained the job files...
3203 if (!JobFiles
&& job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
3206 * Nope - return a "not possible" error...
3209 LogMessage(L_ERROR
, "restart_job: job #%d cannot be restarted - no files!", jobid
);
3210 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
3215 * See if the job is owned by the requesting user...
3218 if (con
->username
[0])
3219 strcpy(username
, con
->username
);
3220 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
3222 strncpy(username
, attr
->values
[0].string
.text
, sizeof(username
) - 1);
3223 username
[sizeof(username
) - 1] = '\0';
3226 strcpy(username
, "anonymous");
3228 if (strcmp(username
, job
->username
) != 0 && strcmp(username
, "root") != 0)
3231 * Not the owner or root; check to see if the user is a member of the
3235 user
= getpwnam(username
);
3238 group
= getgrnam(SystemGroup
);
3243 for (i
= 0; group
->gr_mem
[i
]; i
++)
3244 if (strcmp(username
, group
->gr_mem
[i
]) == 0)
3250 if (user
== NULL
|| group
== NULL
||
3251 (group
->gr_mem
[i
] == NULL
&& group
->gr_gid
!= user
->pw_gid
))
3254 * Username not found, group not found, or user is not part of the
3258 LogMessage(L_ERROR
, "restart_job: \"%s\" not authorized to restart job id %d owned by \"%s\"!",
3259 username
, jobid
, job
->username
);
3260 send_ipp_error(con
, IPP_FORBIDDEN
);
3266 * Restart the job and return...
3271 LogMessage(L_INFO
, "Job %d was restarted by \'%s\'.", jobid
,
3272 con
->username
[0] ? con
->username
: "unknown");
3274 con
->response
->request
.status
.status_code
= IPP_OK
;
3279 * 'send_document()' - Send a file to a printer or class.
3283 send_document(client_t
*con
, /* I - Client connection */
3284 ipp_attribute_t
*uri
) /* I - Printer URI */
3286 int i
; /* Looping var */
3287 ipp_attribute_t
*attr
; /* Current attribute */
3288 ipp_attribute_t
*format
; /* Document-format attribute */
3289 int jobid
; /* Job ID number */
3290 job_t
*job
; /* Current job */
3291 char job_uri
[HTTP_MAX_URI
],
3293 method
[HTTP_MAX_URI
],
3294 /* Method portion of URI */
3295 username
[HTTP_MAX_URI
],
3296 /* Username portion of URI */
3298 /* Host portion of URI */
3299 resource
[HTTP_MAX_URI
];
3300 /* Resource portion of URI */
3301 int port
; /* Port portion of URI */
3302 mime_type_t
*filetype
, /* Type of file */
3303 **filetypes
; /* File types array */
3304 char super
[MIME_MAX_SUPER
],
3305 /* Supertype of file */
3306 type
[MIME_MAX_TYPE
],
3307 /* Subtype of file */
3308 mimetype
[MIME_MAX_SUPER
+ MIME_MAX_TYPE
+ 2];
3309 /* Textual name of mime type */
3310 struct passwd
*user
; /* User info */
3311 struct group
*group
; /* System group info */
3312 char filename
[1024]; /* Job filename */
3315 DEBUG_printf(("send_document(%08x, %08x)\n", con
, uri
));
3318 * Verify that the POST operation was done to a valid URI.
3321 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
3322 strncmp(con
->uri
, "/jobs/", 6) != 0 &&
3323 strncmp(con
->uri
, "/printers/", 10) != 0)
3325 LogMessage(L_ERROR
, "send_document: print request on bad resource \'%s\'!",
3327 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
3332 * See if we have a job URI or a printer URI...
3335 if (strcmp(uri
->name
, "printer-uri") == 0)
3338 * Got a printer URI; see if we also have a job-id attribute...
3341 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
3343 LogMessage(L_ERROR
, "send_document: got a printer-uri attribute but no job-id!");
3344 send_ipp_error(con
, IPP_BAD_REQUEST
);
3348 jobid
= attr
->values
[0].integer
;
3353 * Got a job URI; parse it to get the job ID...
3356 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3358 if (strncmp(resource
, "/jobs/", 6) != 0)
3364 LogMessage(L_ERROR
, "send_document: bad job-uri attribute \'%s\'!",
3365 uri
->values
[0].string
.text
);
3366 send_ipp_error(con
, IPP_BAD_REQUEST
);
3370 jobid
= atoi(resource
+ 6);
3374 * See if the job exists...
3377 if ((job
= FindJob(jobid
)) == NULL
)
3380 * Nope - return a "not found" error...
3383 LogMessage(L_ERROR
, "send_document: job #%d doesn't exist!", jobid
);
3384 send_ipp_error(con
, IPP_NOT_FOUND
);
3389 * See if the job is owned by the requesting user...
3392 if (con
->username
[0])
3393 strcpy(username
, con
->username
);
3394 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
3396 strncpy(username
, attr
->values
[0].string
.text
, sizeof(username
) - 1);
3397 username
[sizeof(username
) - 1] = '\0';
3400 strcpy(username
, "anonymous");
3402 if (strcmp(username
, job
->username
) != 0 && strcmp(username
, "root") != 0)
3405 * Not the owner or root; check to see if the user is a member of the
3409 user
= getpwnam(username
);
3412 group
= getgrnam(SystemGroup
);
3417 for (i
= 0; group
->gr_mem
[i
]; i
++)
3418 if (strcmp(username
, group
->gr_mem
[i
]) == 0)
3424 if (user
== NULL
|| group
== NULL
||
3425 (group
->gr_mem
[i
] == NULL
&& group
->gr_gid
!= user
->pw_gid
))
3428 * Username not found, group not found, or user is not part of the
3432 LogMessage(L_ERROR
, "send_document: \"%s\" not authorized to send document for job id %d owned by \"%s\"!",
3433 username
, jobid
, job
->username
);
3434 send_ipp_error(con
, IPP_FORBIDDEN
);
3440 * OK, see if the client is sending the document compressed - CUPS
3441 * doesn't support compression yet...
3444 if ((attr
= ippFindAttribute(con
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
&&
3445 strcmp(attr
->values
[0].string
.text
, "none") == 0)
3447 LogMessage(L_ERROR
, "send_document: Unsupported compression attribute %s!",
3448 attr
->values
[0].string
.text
);
3449 send_ipp_error(con
, IPP_ATTRIBUTES
);
3450 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
3451 "compression", NULL
, attr
->values
[0].string
.text
);
3456 * Do we have a file to print?
3459 if (con
->filename
[0] == '\0')
3461 LogMessage(L_ERROR
, "send_document: No file!?!");
3462 send_ipp_error(con
, IPP_BAD_REQUEST
);
3467 * Is it a format we support?
3470 if ((format
= ippFindAttribute(con
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
3473 * Grab format from client...
3476 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
, type
) != 2)
3478 LogMessage(L_ERROR
, "send_document: could not scan type \'%s\'!",
3479 format
->values
[0].string
.text
);
3480 send_ipp_error(con
, IPP_BAD_REQUEST
);
3487 * No document format attribute? Auto-type it!
3490 strcpy(super
, "application");
3491 strcpy(type
, "octet-stream");
3494 if (strcmp(super
, "application") == 0 &&
3495 strcmp(type
, "octet-stream") == 0)
3498 * Auto-type the file...
3501 LogMessage(L_DEBUG
, "send_document: auto-typing file...");
3503 filetype
= mimeFileType(MimeDatabase
, con
->filename
);
3505 if (filetype
!= NULL
)
3508 * Replace the document-format attribute value with the auto-typed one.
3511 sprintf(mimetype
, "%s/%s", filetype
->super
, filetype
->type
);
3515 free(format
->values
[0].string
.text
);
3516 format
->values
[0].string
.text
= strdup(mimetype
);
3519 ippAddString(con
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
3520 "document-format", NULL
, mimetype
);
3524 filetype
= mimeType(MimeDatabase
, super
, type
);
3526 if (filetype
== NULL
)
3528 LogMessage(L_ERROR
, "send_document: Unsupported format \'%s\'!",
3529 format
->values
[0].string
.text
);
3530 send_ipp_error(con
, IPP_DOCUMENT_FORMAT
);
3531 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
3532 "document-format", NULL
, format
->values
[0].string
.text
);
3536 LogMessage(L_DEBUG
, "send_document: request file type is %s/%s.",
3537 filetype
->super
, filetype
->type
);
3540 * Add the file to the job...
3543 if (job
->num_files
== 0)
3544 filetypes
= (mime_type_t
**)malloc(sizeof(mime_type_t
*));
3546 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
3547 (job
->num_files
+ 1) *
3548 sizeof(mime_type_t
));
3550 if (filetypes
== NULL
)
3553 LogMessage(L_ERROR
, "send_document: unable to allocate memory for file types!");
3554 send_ipp_error(con
, IPP_INTERNAL_ERROR
);
3558 job
->filetypes
= filetypes
;
3559 job
->filetypes
[job
->num_files
] = filetype
;
3562 sprintf(filename
, "%s/d%05d-%03d", RequestRoot
, job
->id
, job
->num_files
);
3563 rename(con
->filename
, filename
);
3565 con
->filename
[0] = '\0';
3567 LogMessage(L_INFO
, "File queued in job #%d by \'%s\'.", job
->id
,
3571 * Start the job if this is the last document...
3574 if ((attr
= ippFindAttribute(con
->request
, "last-document", IPP_TAG_BOOLEAN
)) != NULL
&&
3575 attr
->values
[0].boolean
)
3577 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
3582 * Fill in the response info...
3585 sprintf(job_uri
, "http://%s:%d/jobs/%d", ServerName
,
3586 ntohs(con
->http
.hostaddr
.sin_port
), job
->id
);
3587 ippAddString(con
->response
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
,
3590 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
3592 ippAddInteger(con
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "job-state",
3593 job
->state
->values
[0].integer
);
3594 add_job_state_reasons(con
, job
);
3596 con
->response
->request
.status
.status_code
= IPP_OK
;
3601 * 'send_ipp_error()' - Send an error status back to the IPP client.
3605 send_ipp_error(client_t
*con
, /* I - Client connection */
3606 ipp_status_t status
) /* I - IPP status code */
3608 DEBUG_printf(("send_ipp_error(%08x, %04x)\n", con
, status
));
3610 LogMessage(L_DEBUG
, "Sending IPP error code %x.", status
);
3611 if (con
->filename
[0])
3612 unlink(con
->filename
);
3614 con
->response
->request
.status
.status_code
= status
;
3619 * 'set_default()' - Set the default destination...
3623 set_default(client_t
*con
, /* I - Client connection */
3624 ipp_attribute_t
*uri
) /* I - Printer URI */
3626 cups_ptype_t dtype
; /* Destination type (printer or class) */
3627 char method
[HTTP_MAX_URI
],
3628 /* Method portion of URI */
3629 username
[HTTP_MAX_URI
],
3630 /* Username portion of URI */
3632 /* Host portion of URI */
3633 resource
[HTTP_MAX_URI
];
3634 /* Resource portion of URI */
3635 int port
; /* Port portion of URI */
3636 const char *name
; /* Printer name */
3639 DEBUG_printf(("set_default(%08x, %08x)\n", con
, uri
));
3642 * Was this operation called from the correct URI?
3645 if (strncmp(con
->uri
, "/admin/", 7) != 0)
3647 LogMessage(L_ERROR
, "set_default: admin request on bad resource \'%s\'!",
3649 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
3654 * Is the destination valid?
3657 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3659 if ((name
= ValidateDest(resource
, &dtype
)) == NULL
)
3665 LogMessage(L_ERROR
, "set_default: resource name \'%s\' no good!", resource
);
3666 send_ipp_error(con
, IPP_NOT_FOUND
);
3671 * Set it as the default...
3674 if (dtype
== CUPS_PRINTER_CLASS
)
3675 DefaultPrinter
= FindClass(name
);
3677 DefaultPrinter
= FindPrinter(name
);
3682 LogMessage(L_INFO
, "Default destination set to \'%s\' by \'%s\'.", name
,
3686 * Everything was ok, so return OK status...
3689 con
->response
->request
.status
.status_code
= IPP_OK
;
3694 * 'set_job_attrs()' - Set job attributes.
3698 set_job_attrs(client_t
*con
, /* I - Client connection */
3699 ipp_attribute_t
*uri
) /* I - Job URI */
3701 int i
; /* Looping var */
3702 ipp_attribute_t
*attr
; /* Current attribute */
3703 int jobid
; /* Job ID */
3704 job_t
*job
; /* Current job */
3705 const char *dest
; /* Destination */
3706 cups_ptype_t dtype
; /* Destination type (printer or class) */
3707 char method
[HTTP_MAX_URI
],
3708 /* Method portion of URI */
3709 username
[HTTP_MAX_URI
],
3710 /* Username portion of URI */
3712 /* Host portion of URI */
3713 resource
[HTTP_MAX_URI
];
3714 /* Resource portion of URI */
3715 int port
; /* Port portion of URI */
3716 struct passwd
*user
; /* User info */
3717 struct group
*group
; /* System group info */
3720 DEBUG_printf(("set_job_attrs(%08x, %08x)\n", con
, uri
));
3723 * See if we have a job URI or a printer URI...
3726 if (strcmp(uri
->name
, "printer-uri") == 0)
3729 * Got a printer URI; see if we also have a job-id attribute...
3732 if ((attr
= ippFindAttribute(con
->request
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
3734 LogMessage(L_ERROR
, "set_job_attrs: got a printer-uri attribute but no job-id!");
3735 send_ipp_error(con
, IPP_BAD_REQUEST
);
3739 jobid
= attr
->values
[0].integer
;
3744 * Got a job URI; parse it to get the job ID...
3747 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3749 if (strncmp(resource
, "/jobs/", 6) != 0)
3755 LogMessage(L_ERROR
, "set_job_attrs: bad job-uri attribute \'%s\'!\n",
3756 uri
->values
[0].string
.text
);
3757 send_ipp_error(con
, IPP_BAD_REQUEST
);
3761 jobid
= atoi(resource
+ 6);
3765 * See if the job exists...
3768 if ((job
= FindJob(jobid
)) == NULL
)
3771 * Nope - return a "not found" error...
3774 LogMessage(L_ERROR
, "set_job_attrs: job #%d doesn't exist!", jobid
);
3775 send_ipp_error(con
, IPP_NOT_FOUND
);
3780 * See if the job has been completed...
3783 if (job
->state
->values
[0].integer
> IPP_JOB_STOPPED
)
3786 * Return a "not-possible" error...
3789 LogMessage(L_ERROR
, "set_job_attrs: job #%d is finished and cannot be altered!", jobid
);
3790 send_ipp_error(con
, IPP_NOT_POSSIBLE
);
3795 * See if the job is owned by the requesting user...
3798 if (con
->username
[0])
3799 strcpy(username
, con
->username
);
3800 else if ((attr
= ippFindAttribute(con
->request
, "requesting-user-name", IPP_TAG_NAME
)) != NULL
)
3802 strncpy(username
, attr
->values
[0].string
.text
, sizeof(username
) - 1);
3803 username
[sizeof(username
) - 1] = '\0';
3806 strcpy(username
, "anonymous");
3808 if (strcmp(username
, job
->username
) != 0 && strcmp(username
, "root") != 0)
3811 * Not the owner or root; check to see if the user is a member of the
3815 user
= getpwnam(username
);
3818 group
= getgrnam(SystemGroup
);
3823 for (i
= 0; group
->gr_mem
[i
]; i
++)
3824 if (strcmp(username
, group
->gr_mem
[i
]) == 0)
3830 if (user
== NULL
|| group
== NULL
||
3831 (group
->gr_mem
[i
] == NULL
&& group
->gr_gid
!= user
->pw_gid
))
3834 * Username not found, group not found, or user is not part of the
3838 LogMessage(L_ERROR
, "cancel_job: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
3839 username
, jobid
, job
->username
);
3840 send_ipp_error(con
, IPP_FORBIDDEN
);
3846 * See what the user wants to change.
3848 * NOTE: Unfortunately, the job-printer-uri attribute is specified as
3849 * READ ONLY in the Job and Printer Set Operations. In order to
3850 * support a "move" operation from one printer to another, and
3851 * rather than defining YET ANOTHER extension operation, CUPS
3852 * allows the client to set this attribute in violation of the
3855 * If this bothers you, comment the job-printer-uri code out to
3856 * provide a completely compliant set-job-attributes operation.
3857 * [you will lose the ability to move jobs]
3859 * We did propose a change to the spec for this, but it was rejected
3860 * due to some special cases that might need to be supported (although
3861 * it is entirely possible to limit the valid values to the same
3862 * host, eliminating the problem... sigh...)
3865 if ((attr
= ippFindAttribute(con
->request
, "job-printer-uri", IPP_TAG_URI
)) != NULL
)
3868 * Move the job to a different printer or class...
3871 httpSeparate(attr
->values
[0].string
.text
, method
, username
, host
, &port
,
3873 if ((dest
= ValidateDest(resource
, &dtype
)) == NULL
)
3879 LogMessage(L_ERROR
, "set_job_attrs: resource name \'%s\' no good!", resource
);
3880 send_ipp_error(con
, IPP_NOT_FOUND
);
3884 MoveJob(jobid
, dest
);
3887 if ((attr
= ippFindAttribute(con
->request
, "job-priority", IPP_TAG_INTEGER
)) != NULL
&&
3888 job
->state
->values
[0].integer
!= IPP_JOB_PROCESSING
)
3891 * Change the job priority
3894 SetJobPriority(jobid
, attr
->values
[0].integer
);
3898 * Start jobs if possible...
3904 * Return with "everything is OK" status...
3907 con
->response
->request
.status
.status_code
= IPP_OK
;
3912 * 'start_printer()' - Start a printer.
3916 start_printer(client_t
*con
, /* I - Client connection */
3917 ipp_attribute_t
*uri
) /* I - Printer URI */
3919 cups_ptype_t dtype
; /* Destination type (printer or class) */
3920 char method
[HTTP_MAX_URI
],
3921 /* Method portion of URI */
3922 username
[HTTP_MAX_URI
],
3923 /* Username portion of URI */
3925 /* Host portion of URI */
3926 resource
[HTTP_MAX_URI
];
3927 /* Resource portion of URI */
3928 int port
; /* Port portion of URI */
3929 const char *name
; /* Printer name */
3930 printer_t
*printer
; /* Printer data */
3933 DEBUG_printf(("start_printer(%08x, %08x)\n", con
, uri
));
3936 * Was this operation called from the correct URI?
3939 if (strncmp(con
->uri
, "/admin/", 7) != 0)
3941 LogMessage(L_ERROR
, "start_printer: admin request on bad resource \'%s\'!",
3943 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
3948 * Is the destination valid?
3951 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
3953 if ((name
= ValidateDest(resource
, &dtype
)) == NULL
)
3959 LogMessage(L_ERROR
, "start_printer: resource name \'%s\' no good!", resource
);
3960 send_ipp_error(con
, IPP_NOT_FOUND
);
3965 * Start the printer...
3968 if (dtype
== CUPS_PRINTER_CLASS
)
3969 printer
= FindClass(name
);
3971 printer
= FindPrinter(name
);
3973 StartPrinter(printer
);
3975 if (dtype
== CUPS_PRINTER_CLASS
)
3980 if (dtype
== CUPS_PRINTER_CLASS
)
3981 LogMessage(L_INFO
, "Class \'%s\' started by \'%s\'.", name
,
3984 LogMessage(L_INFO
, "Printer \'%s\' started by \'%s\'.", name
,
3987 printer
->state_message
[0] = '\0';
3992 * Everything was ok, so return OK status...
3995 con
->response
->request
.status
.status_code
= IPP_OK
;
4000 * 'stop_printer()' - Stop a printer.
4004 stop_printer(client_t
*con
, /* I - Client connection */
4005 ipp_attribute_t
*uri
) /* I - Printer URI */
4007 cups_ptype_t dtype
; /* Destination type (printer or class) */
4008 char method
[HTTP_MAX_URI
],
4009 /* Method portion of URI */
4010 username
[HTTP_MAX_URI
],
4011 /* Username portion of URI */
4013 /* Host portion of URI */
4014 resource
[HTTP_MAX_URI
];
4015 /* Resource portion of URI */
4016 int port
; /* Port portion of URI */
4017 const char *name
; /* Printer name */
4018 printer_t
*printer
; /* Printer data */
4019 ipp_attribute_t
*attr
; /* printer-state-message attribute */
4022 DEBUG_printf(("stop_printer(%08x, %08x)\n", con
, uri
));
4025 * Was this operation called from the correct URI?
4028 if (strncmp(con
->uri
, "/admin/", 7) != 0)
4030 LogMessage(L_ERROR
, "stop_printer: admin request on bad resource \'%s\'!",
4032 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
4037 * Is the destination valid?
4040 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
4042 if ((name
= ValidateDest(resource
, &dtype
)) == NULL
)
4048 LogMessage(L_ERROR
, "stop_printer: resource name \'%s\' no good!", resource
);
4049 send_ipp_error(con
, IPP_NOT_FOUND
);
4054 * Stop the printer...
4057 if (dtype
== CUPS_PRINTER_CLASS
)
4058 printer
= FindClass(name
);
4060 printer
= FindPrinter(name
);
4062 StopPrinter(printer
);
4064 if (dtype
== CUPS_PRINTER_CLASS
)
4069 if ((attr
= ippFindAttribute(con
->request
, "printer-state-message",
4070 IPP_TAG_TEXT
)) == NULL
)
4071 strcpy(printer
->state_message
, "Paused");
4074 strncpy(printer
->state_message
, attr
->values
[0].string
.text
,
4075 sizeof(printer
->state_message
) - 1);
4076 printer
->state_message
[sizeof(printer
->state_message
) - 1] = '\0';
4079 if (dtype
== CUPS_PRINTER_CLASS
)
4080 LogMessage(L_INFO
, "Class \'%s\' stopped by \'%s\'.", name
,
4083 LogMessage(L_INFO
, "Printer \'%s\' stopped by \'%s\'.", name
,
4087 * Everything was ok, so return OK status...
4090 con
->response
->request
.status
.status_code
= IPP_OK
;
4095 * 'validate_job()' - Validate printer options and destination.
4099 validate_job(client_t
*con
, /* I - Client connection */
4100 ipp_attribute_t
*uri
) /* I - Printer URI */
4102 ipp_attribute_t
*attr
; /* Current attribute */
4103 ipp_attribute_t
*format
; /* Document-format attribute */
4104 cups_ptype_t dtype
; /* Destination type (printer or class) */
4105 char method
[HTTP_MAX_URI
],
4106 /* Method portion of URI */
4107 username
[HTTP_MAX_URI
],
4108 /* Username portion of URI */
4110 /* Host portion of URI */
4111 resource
[HTTP_MAX_URI
];
4112 /* Resource portion of URI */
4113 int port
; /* Port portion of URI */
4114 char super
[MIME_MAX_SUPER
],
4115 /* Supertype of file */
4116 type
[MIME_MAX_TYPE
];
4117 /* Subtype of file */
4120 DEBUG_printf(("validate_job(%08x, %08x)\n", con
, uri
));
4123 * Verify that the POST operation was done to a valid URI.
4126 if (strncmp(con
->uri
, "/classes/", 9) != 0 &&
4127 strncmp(con
->uri
, "/printers/", 10) != 0)
4129 LogMessage(L_ERROR
, "validate_job: request on bad resource \'%s\'!",
4131 send_ipp_error(con
, IPP_NOT_AUTHORIZED
);
4136 * OK, see if the client is sending the document compressed - CUPS
4137 * doesn't support compression yet...
4140 if ((attr
= ippFindAttribute(con
->request
, "compression", IPP_TAG_KEYWORD
)) != NULL
&&
4141 strcmp(attr
->values
[0].string
.text
, "none") == 0)
4143 LogMessage(L_ERROR
, "validate_job: Unsupported compression attribute %s!",
4144 attr
->values
[0].string
.text
);
4145 send_ipp_error(con
, IPP_ATTRIBUTES
);
4146 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
4147 "compression", NULL
, attr
->values
[0].string
.text
);
4152 * Is it a format we support?
4155 if ((format
= ippFindAttribute(con
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
4157 if (sscanf(format
->values
[0].string
.text
, "%15[^/]/%31[^;]", super
, type
) != 2)
4159 LogMessage(L_ERROR
, "validate_job: could not scan type \'%s\'!\n",
4160 format
->values
[0].string
.text
);
4161 send_ipp_error(con
, IPP_BAD_REQUEST
);
4165 if ((strcmp(super
, "application") != 0 ||
4166 strcmp(type
, "octet-stream") != 0) &&
4167 mimeType(MimeDatabase
, super
, type
) == NULL
)
4169 LogMessage(L_ERROR
, "validate_job: Unsupported format \'%s\'!\n",
4170 format
->values
[0].string
.text
);
4171 send_ipp_error(con
, IPP_DOCUMENT_FORMAT
);
4172 ippAddString(con
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_MIMETYPE
,
4173 "document-format", NULL
, format
->values
[0].string
.text
);
4179 * Is the destination valid?
4182 httpSeparate(uri
->values
[0].string
.text
, method
, username
, host
, &port
, resource
);
4184 if (ValidateDest(resource
, &dtype
) == NULL
)
4190 LogMessage(L_ERROR
, "validate_job: resource name \'%s\' no good!", resource
);
4191 send_ipp_error(con
, IPP_NOT_FOUND
);
4196 * Everything was ok, so return OK status...
4199 con
->response
->request
.status
.status_code
= IPP_OK
;
4204 * End of "$Id: ipp.c,v 1.59 2000/03/21 04:03:34 mike Exp $".