]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/ipp.c
Add simple device URI check to add_printer(); basically, now all device
[thirdparty/cups.git] / scheduler / ipp.c
1 /*
2 * "$Id$"
3 *
4 * IPP routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-2005 by Easy Software Products, all rights reserved.
7 *
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
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * ProcessIPPRequest() - Process an incoming IPP request...
27 * accept_jobs() - Accept print jobs to a printer.
28 * add_class() - Add a class to the system.
29 * add_file() - Add a file to a job.
30 * add_job_state_reasons() - Add the "job-state-reasons" attribute based
31 * upon the job and printer state...
32 * add_job_subscriptions() - Add any subcriptions for a job.
33 * add_printer() - Add a printer to the system.
34 * add_printer_state_reasons() - Add the "printer-state-reasons" attribute
35 * based upon the printer state...
36 * add_queued_job_count() - Add the "queued-job-count" attribute for
37 * cancel_all_jobs() - Cancel all print jobs.
38 * cancel_job() - Cancel a print job.
39 * cancel_subscription() - Cancel a subscription.
40 * check_quotas() - Check quotas for a printer and user.
41 * copy_attribute() - Copy a single attribute.
42 * copy_attrs() - Copy attributes from one request to another.
43 * copy_banner() - Copy a banner file to the requests directory
44 * for the specified job.
45 * copy_file() - Copy a PPD file or interface script...
46 * copy_model() - Copy a PPD model file, substituting default
47 * values as needed...
48 * create_job() - Print a file to a printer or class.
49 * create_subscription() - Create a notification subscription.
50 * delete_printer() - Remove a printer or class from the system.
51 * get_default() - Get the default destination.
52 * get_devices() - Get the list of available devices on the
53 * local system.
54 * get_jobs() - Get a list of jobs for the specified printer.
55 * get_job_attrs() - Get job attributes.
56 * get_notifications() - Get events for a subscription.
57 * get_ppds() - Get the list of PPD files on the local
58 * system.
59 * get_printer_attrs() - Get printer attributes.
60 * get_printers() - Get a list of printers.
61 * get_subscription_attrs() - Get subscription attributes.
62 * get_subscriptions() - Get subscriptions.
63 * hold_job() - Hold a print job.
64 * move_job() - Move a job to a new destination.
65 * ppd_add_default() - Add a PPD default choice.
66 * ppd_parse_line() - Parse a PPD default line.
67 * print_job() - Print a file to a printer or class.
68 * read_ps_line() - Read a line from a PS file...
69 * read_ps_job_ticket() - Reads a job ticket embedded in a PS file.
70 * reject_jobs() - Reject print jobs to a printer.
71 * release_job() - Release a held print job.
72 * restart_job() - Restart an old print job.
73 * send_document() - Send a file to a printer or class.
74 * send_ipp_error() - Send an error status back to the IPP client.
75 * set_default() - Set the default destination...
76 * set_job_attrs() - Set job attributes.
77 * start_printer() - Start a printer.
78 * stop_printer() - Stop a printer.
79 * user_allowed() - See if a user is allowed to print to a queue.
80 * validate_job() - Validate printer options and destination.
81 * validate_name() - Make sure the printer name only contains
82 * valid chars.
83 * validate_user() - Validate the user for the request.
84 */
85
86 /*
87 * Include necessary headers...
88 */
89
90 #include "cupsd.h"
91 #include <pwd.h>
92 #include <grp.h>
93
94 #ifdef HAVE_LIBPAPER
95 # include <paper.h>
96 #endif /* HAVE_LIBPAPER */
97
98
99 /*
100 * PPD default choice structure...
101 */
102
103 typedef struct
104 {
105 char option[PPD_MAX_NAME]; /* Main keyword (option name) */
106 char choice[PPD_MAX_NAME]; /* Option keyword (choice name) */
107 } ppd_default_t;
108
109
110 /*
111 * Local functions...
112 */
113
114 static void accept_jobs(client_t *con, ipp_attribute_t *uri);
115 static void add_class(client_t *con, ipp_attribute_t *uri);
116 static int add_file(client_t *con, job_t *job, mime_type_t *filetype,
117 int compression);
118 static void add_job_state_reasons(client_t *con, job_t *job);
119 static void add_job_subscriptions(client_t *con, job_t *job);
120 static void add_printer(client_t *con, ipp_attribute_t *uri);
121 static void add_printer_state_reasons(client_t *con, printer_t *p);
122 static void add_queued_job_count(client_t *con, printer_t *p);
123 static void cancel_all_jobs(client_t *con, ipp_attribute_t *uri);
124 static void cancel_job(client_t *con, ipp_attribute_t *uri);
125 static void cancel_subscription(client_t *con, int id);
126 static int check_quotas(client_t *con, printer_t *p);
127 static ipp_attribute_t *copy_attribute(ipp_t *to, ipp_attribute_t *attr,
128 int quickcopy);
129 static void copy_attrs(ipp_t *to, ipp_t *from, ipp_attribute_t *req,
130 ipp_tag_t group, int quickcopy);
131 static int copy_banner(client_t *con, job_t *job, const char *name);
132 static int copy_file(const char *from, const char *to);
133 static int copy_model(const char *from, const char *to);
134 static void create_job(client_t *con, ipp_attribute_t *uri);
135 static void create_subscription(client_t *con, ipp_attribute_t *uri);
136 static void delete_printer(client_t *con, ipp_attribute_t *uri);
137 static void get_default(client_t *con);
138 static void get_devices(client_t *con);
139 static void get_jobs(client_t *con, ipp_attribute_t *uri);
140 static void get_job_attrs(client_t *con, ipp_attribute_t *uri);
141 static void get_notifications(client_t *con, int id);
142 static void get_ppds(client_t *con);
143 static void get_printers(client_t *con, int type);
144 static void get_printer_attrs(client_t *con, ipp_attribute_t *uri);
145 static void get_subscription_attrs(client_t *con, int sub_id);
146 static void get_subscriptions(client_t *con, ipp_attribute_t *uri);
147 static void hold_job(client_t *con, ipp_attribute_t *uri);
148 static void move_job(client_t *con, ipp_attribute_t *uri);
149 static int ppd_add_default(const char *option, const char *choice,
150 int num_defaults, ppd_default_t **defaults);
151 static int ppd_parse_line(const char *line, char *option, int olen,
152 char *choice, int clen);
153 static void print_job(client_t *con, ipp_attribute_t *uri);
154 static void read_ps_job_ticket(client_t *con);
155 static void reject_jobs(client_t *con, ipp_attribute_t *uri);
156 static void release_job(client_t *con, ipp_attribute_t *uri);
157 static void renew_subscription(client_t *con, int sub_id);
158 static void restart_job(client_t *con, ipp_attribute_t *uri);
159 static void send_document(client_t *con, ipp_attribute_t *uri);
160 static void send_ipp_error(client_t *con, ipp_status_t status);
161 static void set_default(client_t *con, ipp_attribute_t *uri);
162 static void set_job_attrs(client_t *con, ipp_attribute_t *uri);
163 static void start_printer(client_t *con, ipp_attribute_t *uri);
164 static void stop_printer(client_t *con, ipp_attribute_t *uri);
165 static int user_allowed(printer_t *p, const char *username);
166 static void validate_job(client_t *con, ipp_attribute_t *uri);
167 static int validate_name(const char *name);
168 static int validate_user(job_t *job, client_t *con,
169 const char *owner, char *username,
170 int userlen);
171
172
173 /*
174 * 'ProcessIPPRequest()' - Process an incoming IPP request...
175 */
176
177 int /* O - 1 on success, 0 on failure */
178 ProcessIPPRequest(client_t *con) /* I - Client connection */
179 {
180 ipp_tag_t group; /* Current group tag */
181 ipp_attribute_t *attr; /* Current attribute */
182 ipp_attribute_t *charset; /* Character set attribute */
183 ipp_attribute_t *language; /* Language attribute */
184 ipp_attribute_t *uri; /* Printer URI attribute */
185 ipp_attribute_t *username; /* requesting-user-name attr */
186 int sub_id; /* Subscription ID */
187
188
189 LogMessage(L_DEBUG2, "ProcessIPPRequest(%p[%d]): operation_id = %04x",
190 con, con->http.fd, con->request->request.op.operation_id);
191
192 /*
193 * First build an empty response message for this request...
194 */
195
196 con->response = ippNew();
197
198 con->response->request.status.version[0] = con->request->request.op.version[0];
199 con->response->request.status.version[1] = con->request->request.op.version[1];
200 con->response->request.status.request_id = con->request->request.op.request_id;
201
202 /*
203 * Then validate the request header and required attributes...
204 */
205
206 if (con->request->request.any.version[0] != 1)
207 {
208 /*
209 * Return an error, since we only support IPP 1.x.
210 */
211
212 LogMessage(L_ERROR, "ProcessIPPRequest: bad request version (%d.%d)!",
213 con->request->request.any.version[0],
214 con->request->request.any.version[1]);
215
216 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
217 "%04X %s Bad request version (%d.%d)",
218 IPP_VERSION_NOT_SUPPORTED, con->http.hostname,
219 con->request->request.any.version[0],
220 con->request->request.any.version[1]);
221
222 send_ipp_error(con, IPP_VERSION_NOT_SUPPORTED);
223 }
224 else if (con->request->attrs == NULL)
225 {
226 LogMessage(L_ERROR, "ProcessIPPRequest: no attributes in request!");
227
228 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
229 "%04X %s No attributes in request",
230 IPP_BAD_REQUEST, con->http.hostname);
231
232 send_ipp_error(con, IPP_BAD_REQUEST);
233 }
234 else
235 {
236 /*
237 * Make sure that the attributes are provided in the correct order and
238 * don't repeat groups...
239 */
240
241 for (attr = con->request->attrs, group = attr->group_tag;
242 attr != NULL;
243 attr = attr->next)
244 if (attr->group_tag < group)
245 {
246 /*
247 * Out of order; return an error...
248 */
249
250 LogMessage(L_ERROR, "ProcessIPPRequest: attribute groups are out of order!");
251
252 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
253 "%04X %s Attribute groups are out of order",
254 IPP_BAD_REQUEST, con->http.hostname);
255
256 send_ipp_error(con, IPP_BAD_REQUEST);
257 break;
258 }
259 else
260 group = attr->group_tag;
261
262 if (attr == NULL)
263 {
264 /*
265 * Then make sure that the first three attributes are:
266 *
267 * attributes-charset
268 * attributes-natural-language
269 * printer-uri/job-uri
270 */
271
272 attr = con->request->attrs;
273 if (attr && !strcmp(attr->name, "attributes-charset") &&
274 attr->value_tag == IPP_TAG_CHARSET)
275 charset = attr;
276 else
277 charset = NULL;
278
279 if (attr)
280 attr = attr->next;
281
282 if (attr && !strcmp(attr->name, "attributes-natural-language") &&
283 attr->value_tag == IPP_TAG_LANGUAGE)
284 language = attr;
285 else
286 language = NULL;
287
288 if ((attr = ippFindAttribute(con->request, "printer-uri", IPP_TAG_URI)) != NULL)
289 uri = attr;
290 else if ((attr = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) != NULL)
291 uri = attr;
292 else
293 uri = NULL;
294
295 if (charset)
296 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
297 "attributes-charset", NULL, charset->values[0].string.text);
298 else
299 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
300 "attributes-charset", NULL, DefaultCharset);
301
302 if (language)
303 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
304 "attributes-natural-language", NULL,
305 language->values[0].string.text);
306 else
307 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
308 "attributes-natural-language", NULL, DefaultLanguage);
309
310 if (charset == NULL || language == NULL ||
311 (uri == NULL &&
312 con->request->request.op.operation_id != CUPS_GET_DEFAULT &&
313 con->request->request.op.operation_id != CUPS_GET_PRINTERS &&
314 con->request->request.op.operation_id != CUPS_GET_CLASSES &&
315 con->request->request.op.operation_id != CUPS_GET_DEVICES &&
316 con->request->request.op.operation_id != CUPS_GET_PPDS))
317 {
318 /*
319 * Return an error, since attributes-charset,
320 * attributes-natural-language, and printer-uri/job-uri are required
321 * for all operations.
322 */
323
324 if (!charset)
325 {
326 LogMessage(L_ERROR, "ProcessIPPRequest: missing attributes-charset attribute!");
327
328 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
329 "%04X %s Missing attributes-charset attribute",
330 IPP_BAD_REQUEST, con->http.hostname);
331 }
332
333 if (!language)
334 {
335 LogMessage(L_ERROR, "ProcessIPPRequest: missing attributes-natural-language attribute!");
336
337 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
338 "%04X %s Missing attributes-natural-language attribute",
339 IPP_BAD_REQUEST, con->http.hostname);
340 }
341
342 if (!uri)
343 {
344 LogMessage(L_ERROR, "ProcessIPPRequest: missing printer-uri or job-uri attribute!");
345
346 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
347 "%04X %s Missing printer-uri or job-uri attribute",
348 IPP_BAD_REQUEST, con->http.hostname);
349 }
350
351 LogMessage(L_DEBUG, "Request attributes follow...");
352
353 for (attr = con->request->attrs; attr != NULL; attr = attr->next)
354 LogMessage(L_DEBUG, "attr \"%s\": group_tag = %x, value_tag = %x",
355 attr->name ? attr->name : "(null)", attr->group_tag,
356 attr->value_tag);
357
358 LogMessage(L_DEBUG, "End of attributes...");
359
360 send_ipp_error(con, IPP_BAD_REQUEST);
361 }
362 else
363 {
364 /*
365 * OK, all the checks pass so far; make sure requesting-user-name is
366 * not "root" from a remote host...
367 */
368
369 if ((username = ippFindAttribute(con->request, "requesting-user-name",
370 IPP_TAG_NAME)) != NULL)
371 {
372 /*
373 * Check for root user...
374 */
375
376 if (strcmp(username->values[0].string.text, "root") == 0 &&
377 strcasecmp(con->http.hostname, "localhost") != 0 &&
378 strcmp(con->username, "root") != 0)
379 {
380 /*
381 * Remote unauthenticated user masquerading as local root...
382 */
383
384 SetString(&(username->values[0].string.text), RemoteRoot);
385 }
386 }
387
388 if ((attr = ippFindAttribute(con->request, "notify-subscription-id",
389 IPP_TAG_INTEGER)) != NULL)
390 sub_id = attr->values[0].integer;
391 else
392 sub_id = 0;
393
394 /*
395 * Then try processing the operation...
396 */
397
398 if (uri)
399 LogMessage(L_DEBUG2, "ProcessIPPRequest: URI=\"%s\"",
400 uri->values[0].string.text);
401
402 switch (con->request->request.op.operation_id)
403 {
404 case IPP_PRINT_JOB :
405 print_job(con, uri);
406 break;
407
408 case IPP_VALIDATE_JOB :
409 validate_job(con, uri);
410 break;
411
412 case IPP_CREATE_JOB :
413 create_job(con, uri);
414 break;
415
416 case IPP_SEND_DOCUMENT :
417 send_document(con, uri);
418 break;
419
420 case IPP_CANCEL_JOB :
421 cancel_job(con, uri);
422 break;
423
424 case IPP_GET_JOB_ATTRIBUTES :
425 get_job_attrs(con, uri);
426 break;
427
428 case IPP_GET_JOBS :
429 get_jobs(con, uri);
430 break;
431
432 case IPP_GET_PRINTER_ATTRIBUTES :
433 get_printer_attrs(con, uri);
434 break;
435
436 case IPP_HOLD_JOB :
437 hold_job(con, uri);
438 break;
439
440 case IPP_RELEASE_JOB :
441 release_job(con, uri);
442 break;
443
444 case IPP_RESTART_JOB :
445 restart_job(con, uri);
446 break;
447
448 case IPP_PAUSE_PRINTER :
449 stop_printer(con, uri);
450 break;
451
452 case IPP_RESUME_PRINTER :
453 start_printer(con, uri);
454 break;
455
456 case IPP_PURGE_JOBS :
457 cancel_all_jobs(con, uri);
458 break;
459
460 case IPP_SET_JOB_ATTRIBUTES :
461 set_job_attrs(con, uri);
462 break;
463
464 case CUPS_GET_DEFAULT :
465 get_default(con);
466 break;
467
468 case CUPS_GET_PRINTERS :
469 get_printers(con, 0);
470 break;
471
472 case CUPS_GET_CLASSES :
473 get_printers(con, CUPS_PRINTER_CLASS);
474 break;
475
476 case CUPS_ADD_PRINTER :
477 add_printer(con, uri);
478 break;
479
480 case CUPS_DELETE_PRINTER :
481 delete_printer(con, uri);
482 break;
483
484 case CUPS_ADD_CLASS :
485 add_class(con, uri);
486 break;
487
488 case CUPS_DELETE_CLASS :
489 delete_printer(con, uri);
490 break;
491
492 case CUPS_ACCEPT_JOBS :
493 case IPP_ENABLE_PRINTER :
494 accept_jobs(con, uri);
495 break;
496
497 case CUPS_REJECT_JOBS :
498 case IPP_DISABLE_PRINTER :
499 reject_jobs(con, uri);
500 break;
501
502 case CUPS_SET_DEFAULT :
503 set_default(con, uri);
504 break;
505
506 case CUPS_GET_DEVICES :
507 get_devices(con);
508 break;
509
510 case CUPS_GET_PPDS :
511 get_ppds(con);
512 break;
513
514 case CUPS_MOVE_JOB :
515 move_job(con, uri);
516 break;
517
518 case IPP_CREATE_PRINTER_SUBSCRIPTION :
519 case IPP_CREATE_JOB_SUBSCRIPTION :
520 create_subscription(con, uri);
521 break;
522
523 case IPP_GET_SUBSCRIPTION_ATTRIBUTES :
524 get_subscription_attrs(con, sub_id);
525 break;
526
527 case IPP_GET_SUBSCRIPTIONS :
528 get_subscriptions(con, uri);
529 break;
530
531 case IPP_RENEW_SUBSCRIPTION :
532 renew_subscription(con, sub_id);
533 break;
534
535 case IPP_CANCEL_SUBSCRIPTION :
536 cancel_subscription(con, sub_id);
537 break;
538
539 case IPP_GET_NOTIFICATIONS :
540 get_notifications(con, sub_id);
541 break;
542
543 default :
544 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
545 "%04X %s Operation %04X (%s) not supported",
546 IPP_OPERATION_NOT_SUPPORTED, con->http.hostname,
547 con->request->request.op.operation_id,
548 ippOpString(con->request->request.op.operation_id));
549
550 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
551 break;
552 }
553 }
554 }
555 }
556
557 if (con->response)
558 {
559 /*
560 * Sending data from the scheduler...
561 */
562
563 LogMessage(L_DEBUG, "ProcessIPPRequest: %d status_code=%x (%s)",
564 con->http.fd, con->response->request.status.status_code,
565 ippErrorString(con->response->request.status.status_code));
566
567 if (SendHeader(con, HTTP_OK, "application/ipp"))
568 {
569 if (con->http.version == HTTP_1_1)
570 {
571 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
572
573 httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n");
574 }
575 else
576 {
577 con->http.data_encoding = HTTP_ENCODE_LENGTH;
578 con->http.data_remaining = ippLength(con->response);
579
580 httpPrintf(HTTP(con), "Content-Length: %d\r\n\r\n",
581 con->http.data_remaining);
582 }
583
584 LogMessage(L_DEBUG2, "ProcessIPPRequest: Adding fd %d to OutputSet...",
585 con->http.fd);
586
587 FD_SET(con->http.fd, OutputSet);
588
589 /*
590 * Tell the caller the response header was sent successfully...
591 */
592
593 return (1);
594 }
595 else
596 {
597 /*
598 * Tell the caller the response header could not be sent...
599 */
600
601 return (0);
602 }
603 }
604 else
605 {
606 /*
607 * Sending data from a subprocess like cups-deviced; tell the caller
608 * everything is A-OK so far...
609 */
610
611 return (1);
612 }
613 }
614
615
616 /*
617 * 'accept_jobs()' - Accept print jobs to a printer.
618 */
619
620 static void
621 accept_jobs(client_t *con, /* I - Client connection */
622 ipp_attribute_t *uri) /* I - Printer or class URI */
623 {
624 cups_ptype_t dtype; /* Destination type (printer or class) */
625 char method[HTTP_MAX_URI], /* Method portion of URI */
626 username[HTTP_MAX_URI], /* Username portion of URI */
627 host[HTTP_MAX_URI], /* Host portion of URI */
628 resource[HTTP_MAX_URI]; /* Resource portion of URI */
629 int port; /* Port portion of URI */
630 const char *name; /* Printer name */
631 printer_t *printer; /* Printer data */
632
633
634 LogMessage(L_DEBUG2, "accept_jobs(%p[%d], %s)\n", con, con->http.fd,
635 uri->values[0].string.text);
636
637 /*
638 * Was this operation called from the correct URI?
639 */
640
641 if (strncmp(con->uri, "/admin/", 7) != 0)
642 {
643 LogMessage(L_ERROR, "accept_jobs: admin request on bad resource \'%s\'!",
644 con->uri);
645 send_ipp_error(con, IPP_NOT_AUTHORIZED);
646 return;
647 }
648
649 /*
650 * Is the destination valid?
651 */
652
653 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
654
655 if ((name = ValidateDest(host, resource, &dtype, &printer)) == NULL)
656 {
657 /*
658 * Bad URI...
659 */
660
661 LogMessage(L_ERROR, "accept_jobs: resource name \'%s\' no good!", resource);
662 send_ipp_error(con, IPP_NOT_FOUND);
663 return;
664 }
665
666 /*
667 * Check policy...
668 */
669
670 if (!CheckPolicy(printer->op_policy_ptr, con, NULL))
671 {
672 LogMessage(L_ERROR, "accept_jobs: not authorized!");
673 send_ipp_error(con, IPP_NOT_AUTHORIZED);
674 return;
675 }
676
677 /*
678 * Accept jobs sent to the printer...
679 */
680
681 printer->accepting = 1;
682 printer->state_message[0] = '\0';
683
684 AddPrinterHistory(printer);
685
686 if (dtype & CUPS_PRINTER_CLASS)
687 SaveAllClasses();
688 else
689 SaveAllPrinters();
690
691 LogMessage(L_INFO, "Printer \'%s\' now accepting jobs (\'%s\').", name,
692 con->username);
693
694 /*
695 * Everything was ok, so return OK status...
696 */
697
698 con->response->request.status.status_code = IPP_OK;
699 }
700
701
702 /*
703 * 'add_class()' - Add a class to the system.
704 */
705
706 static void
707 add_class(client_t *con, /* I - Client connection */
708 ipp_attribute_t *uri) /* I - URI of class */
709 {
710 int i; /* Looping var */
711 char method[HTTP_MAX_URI], /* Method portion of URI */
712 username[HTTP_MAX_URI], /* Username portion of URI */
713 host[HTTP_MAX_URI], /* Host portion of URI */
714 resource[HTTP_MAX_URI]; /* Resource portion of URI */
715 int port; /* Port portion of URI */
716 printer_t *pclass, /* Class */
717 *member; /* Member printer/class */
718 cups_ptype_t dtype; /* Destination type */
719 const char *dest; /* Printer or class name */
720 ipp_attribute_t *attr; /* Printer attribute */
721 int modify; /* Non-zero if we just modified */
722
723
724 LogMessage(L_DEBUG2, "add_class(%p[%d], %s)\n", con, con->http.fd,
725 uri->values[0].string.text);
726
727 /*
728 * Was this operation called from the correct URI?
729 */
730
731 if (strncmp(con->uri, "/admin/", 7) != 0)
732 {
733 LogMessage(L_ERROR, "add_class: admin request on bad resource \'%s\'!",
734 con->uri);
735 send_ipp_error(con, IPP_NOT_AUTHORIZED);
736 return;
737 }
738
739 /*
740 * Do we have a valid URI?
741 */
742
743 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
744
745 if (strncmp(resource, "/classes/", 9) != 0 || strlen(resource) == 9)
746 {
747 /*
748 * No, return an error...
749 */
750
751 send_ipp_error(con, IPP_BAD_REQUEST);
752 return;
753 }
754
755 /*
756 * Do we have a valid printer name?
757 */
758
759 if (!validate_name(resource + 9))
760 {
761 /*
762 * No, return an error...
763 */
764
765 send_ipp_error(con, IPP_BAD_REQUEST);
766 return;
767 }
768
769 /*
770 * Check policy...
771 */
772
773 if (!CheckPolicy(DefaultPolicyPtr, con, NULL))
774 {
775 LogMessage(L_ERROR, "add_class: not authorized!");
776 send_ipp_error(con, IPP_NOT_AUTHORIZED);
777 return;
778 }
779
780 /*
781 * See if the class already exists; if not, create a new class...
782 */
783
784 if ((pclass = FindClass(resource + 9)) == NULL)
785 {
786 /*
787 * Class doesn't exist; see if we have a printer of the same name...
788 */
789
790 if ((pclass = FindPrinter(resource + 9)) != NULL &&
791 !(pclass->type & CUPS_PRINTER_REMOTE))
792 {
793 /*
794 * Yes, return an error...
795 */
796
797 send_ipp_error(con, IPP_NOT_POSSIBLE);
798 return;
799 }
800
801 /*
802 * No, add the pclass...
803 */
804
805 pclass = AddClass(resource + 9);
806 modify = 0;
807 }
808 else if (pclass->type & CUPS_PRINTER_IMPLICIT)
809 {
810 /*
811 * Rename the implicit class to "AnyClass" or remove it...
812 */
813
814 if (ImplicitAnyClasses)
815 {
816 SetStringf(&pclass->name, "Any%s", resource + 9);
817 SortPrinters();
818 }
819 else
820 DeletePrinter(pclass, 1);
821
822 /*
823 * Add the class as a new local class...
824 */
825
826 pclass = AddClass(resource + 9);
827 modify = 0;
828 }
829 else if (pclass->type & CUPS_PRINTER_REMOTE)
830 {
831 /*
832 * Rename the remote class to "Class"...
833 */
834
835 DeletePrinterFilters(pclass);
836 SetStringf(&pclass->name, "%s@%s", resource + 9, pclass->hostname);
837 SetPrinterAttrs(pclass);
838 SortPrinters();
839
840 /*
841 * Add the class as a new local class...
842 */
843
844 pclass = AddClass(resource + 9);
845 modify = 0;
846 }
847 else
848 modify = 1;
849
850 /*
851 * Look for attributes and copy them over as needed...
852 */
853
854 if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
855 SetString(&pclass->location, attr->values[0].string.text);
856
857 if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
858 SetString(&pclass->info, attr->values[0].string.text);
859
860 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
861 {
862 LogMessage(L_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
863 pclass->name, attr->values[0].boolean, pclass->accepting);
864
865 pclass->accepting = attr->values[0].boolean;
866 AddPrinterHistory(pclass);
867 }
868 if ((attr = ippFindAttribute(con->request, "printer-state", IPP_TAG_ENUM)) != NULL)
869 {
870 if (attr->values[0].integer != IPP_PRINTER_IDLE &&
871 attr->values[0].integer == IPP_PRINTER_STOPPED)
872 {
873 LogMessage(L_ERROR, "Attempt to set %s printer-state to bad value %d!",
874 pclass->name, attr->values[0].integer);
875 send_ipp_error(con, IPP_BAD_REQUEST);
876 return;
877 }
878
879 LogMessage(L_INFO, "Setting %s printer-state to %d (was %d.)", pclass->name,
880 attr->values[0].integer, pclass->state);
881
882 SetPrinterState(pclass, (ipp_pstate_t)(attr->values[0].integer), 0);
883 }
884 if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL)
885 {
886 strlcpy(pclass->state_message, attr->values[0].string.text,
887 sizeof(pclass->state_message));
888 AddPrinterHistory(pclass);
889 }
890 if ((attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_ZERO)) != NULL &&
891 !Classification)
892 {
893 SetString(&pclass->job_sheets[0], attr->values[0].string.text);
894 if (attr->num_values > 1)
895 SetString(&pclass->job_sheets[1], attr->values[1].string.text);
896 else
897 SetString(&pclass->job_sheets[1], "none");
898 }
899 if ((attr = ippFindAttribute(con->request, "requesting-user-name-allowed",
900 IPP_TAG_ZERO)) != NULL)
901 {
902 FreePrinterUsers(pclass);
903
904 pclass->deny_users = 0;
905 if (attr->value_tag == IPP_TAG_NAME &&
906 (attr->num_values > 1 ||
907 strcmp(attr->values[0].string.text, "all") != 0))
908 for (i = 0; i < attr->num_values; i ++)
909 AddPrinterUser(pclass, attr->values[i].string.text);
910 }
911 else if ((attr = ippFindAttribute(con->request, "requesting-user-name-denied",
912 IPP_TAG_ZERO)) != NULL)
913 {
914 FreePrinterUsers(pclass);
915
916 pclass->deny_users = 1;
917 if (attr->value_tag == IPP_TAG_NAME &&
918 (attr->num_values > 1 ||
919 strcmp(attr->values[0].string.text, "none") != 0))
920 for (i = 0; i < attr->num_values; i ++)
921 AddPrinterUser(pclass, attr->values[i].string.text);
922 }
923 if ((attr = ippFindAttribute(con->request, "job-quota-period",
924 IPP_TAG_INTEGER)) != NULL)
925 {
926 LogMessage(L_DEBUG, "add_class: Setting job-quota-period to %d...",
927 attr->values[0].integer);
928 FreeQuotas(pclass);
929 pclass->quota_period = attr->values[0].integer;
930 }
931 if ((attr = ippFindAttribute(con->request, "job-k-limit",
932 IPP_TAG_INTEGER)) != NULL)
933 {
934 LogMessage(L_DEBUG, "add_class: Setting job-k-limit to %d...",
935 attr->values[0].integer);
936 FreeQuotas(pclass);
937 pclass->k_limit = attr->values[0].integer;
938 }
939 if ((attr = ippFindAttribute(con->request, "job-page-limit",
940 IPP_TAG_INTEGER)) != NULL)
941 {
942 LogMessage(L_DEBUG, "add_class: Setting job-page-limit to %d...",
943 attr->values[0].integer);
944 FreeQuotas(pclass);
945 pclass->page_limit = attr->values[0].integer;
946 }
947 if ((attr = ippFindAttribute(con->request, "printer-op-policy", IPP_TAG_TEXT)) != NULL)
948 {
949 policy_t *p; /* Policy */
950
951
952 if ((p = FindPolicy(attr->values[0].string.text)) != NULL)
953 {
954 LogMessage(L_DEBUG, "add_class: Setting printer-op-policy to \"%s\"...",
955 attr->values[0].string.text);
956 SetString(&pclass->op_policy, attr->values[0].string.text);
957 pclass->op_policy_ptr = p;
958 }
959 else
960 {
961 LogMessage(L_ERROR, "add_class: Unknown printer-op-policy \"%s\"...",
962 attr->values[0].string.text);
963 send_ipp_error(con, IPP_NOT_POSSIBLE);
964 return;
965 }
966 }
967 if ((attr = ippFindAttribute(con->request, "printer-error-policy", IPP_TAG_TEXT)) != NULL)
968 {
969 if (strcmp(attr->values[0].string.text, "abort-job") &&
970 strcmp(attr->values[0].string.text, "retry-job") &&
971 strcmp(attr->values[0].string.text, "stop-printer"))
972 {
973 LogMessage(L_ERROR, "add_class: Unknown printer-error-policy \"%s\"...",
974 attr->values[0].string.text);
975 send_ipp_error(con, IPP_NOT_POSSIBLE);
976 return;
977 }
978
979 LogMessage(L_DEBUG, "add_class: Setting printer-error-policy to \"%s\"...",
980 attr->values[0].string.text);
981 SetString(&pclass->error_policy, attr->values[0].string.text);
982 }
983 if ((attr = ippFindAttribute(con->request, "member-uris", IPP_TAG_URI)) != NULL)
984 {
985 /*
986 * Clear the printer array as needed...
987 */
988
989 if (pclass->num_printers > 0)
990 {
991 free(pclass->printers);
992 pclass->num_printers = 0;
993 }
994
995 /*
996 * Add each printer or class that is listed...
997 */
998
999 for (i = 0; i < attr->num_values; i ++)
1000 {
1001 /*
1002 * Search for the printer or class URI...
1003 */
1004
1005 httpSeparate(attr->values[i].string.text, method, username, host,
1006 &port, resource);
1007
1008 if ((dest = ValidateDest(host, resource, &dtype, &member)) == NULL)
1009 {
1010 /*
1011 * Bad URI...
1012 */
1013
1014 LogMessage(L_ERROR, "add_class: resource name \'%s\' no good!", resource);
1015 send_ipp_error(con, IPP_NOT_FOUND);
1016 return;
1017 }
1018
1019 /*
1020 * Add it to the class...
1021 */
1022
1023 AddPrinterToClass(pclass, member);
1024 }
1025 }
1026
1027 /*
1028 * Update the printer class attributes and return...
1029 */
1030
1031 SetPrinterAttrs(pclass);
1032 SaveAllClasses();
1033 CheckJobs();
1034
1035 WritePrintcap();
1036
1037 if (modify)
1038 {
1039 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, pclass, NULL,
1040 "Class \'%s\' modified by \'%s\'.", pclass->name,
1041 con->username);
1042
1043 LogMessage(L_INFO, "Class \'%s\' modified by \'%s\'.", pclass->name,
1044 con->username);
1045 }
1046 else
1047 {
1048 AddPrinterHistory(pclass);
1049
1050 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, pclass, NULL,
1051 "New class \'%s\' added by \'%s\'.", pclass->name,
1052 con->username);
1053
1054 LogMessage(L_INFO, "New class \'%s\' added by \'%s\'.", pclass->name,
1055 con->username);
1056 }
1057
1058 con->response->request.status.status_code = IPP_OK;
1059 }
1060
1061
1062 /*
1063 * 'add_file()' - Add a file to a job.
1064 */
1065
1066 static int /* O - 0 on success, -1 on error */
1067 add_file(client_t *con, /* I - Connection to client */
1068 job_t *job, /* I - Job to add to */
1069 mime_type_t *filetype, /* I - Type of file */
1070 int compression) /* I - Compression */
1071 {
1072 mime_type_t **filetypes; /* New filetypes array... */
1073 int *compressions; /* New compressions array... */
1074
1075
1076 LogMessage(L_DEBUG2, "add_file(con=%p[%d], job=%d, filetype=%s/%s, compression=%d)\n",
1077 con, con->http.fd, job->id, filetype->super, filetype->type,
1078 compression);
1079
1080 /*
1081 * Add the file to the job...
1082 */
1083
1084 if (job->num_files == 0)
1085 {
1086 compressions = (int *)malloc(sizeof(int));
1087 filetypes = (mime_type_t **)malloc(sizeof(mime_type_t *));
1088 }
1089 else
1090 {
1091 compressions = (int *)realloc(job->compressions,
1092 (job->num_files + 1) * sizeof(int));
1093 filetypes = (mime_type_t **)realloc(job->filetypes,
1094 (job->num_files + 1) *
1095 sizeof(mime_type_t *));
1096 }
1097
1098 if (compressions == NULL || filetypes == NULL)
1099 {
1100 CancelJob(job->id, 1);
1101 LogMessage(L_ERROR, "add_file: unable to allocate memory for file types!");
1102 send_ipp_error(con, IPP_INTERNAL_ERROR);
1103 return (-1);
1104 }
1105
1106 job->compressions = compressions;
1107 job->compressions[job->num_files] = compression;
1108 job->filetypes = filetypes;
1109 job->filetypes[job->num_files] = filetype;
1110
1111 job->num_files ++;
1112
1113 return (0);
1114 }
1115
1116
1117 /*
1118 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
1119 * upon the job and printer state...
1120 */
1121
1122 static void
1123 add_job_state_reasons(client_t *con, /* I - Client connection */
1124 job_t *job) /* I - Job info */
1125 {
1126 printer_t *dest; /* Destination printer */
1127
1128
1129 LogMessage(L_DEBUG2, "add_job_state_reasons(%p[%d], %d)\n", con, con->http.fd,
1130 job ? job->id : 0);
1131
1132 switch (job ? job->state->values[0].integer : IPP_JOB_CANCELLED)
1133 {
1134 case IPP_JOB_PENDING :
1135 if (job->dtype & CUPS_PRINTER_CLASS)
1136 dest = FindClass(job->dest);
1137 else
1138 dest = FindPrinter(job->dest);
1139
1140 if (dest != NULL && dest->state == IPP_PRINTER_STOPPED)
1141 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1142 "job-state-reasons", NULL, "printer-stopped");
1143 else
1144 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1145 "job-state-reasons", NULL, "none");
1146 break;
1147
1148 case IPP_JOB_HELD :
1149 if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
1150 ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
1151 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1152 "job-state-reasons", NULL, "job-hold-until-specified");
1153 else
1154 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1155 "job-state-reasons", NULL, "job-incoming");
1156 break;
1157
1158 case IPP_JOB_PROCESSING :
1159 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1160 "job-state-reasons", NULL, "job-printing");
1161 break;
1162
1163 case IPP_JOB_STOPPED :
1164 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1165 "job-state-reasons", NULL, "job-stopped");
1166 break;
1167
1168 case IPP_JOB_CANCELLED :
1169 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1170 "job-state-reasons", NULL, "job-canceled-by-user");
1171 break;
1172
1173 case IPP_JOB_ABORTED :
1174 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1175 "job-state-reasons", NULL, "aborted-by-system");
1176 break;
1177
1178 case IPP_JOB_COMPLETED :
1179 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1180 "job-state-reasons", NULL, "job-completed-successfully");
1181 break;
1182 }
1183 }
1184
1185
1186 /*
1187 * 'add_job_subscriptions()' - Add any subcriptions for a job.
1188 */
1189
1190 static void
1191 add_job_subscriptions(client_t *con, /* I - Client connection */
1192 job_t *job) /* I - Newly created job */
1193 {
1194 int i; /* Looping var */
1195 ipp_attribute_t *prev, /* Previous attribute */
1196 *next, /* Next attribute */
1197 *attr; /* Current attribute */
1198 cupsd_subscription_t *sub; /* Subscription object */
1199 const char *recipient, /* notify-recipient-uri */
1200 *pullmethod; /* notify-pull-method */
1201 ipp_attribute_t *user_data; /* notify-user-data */
1202 int interval; /* notify-time-interval */
1203 unsigned mask; /* notify-events */
1204
1205
1206 /*
1207 * Find the first subscription group attribute; return if we have
1208 * none...
1209 */
1210
1211 for (attr = job->attrs->attrs, prev = NULL; attr; prev = attr, attr = attr->next)
1212 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
1213 break;
1214
1215 if (!attr)
1216 return;
1217
1218 /*
1219 * Process the subscription attributes in the request...
1220 */
1221
1222 while (attr)
1223 {
1224 recipient = NULL;
1225 pullmethod = NULL;
1226 user_data = NULL;
1227 interval = 0;
1228 mask = CUPSD_EVENT_NONE;
1229
1230 while (attr && attr->group_tag != IPP_TAG_ZERO)
1231 {
1232 if (!strcmp(attr->name, "notify-recipient") &&
1233 attr->value_tag == IPP_TAG_URI)
1234 recipient = attr->values[0].string.text;
1235 else if (!strcmp(attr->name, "notify-pull-method") &&
1236 attr->value_tag == IPP_TAG_KEYWORD)
1237 pullmethod = attr->values[0].string.text;
1238 else if (!strcmp(attr->name, "notify-charset") &&
1239 attr->value_tag == IPP_TAG_CHARSET &&
1240 strcmp(attr->values[0].string.text, "utf-8"))
1241 {
1242 send_ipp_error(con, IPP_CHARSET);
1243 return;
1244 }
1245 else if (!strcmp(attr->name, "notify-natural-language") &&
1246 attr->value_tag == IPP_TAG_LANGUAGE &&
1247 strcmp(attr->values[0].string.text, DefaultLanguage))
1248 {
1249 send_ipp_error(con, IPP_CHARSET);
1250 return;
1251 }
1252 else if (!strcmp(attr->name, "notify-user-data") &&
1253 attr->value_tag == IPP_TAG_STRING)
1254 {
1255 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
1256 {
1257 send_ipp_error(con, IPP_REQUEST_VALUE);
1258 return;
1259 }
1260
1261 user_data = attr;
1262 }
1263 else if (!strcmp(attr->name, "notify-events") &&
1264 attr->value_tag == IPP_TAG_KEYWORD)
1265 {
1266 for (i = 0; i < attr->num_values; i ++)
1267 mask |= cupsdEventValue(attr->values[i].string.text);
1268 }
1269 else if (!strcmp(attr->name, "notify-lease-time"))
1270 {
1271 send_ipp_error(con, IPP_BAD_REQUEST);
1272 return;
1273 }
1274 else if (!strcmp(attr->name, "notify-time-interval") &&
1275 attr->value_tag == IPP_TAG_INTEGER)
1276 interval = attr->values[0].integer;
1277
1278 attr = attr->next;
1279 }
1280
1281 if (!recipient && !pullmethod)
1282 break;
1283
1284 if (mask == CUPSD_EVENT_NONE)
1285 mask = CUPSD_EVENT_JOB_COMPLETED;
1286
1287 sub = cupsdAddSubscription(mask, FindDest(job->dest), job, recipient);
1288
1289 sub->interval = interval;
1290
1291 SetString(&sub->owner, job->username);
1292
1293 if (user_data)
1294 {
1295 sub->user_data_len = user_data->values[0].unknown.length;
1296 memcpy(sub->user_data, user_data->values[0].unknown.data,
1297 sub->user_data_len);
1298 }
1299
1300 if (attr)
1301 attr = attr->next;
1302 }
1303
1304 /*
1305 * Remove all of the subscription attributes from the job request...
1306 */
1307
1308 for (attr = job->attrs->attrs, prev = NULL; attr; attr = next)
1309 {
1310 next = attr->next;
1311
1312 if (attr->group_tag == IPP_TAG_SUBSCRIPTION ||
1313 attr->group_tag == IPP_TAG_ZERO)
1314 {
1315 /*
1316 * Free and remove this attribute...
1317 */
1318
1319 _ipp_free_attr(attr);
1320
1321 if (prev)
1322 prev->next = next;
1323 else
1324 job->attrs->attrs = next;
1325 }
1326 else
1327 prev = attr;
1328 }
1329
1330 job->attrs->last = prev;
1331 job->attrs->current = prev;
1332 }
1333
1334
1335 /*
1336 * 'add_printer()' - Add a printer to the system.
1337 */
1338
1339 static void
1340 add_printer(client_t *con, /* I - Client connection */
1341 ipp_attribute_t *uri) /* I - URI of printer */
1342 {
1343 int i; /* Looping var */
1344 char method[HTTP_MAX_URI], /* Method portion of URI */
1345 username[HTTP_MAX_URI], /* Username portion of URI */
1346 host[HTTP_MAX_URI], /* Host portion of URI */
1347 resource[HTTP_MAX_URI]; /* Resource portion of URI */
1348 int port; /* Port portion of URI */
1349 printer_t *printer; /* Printer/class */
1350 ipp_attribute_t *attr; /* Printer attribute */
1351 cups_file_t *fp; /* Script/PPD file */
1352 char line[1024]; /* Line from file... */
1353 char srcfile[1024], /* Source Script/PPD file */
1354 dstfile[1024]; /* Destination Script/PPD file */
1355 int modify; /* Non-zero if we are modifying */
1356
1357
1358 LogMessage(L_DEBUG2, "add_printer(%p[%d], %s)\n", con, con->http.fd,
1359 uri->values[0].string.text);
1360
1361 /*
1362 * Was this operation called from the correct URI?
1363 */
1364
1365 if (strncmp(con->uri, "/admin/", 7) != 0)
1366 {
1367 LogMessage(L_ERROR, "add_printer: admin request on bad resource \'%s\'!",
1368 con->uri);
1369 send_ipp_error(con, IPP_NOT_AUTHORIZED);
1370 return;
1371 }
1372
1373 /*
1374 * Do we have a valid URI?
1375 */
1376
1377 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1378
1379 if (strncmp(resource, "/printers/", 10) != 0 || strlen(resource) == 10)
1380 {
1381 /*
1382 * No, return an error...
1383 */
1384
1385 LogMessage(L_ERROR, "add_printer: bad printer URI \"%s\"!",
1386 uri->values[0].string.text);
1387 send_ipp_error(con, IPP_BAD_REQUEST);
1388 return;
1389 }
1390
1391 /*
1392 * Do we have a valid printer name?
1393 */
1394
1395 if (!validate_name(resource + 10))
1396 {
1397 /*
1398 * No, return an error...
1399 */
1400
1401 send_ipp_error(con, IPP_BAD_REQUEST);
1402 return;
1403 }
1404
1405 /*
1406 * Check policy...
1407 */
1408
1409 if (!CheckPolicy(DefaultPolicyPtr, con, NULL))
1410 {
1411 LogMessage(L_ERROR, "add_printer: not authorized!");
1412 send_ipp_error(con, IPP_NOT_AUTHORIZED);
1413 return;
1414 }
1415
1416 /*
1417 * See if the printer already exists; if not, create a new printer...
1418 */
1419
1420 if ((printer = FindPrinter(resource + 10)) == NULL)
1421 {
1422 /*
1423 * Printer doesn't exist; see if we have a class of the same name...
1424 */
1425
1426 if ((printer = FindClass(resource + 10)) != NULL &&
1427 !(printer->type & CUPS_PRINTER_REMOTE))
1428 {
1429 /*
1430 * Yes, return an error...
1431 */
1432
1433 LogMessage(L_ERROR, "add_printer: \"%s\" already exists as a class!",
1434 resource + 10);
1435 send_ipp_error(con, IPP_NOT_POSSIBLE);
1436 return;
1437 }
1438
1439 /*
1440 * No, add the printer...
1441 */
1442
1443 printer = AddPrinter(resource + 10);
1444 modify = 0;
1445 }
1446 else if (printer->type & CUPS_PRINTER_IMPLICIT)
1447 {
1448 /*
1449 * Rename the implicit printer to "AnyPrinter" or delete it...
1450 */
1451
1452 if (ImplicitAnyClasses)
1453 {
1454 SetStringf(&printer->name, "Any%s", resource + 10);
1455 SortPrinters();
1456 }
1457 else
1458 DeletePrinter(printer, 1);
1459
1460 /*
1461 * Add the printer as a new local printer...
1462 */
1463
1464 printer = AddPrinter(resource + 10);
1465 modify = 0;
1466 }
1467 else if (printer->type & CUPS_PRINTER_REMOTE)
1468 {
1469 /*
1470 * Rename the remote printer to "Printer@server"...
1471 */
1472
1473 DeletePrinterFilters(printer);
1474 SetStringf(&printer->name, "%s@%s", resource + 10, printer->hostname);
1475 SetPrinterAttrs(printer);
1476 SortPrinters();
1477
1478 /*
1479 * Add the printer as a new local printer...
1480 */
1481
1482 printer = AddPrinter(resource + 10);
1483 modify = 0;
1484 }
1485 else
1486 modify = 1;
1487
1488 /*
1489 * Look for attributes and copy them over as needed...
1490 */
1491
1492 if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
1493 SetString(&printer->location, attr->values[0].string.text);
1494
1495 if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
1496 SetString(&printer->info, attr->values[0].string.text);
1497
1498 if ((attr = ippFindAttribute(con->request, "device-uri", IPP_TAG_URI)) != NULL)
1499 {
1500 ipp_attribute_t *device; /* Current device */
1501 int methodlen; /* Length of method string */
1502
1503
1504 /*
1505 * Do we have a valid device URI?
1506 */
1507
1508 httpSeparate(attr->values[0].string.text, method, username, host,
1509 &port, resource);
1510 methodlen = strlen(method);
1511
1512 if (!strcmp(method, "file"))
1513 {
1514 /*
1515 * See if the administrator has enabled file devices...
1516 */
1517
1518 if (!FileDevice && strcmp(resource, "/dev/null"))
1519 {
1520 /*
1521 * File devices are disabled and the URL is not file:/dev/null...
1522 */
1523
1524 LogMessage(L_ERROR, "add_printer: File device URIs have been disabled! "
1525 "To enable, see the FileDevice directive in cupsd.conf.");
1526 send_ipp_error(con, IPP_NOT_POSSIBLE);
1527 return;
1528 }
1529 }
1530 else
1531 {
1532 /*
1533 * See if the backend exists and is executable...
1534 */
1535
1536 snprintf(srcfile, sizeof(srcfile), "%s/backend/%s", ServerBin, method);
1537 if (access(srcfile, X_OK))
1538 {
1539 /*
1540 * Could not find device in list!
1541 */
1542
1543 LogMessage(L_ERROR, "add_printer: bad device-uri attribute \'%s\'!",
1544 attr->values[0].string.text);
1545 send_ipp_error(con, IPP_NOT_POSSIBLE);
1546 return;
1547 }
1548 }
1549
1550 LogMessage(L_INFO, "Setting %s device-uri to \"%s\" (was \"%s\".)",
1551 printer->name,
1552 cupsdSanitizeURI(attr->values[0].string.text, line, sizeof(line)),
1553 cupsdSanitizeURI(printer->device_uri, resource, sizeof(resource)));
1554
1555 SetString(&printer->device_uri, attr->values[0].string.text);
1556 }
1557
1558 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
1559 {
1560 LogMessage(L_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
1561 printer->name, attr->values[0].boolean, printer->accepting);
1562
1563 printer->accepting = attr->values[0].boolean;
1564 AddPrinterHistory(printer);
1565 }
1566 if ((attr = ippFindAttribute(con->request, "printer-state", IPP_TAG_ENUM)) != NULL)
1567 {
1568 if (attr->values[0].integer != IPP_PRINTER_IDLE &&
1569 attr->values[0].integer == IPP_PRINTER_STOPPED)
1570 {
1571 LogMessage(L_ERROR, "Attempt to set %s printer-state to bad value %d!",
1572 printer->name, attr->values[0].integer);
1573 send_ipp_error(con, IPP_BAD_REQUEST);
1574 return;
1575 }
1576
1577 LogMessage(L_INFO, "Setting %s printer-state to %d (was %d.)", printer->name,
1578 attr->values[0].integer, printer->state);
1579
1580 SetPrinterState(printer, (ipp_pstate_t)(attr->values[0].integer), 0);
1581 }
1582 if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL)
1583 {
1584 strlcpy(printer->state_message, attr->values[0].string.text,
1585 sizeof(printer->state_message));
1586 AddPrinterHistory(printer);
1587 }
1588 if ((attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_ZERO)) != NULL &&
1589 !Classification)
1590 {
1591 SetString(&printer->job_sheets[0], attr->values[0].string.text);
1592 if (attr->num_values > 1)
1593 SetString(&printer->job_sheets[1], attr->values[1].string.text);
1594 else
1595 SetString(&printer->job_sheets[1], "none");
1596 }
1597 if ((attr = ippFindAttribute(con->request, "requesting-user-name-allowed",
1598 IPP_TAG_ZERO)) != NULL)
1599 {
1600 FreePrinterUsers(printer);
1601
1602 printer->deny_users = 0;
1603 if (attr->value_tag == IPP_TAG_NAME &&
1604 (attr->num_values > 1 ||
1605 strcmp(attr->values[0].string.text, "all") != 0))
1606 for (i = 0; i < attr->num_values; i ++)
1607 AddPrinterUser(printer, attr->values[i].string.text);
1608 }
1609 else if ((attr = ippFindAttribute(con->request, "requesting-user-name-denied",
1610 IPP_TAG_ZERO)) != NULL)
1611 {
1612 FreePrinterUsers(printer);
1613
1614 printer->deny_users = 1;
1615 if (attr->value_tag == IPP_TAG_NAME &&
1616 (attr->num_values > 1 ||
1617 strcmp(attr->values[0].string.text, "none") != 0))
1618 for (i = 0; i < attr->num_values; i ++)
1619 AddPrinterUser(printer, attr->values[i].string.text);
1620 }
1621 if ((attr = ippFindAttribute(con->request, "job-quota-period",
1622 IPP_TAG_INTEGER)) != NULL)
1623 {
1624 LogMessage(L_DEBUG, "add_printer: Setting job-quota-period to %d...",
1625 attr->values[0].integer);
1626 FreeQuotas(printer);
1627 printer->quota_period = attr->values[0].integer;
1628 }
1629 if ((attr = ippFindAttribute(con->request, "job-k-limit",
1630 IPP_TAG_INTEGER)) != NULL)
1631 {
1632 LogMessage(L_DEBUG, "add_printer: Setting job-k-limit to %d...",
1633 attr->values[0].integer);
1634 FreeQuotas(printer);
1635 printer->k_limit = attr->values[0].integer;
1636 }
1637 if ((attr = ippFindAttribute(con->request, "job-page-limit",
1638 IPP_TAG_INTEGER)) != NULL)
1639 {
1640 LogMessage(L_DEBUG, "add_printer: Setting job-page-limit to %d...",
1641 attr->values[0].integer);
1642 FreeQuotas(printer);
1643 printer->page_limit = attr->values[0].integer;
1644 }
1645 if ((attr = ippFindAttribute(con->request, "printer-op-policy", IPP_TAG_TEXT)) != NULL)
1646 {
1647 policy_t *p; /* Policy */
1648
1649
1650 if ((p = FindPolicy(attr->values[0].string.text)) != NULL)
1651 {
1652 LogMessage(L_DEBUG, "add_printer: Setting printer-op-policy to \"%s\"...",
1653 attr->values[0].string.text);
1654 SetString(&printer->op_policy, attr->values[0].string.text);
1655 printer->op_policy_ptr = p;
1656 }
1657 else
1658 {
1659 LogMessage(L_ERROR, "add_printer: Unknown printer-op-policy \"%s\"...",
1660 attr->values[0].string.text);
1661 send_ipp_error(con, IPP_NOT_POSSIBLE);
1662 return;
1663 }
1664 }
1665 if ((attr = ippFindAttribute(con->request, "printer-error-policy", IPP_TAG_TEXT)) != NULL)
1666 {
1667 if (strcmp(attr->values[0].string.text, "abort-job") &&
1668 strcmp(attr->values[0].string.text, "retry-job") &&
1669 strcmp(attr->values[0].string.text, "stop-printer"))
1670 {
1671 LogMessage(L_ERROR, "add_printer: Unknown printer-error-policy \"%s\"...",
1672 attr->values[0].string.text);
1673 send_ipp_error(con, IPP_NOT_POSSIBLE);
1674 return;
1675 }
1676
1677 LogMessage(L_DEBUG, "add_printer: Setting printer-error-policy to \"%s\"...",
1678 attr->values[0].string.text);
1679 SetString(&printer->error_policy, attr->values[0].string.text);
1680 }
1681
1682 /*
1683 * See if we have all required attributes...
1684 */
1685
1686 if (!printer->device_uri)
1687 SetString(&printer->device_uri, "file:/dev/null");
1688
1689 /*
1690 * See if we have an interface script or PPD file attached to the request...
1691 */
1692
1693 if (con->filename)
1694 {
1695 strlcpy(srcfile, con->filename, sizeof(srcfile));
1696
1697 if ((fp = cupsFileOpen(srcfile, "rb")) != NULL)
1698 {
1699 /*
1700 * Yes; get the first line from it...
1701 */
1702
1703 line[0] = '\0';
1704 cupsFileGets(fp, line, sizeof(line));
1705 cupsFileClose(fp);
1706
1707 /*
1708 * Then see what kind of file it is...
1709 */
1710
1711 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
1712 printer->name);
1713
1714 if (strncmp(line, "*PPD-Adobe", 10) == 0)
1715 {
1716 /*
1717 * The new file is a PPD file, so remove any old interface script
1718 * that might be lying around...
1719 */
1720
1721 unlink(dstfile);
1722 }
1723 else
1724 {
1725 /*
1726 * This must be an interface script, so move the file over to the
1727 * interfaces directory and make it executable...
1728 */
1729
1730 if (copy_file(srcfile, dstfile))
1731 {
1732 LogMessage(L_ERROR, "add_printer: Unable to copy interface script from %s to %s - %s!",
1733 srcfile, dstfile, strerror(errno));
1734 send_ipp_error(con, IPP_INTERNAL_ERROR);
1735 return;
1736 }
1737 else
1738 {
1739 LogMessage(L_DEBUG, "add_printer: Copied interface script successfully!");
1740 chmod(dstfile, 0755);
1741 }
1742 }
1743
1744 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
1745 printer->name);
1746
1747 if (strncmp(line, "*PPD-Adobe", 10) == 0)
1748 {
1749 /*
1750 * The new file is a PPD file, so move the file over to the
1751 * ppd directory and make it readable by all...
1752 */
1753
1754 if (copy_file(srcfile, dstfile))
1755 {
1756 LogMessage(L_ERROR, "add_printer: Unable to copy PPD file from %s to %s - %s!",
1757 srcfile, dstfile, strerror(errno));
1758 send_ipp_error(con, IPP_INTERNAL_ERROR);
1759 return;
1760 }
1761 else
1762 {
1763 LogMessage(L_DEBUG, "add_printer: Copied PPD file successfully!");
1764 chmod(dstfile, 0644);
1765 }
1766 }
1767 else
1768 {
1769 /*
1770 * This must be an interface script, so remove any old PPD file that
1771 * may be lying around...
1772 */
1773
1774 unlink(dstfile);
1775 }
1776 }
1777 }
1778 else if ((attr = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME)) != NULL)
1779 {
1780 if (strcmp(attr->values[0].string.text, "raw") == 0)
1781 {
1782 /*
1783 * Raw driver, remove any existing PPD or interface script files.
1784 */
1785
1786 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
1787 printer->name);
1788 unlink(dstfile);
1789
1790 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
1791 printer->name);
1792 unlink(dstfile);
1793 }
1794 else
1795 {
1796 /*
1797 * PPD model file...
1798 */
1799
1800 snprintf(srcfile, sizeof(srcfile), "%s/model/%s", DataDir,
1801 attr->values[0].string.text);
1802
1803 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
1804 printer->name);
1805 unlink(dstfile);
1806
1807 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
1808 printer->name);
1809
1810 if (copy_model(srcfile, dstfile))
1811 {
1812 LogMessage(L_ERROR, "add_printer: Unable to copy PPD file from %s to %s - %s!",
1813 srcfile, dstfile, strerror(errno));
1814 send_ipp_error(con, IPP_INTERNAL_ERROR);
1815 return;
1816 }
1817 else
1818 {
1819 LogMessage(L_DEBUG, "add_printer: Copied PPD file successfully!");
1820 chmod(dstfile, 0644);
1821 }
1822 }
1823 }
1824
1825 /*
1826 * Make this printer the default if there is none...
1827 */
1828
1829 if (DefaultPrinter == NULL)
1830 DefaultPrinter = printer;
1831
1832 /*
1833 * Update the printer attributes and return...
1834 */
1835
1836 SetPrinterAttrs(printer);
1837 SaveAllPrinters();
1838
1839 if (printer->job != NULL)
1840 {
1841 job_t *job;
1842
1843 /*
1844 * Stop the current job and then restart it below...
1845 */
1846
1847 job = (job_t *)printer->job;
1848
1849 StopJob(job->id, 1);
1850 job->state->values[0].integer = IPP_JOB_PENDING;
1851 }
1852
1853 CheckJobs();
1854
1855 WritePrintcap();
1856
1857 if (modify)
1858 {
1859 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, printer, NULL,
1860 "Printer \'%s\' modified by \'%s\'.", printer->name,
1861 con->username);
1862
1863 LogMessage(L_INFO, "Printer \'%s\' modified by \'%s\'.", printer->name,
1864 con->username);
1865 }
1866 else
1867 {
1868 AddPrinterHistory(printer);
1869
1870 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, printer, NULL,
1871 "New printer \'%s\' added by \'%s\'.", printer->name,
1872 con->username);
1873
1874 LogMessage(L_INFO, "New printer \'%s\' added by \'%s\'.", printer->name,
1875 con->username);
1876 }
1877
1878 con->response->request.status.status_code = IPP_OK;
1879 }
1880
1881
1882 /*
1883 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
1884 * based upon the printer state...
1885 */
1886
1887 static void
1888 add_printer_state_reasons(
1889 client_t *con, /* I - Client connection */
1890 printer_t *p) /* I - Printer info */
1891 {
1892 LogMessage(L_DEBUG2, "add_printer_state_reasons(%p[%d], %p[%s])\n",
1893 con, con->http.fd, p, p->name);
1894
1895 if (p->num_reasons == 0)
1896 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1897 "printer-state-reasons", NULL,
1898 p->state == IPP_PRINTER_STOPPED ? "paused" : "none");
1899 else
1900 ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1901 "printer-state-reasons", p->num_reasons, NULL,
1902 (const char * const *)p->reasons);
1903 }
1904
1905
1906 /*
1907 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
1908 * the specified printer or class.
1909 */
1910
1911 static void
1912 add_queued_job_count(client_t *con, /* I - Client connection */
1913 printer_t *p) /* I - Printer or class */
1914 {
1915 int count; /* Number of jobs on destination */
1916
1917
1918 LogMessage(L_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])\n",
1919 con, con->http.fd, p, p->name);
1920
1921 count = GetPrinterJobCount(p->name);
1922
1923 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1924 "queued-job-count", count);
1925 }
1926
1927
1928 /*
1929 * 'cancel_all_jobs()' - Cancel all print jobs.
1930 */
1931
1932 static void
1933 cancel_all_jobs(client_t *con, /* I - Client connection */
1934 ipp_attribute_t *uri) /* I - Job or Printer URI */
1935 {
1936 const char *dest; /* Destination */
1937 cups_ptype_t dtype; /* Destination type */
1938 char method[HTTP_MAX_URI], /* Method portion of URI */
1939 userpass[HTTP_MAX_URI], /* Username portion of URI */
1940 host[HTTP_MAX_URI], /* Host portion of URI */
1941 resource[HTTP_MAX_URI]; /* Resource portion of URI */
1942 int port; /* Port portion of URI */
1943 ipp_attribute_t *attr; /* Attribute in request */
1944 const char *username; /* Username */
1945 int purge; /* Purge? */
1946 printer_t *printer; /* Printer */
1947
1948
1949 LogMessage(L_DEBUG2, "cancel_all_jobs(%p[%d], %s)\n", con, con->http.fd,
1950 uri->values[0].string.text);
1951
1952 /*
1953 * Was this operation called from the correct URI?
1954 */
1955
1956 if (strncmp(con->uri, "/admin/", 7) &&
1957 strncmp(con->uri, "/jobs/", 7))
1958 {
1959 LogMessage(L_ERROR, "cancel_all_jobs: admin request on bad resource \'%s\'!",
1960 con->uri);
1961 send_ipp_error(con, IPP_NOT_AUTHORIZED);
1962 return;
1963 }
1964
1965 /*
1966 * See if we have a printer URI...
1967 */
1968
1969 if (strcmp(uri->name, "printer-uri"))
1970 {
1971 LogMessage(L_ERROR, "cancel_all_jobs: bad %s attribute \'%s\'!",
1972 uri->name, uri->values[0].string.text);
1973 send_ipp_error(con, IPP_BAD_REQUEST);
1974 return;
1975 }
1976
1977 /*
1978 * Get the username (if any) for the jobs we want to cancel (only if
1979 * "my-jobs" is specified...
1980 */
1981
1982 if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL &&
1983 attr->values[0].boolean)
1984 {
1985 if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
1986 username = attr->values[0].string.text;
1987 else
1988 {
1989 LogMessage(L_ERROR, "cancel_all_jobs: missing requesting-user-name attribute!");
1990 send_ipp_error(con, IPP_BAD_REQUEST);
1991 return;
1992 }
1993 }
1994 else
1995 username = NULL;
1996
1997 if ((!username ||
1998 (username && con->username[0] && strcmp(username, con->username))) &&
1999 strncmp(con->uri, "/admin/", 7))
2000 {
2001 LogMessage(L_ERROR, "cancel_all_jobs: only administrators can cancel "
2002 "other users\' jobs!");
2003 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2004 return;
2005 }
2006
2007 /*
2008 * Look for the "purge-jobs" attribute...
2009 */
2010
2011 if ((attr = ippFindAttribute(con->request, "purge-jobs", IPP_TAG_BOOLEAN)) != NULL)
2012 purge = attr->values[0].boolean;
2013 else
2014 purge = 1;
2015
2016 /*
2017 * And if the destination is valid...
2018 */
2019
2020 httpSeparate(uri->values[0].string.text, method, userpass, host, &port,
2021 resource);
2022
2023 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
2024 {
2025 /*
2026 * Bad URI?
2027 */
2028
2029 if (strcmp(resource, "/printers/") != 0)
2030 {
2031 LogMessage(L_ERROR, "cancel_all_jobs: resource name \'%s\' no good!", resource);
2032 send_ipp_error(con, IPP_NOT_FOUND);
2033 return;
2034 }
2035
2036 /*
2037 * Check policy...
2038 */
2039
2040 if (!CheckPolicy(DefaultPolicyPtr, con, NULL))
2041 {
2042 LogMessage(L_ERROR, "cancel_all_jobs: not authorized!");
2043 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2044 return;
2045 }
2046
2047 /*
2048 * Cancel all jobs on all printers...
2049 */
2050
2051 CancelJobs(NULL, username, purge);
2052
2053 LogMessage(L_INFO, "All jobs were %s by \'%s\'.",
2054 purge ? "purged" : "cancelled", con->username);
2055 }
2056 else
2057 {
2058 /*
2059 * Check policy...
2060 */
2061
2062 if (!CheckPolicy(printer->op_policy_ptr, con, NULL))
2063 {
2064 LogMessage(L_ERROR, "cancel_all_jobs: not authorized!");
2065 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2066 return;
2067 }
2068
2069 /*
2070 * Cancel all of the jobs on the named printer...
2071 */
2072
2073 CancelJobs(dest, username, purge);
2074
2075 LogMessage(L_INFO, "All jobs on \'%s\' were %s by \'%s\'.", dest,
2076 purge ? "purged" : "cancelled", con->username);
2077 }
2078
2079 con->response->request.status.status_code = IPP_OK;
2080 }
2081
2082
2083 /*
2084 * 'cancel_job()' - Cancel a print job.
2085 */
2086
2087 static void
2088 cancel_job(client_t *con, /* I - Client connection */
2089 ipp_attribute_t *uri) /* I - Job or Printer URI */
2090 {
2091 ipp_attribute_t *attr; /* Current attribute */
2092 int jobid; /* Job ID */
2093 char method[HTTP_MAX_URI], /* Method portion of URI */
2094 username[HTTP_MAX_URI], /* Username portion of URI */
2095 host[HTTP_MAX_URI], /* Host portion of URI */
2096 resource[HTTP_MAX_URI]; /* Resource portion of URI */
2097 int port; /* Port portion of URI */
2098 job_t *job; /* Job information */
2099 const char *dest; /* Destination */
2100 cups_ptype_t dtype; /* Destination type (printer or class) */
2101 printer_t *printer; /* Printer data */
2102
2103
2104 LogMessage(L_DEBUG2, "cancel_job(%p[%d], %s)\n", con, con->http.fd,
2105 uri->values[0].string.text);
2106
2107 /*
2108 * Verify that the POST operation was done to a valid URI.
2109 */
2110
2111 if (strncmp(con->uri, "/classes/", 9) != 0 &&
2112 strncmp(con->uri, "/jobs/", 5) != 0 &&
2113 strncmp(con->uri, "/printers/", 10) != 0)
2114 {
2115 LogMessage(L_ERROR, "cancel_job: cancel request on bad resource \'%s\'!",
2116 con->uri);
2117 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2118 return;
2119 }
2120
2121 /*
2122 * See if we have a job URI or a printer URI...
2123 */
2124
2125 if (strcmp(uri->name, "printer-uri") == 0)
2126 {
2127 /*
2128 * Got a printer URI; see if we also have a job-id attribute...
2129 */
2130
2131 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
2132 {
2133 LogMessage(L_ERROR, "cancel_job: got a printer-uri attribute but no job-id!");
2134 send_ipp_error(con, IPP_BAD_REQUEST);
2135 return;
2136 }
2137
2138 if ((jobid = attr->values[0].integer) == 0)
2139 {
2140 /*
2141 * Find the current job on the specified printer...
2142 */
2143
2144 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
2145
2146 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
2147 {
2148 /*
2149 * Bad URI...
2150 */
2151
2152 LogMessage(L_ERROR, "cancel_job: resource name \'%s\' no good!", resource);
2153 send_ipp_error(con, IPP_NOT_FOUND);
2154 return;
2155 }
2156
2157 /*
2158 * See if the printer is currently printing a job...
2159 */
2160
2161 if (printer->job)
2162 jobid = ((job_t *)printer->job)->id;
2163 else
2164 {
2165 /*
2166 * No, see if there are any pending jobs...
2167 */
2168
2169 for (job = Jobs; job != NULL; job = job->next)
2170 if (job->state->values[0].integer <= IPP_JOB_PROCESSING &&
2171 strcasecmp(job->dest, dest) == 0)
2172 break;
2173
2174 if (job != NULL)
2175 jobid = job->id;
2176 else
2177 {
2178 LogMessage(L_ERROR, "cancel_job: No active jobs on %s!", dest);
2179 send_ipp_error(con, IPP_NOT_POSSIBLE);
2180 return;
2181 }
2182 }
2183 }
2184 }
2185 else
2186 {
2187 /*
2188 * Got a job URI; parse it to get the job ID...
2189 */
2190
2191 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
2192
2193 if (strncmp(resource, "/jobs/", 6) != 0)
2194 {
2195 /*
2196 * Not a valid URI!
2197 */
2198
2199 LogMessage(L_ERROR, "cancel_job: bad job-uri attribute \'%s\'!",
2200 uri->values[0].string.text);
2201 send_ipp_error(con, IPP_BAD_REQUEST);
2202 return;
2203 }
2204
2205 jobid = atoi(resource + 6);
2206 }
2207
2208 /*
2209 * See if the job exists...
2210 */
2211
2212 if ((job = FindJob(jobid)) == NULL)
2213 {
2214 /*
2215 * Nope - return a "not found" error...
2216 */
2217
2218 LogMessage(L_ERROR, "cancel_job: job #%d doesn't exist!", jobid);
2219 send_ipp_error(con, IPP_NOT_FOUND);
2220 return;
2221 }
2222
2223 /*
2224 * See if the job is owned by the requesting user...
2225 */
2226
2227 if (!validate_user(job, con, job->username, username, sizeof(username)))
2228 {
2229 LogMessage(L_ERROR, "cancel_job: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
2230 username, jobid, job->username);
2231 send_ipp_error(con, IPP_FORBIDDEN);
2232 return;
2233 }
2234
2235 /*
2236 * See if the job is already completed, cancelled, or aborted; if so,
2237 * we can't cancel...
2238 */
2239
2240 if (job->state->values[0].integer >= IPP_JOB_CANCELLED)
2241 {
2242 LogMessage(L_ERROR, "cancel_job: job id %d is %s - can't cancel!",
2243 jobid,
2244 job->state->values[0].integer == IPP_JOB_CANCELLED ? "cancelled" :
2245 job->state->values[0].integer == IPP_JOB_ABORTED ? "aborted" :
2246 "completed");
2247 send_ipp_error(con, IPP_NOT_POSSIBLE);
2248 return;
2249 }
2250
2251 /*
2252 * Cancel the job and return...
2253 */
2254
2255 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2256 "Job cancelled by \'%s\'.", username);
2257
2258 CancelJob(jobid, 0);
2259 CheckJobs();
2260
2261 LogMessage(L_INFO, "Job %d was cancelled by \'%s\'.", jobid, username);
2262
2263 con->response->request.status.status_code = IPP_OK;
2264 }
2265
2266
2267 /*
2268 * 'cancel_subscription()' - Cancel a subscription.
2269 */
2270
2271 static void
2272 cancel_subscription(client_t *con, /* I - Client connection */
2273 int sub_id) /* I - Subscription ID */
2274 {
2275 }
2276
2277
2278 /*
2279 * 'check_quotas()' - Check quotas for a printer and user.
2280 */
2281
2282 static int /* O - 1 if OK, 0 if not */
2283 check_quotas(client_t *con, /* I - Client connection */
2284 printer_t *p) /* I - Printer or class */
2285 {
2286 int i, j; /* Looping vars */
2287 ipp_attribute_t *attr; /* Current attribute */
2288 char username[33]; /* Username */
2289 quota_t *q; /* Quota data */
2290 struct passwd *pw; /* User password data */
2291 struct group *grp; /* Group data */
2292
2293
2294 LogMessage(L_DEBUG2, "check_quotas(%p[%d], %p[%s])\n",
2295 con, con->http.fd, p, p->name);
2296
2297 /*
2298 * Check input...
2299 */
2300
2301 if (con == NULL || p == NULL)
2302 return (0);
2303
2304 /*
2305 * Figure out who is printing...
2306 */
2307
2308 attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME);
2309
2310 if (con->username[0])
2311 strlcpy(username, con->username, sizeof(username));
2312 else if (attr != NULL)
2313 {
2314 LogMessage(L_DEBUG, "check_quotas: requesting-user-name = \'%s\'",
2315 attr->values[0].string.text);
2316
2317 strlcpy(username, attr->values[0].string.text, sizeof(username));
2318 }
2319 else
2320 strcpy(username, "anonymous");
2321
2322 /*
2323 * Check global active job limits for printers and users...
2324 */
2325
2326 if (MaxJobsPerPrinter)
2327 {
2328 /*
2329 * Check if there are too many pending jobs on this printer...
2330 */
2331
2332 if (GetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
2333 {
2334 LogMessage(L_INFO, "Too many jobs for printer \"%s\"...", p->name);
2335 return (0);
2336 }
2337 }
2338
2339 if (MaxJobsPerUser)
2340 {
2341 /*
2342 * Check if there are too many pending jobs for this user...
2343 */
2344
2345 if (GetUserJobCount(username) >= MaxJobsPerUser)
2346 {
2347 LogMessage(L_INFO, "Too many jobs for user \"%s\"...", username);
2348 return (0);
2349 }
2350 }
2351
2352 /*
2353 * Check against users...
2354 */
2355
2356 if (p->num_users == 0 && p->k_limit == 0 && p->page_limit == 0)
2357 return (1);
2358
2359 if (p->num_users)
2360 {
2361 pw = getpwnam(username);
2362 endpwent();
2363
2364 for (i = 0; i < p->num_users; i ++)
2365 if (p->users[i][0] == '@')
2366 {
2367 /*
2368 * Check group membership...
2369 */
2370
2371 grp = getgrnam(p->users[i] + 1);
2372 endgrent();
2373
2374 if (grp)
2375 {
2376 /*
2377 * Check primary group...
2378 */
2379
2380 if (pw && grp->gr_gid == pw->pw_gid)
2381 break;
2382
2383 /*
2384 * Check usernames in group...
2385 */
2386
2387 for (j = 0; grp->gr_mem[j]; j ++)
2388 if (!strcasecmp(username, grp->gr_mem[j]))
2389 break;
2390
2391 if (grp->gr_mem[j])
2392 break;
2393 }
2394 }
2395 else if (!strcasecmp(username, p->users[i]))
2396 break;
2397
2398 if ((i < p->num_users) == p->deny_users)
2399 {
2400 LogMessage(L_INFO, "Denying user \"%s\" access to printer \"%s\"...",
2401 username, p->name);
2402 return (0);
2403 }
2404 }
2405
2406 /*
2407 * Check quotas...
2408 */
2409
2410 if (p->k_limit || p->page_limit)
2411 {
2412 if ((q = UpdateQuota(p, username, 0, 0)) == NULL)
2413 {
2414 LogMessage(L_ERROR, "Unable to allocate quota data for user \"%s\"!",
2415 username);
2416 return (0);
2417 }
2418
2419 if ((q->k_count >= p->k_limit && p->k_limit) ||
2420 (q->page_count >= p->page_limit && p->page_limit))
2421 {
2422 LogMessage(L_INFO, "User \"%s\" is over the quota limit...",
2423 username);
2424 return (0);
2425 }
2426 }
2427
2428 /*
2429 * If we have gotten this far, we're done!
2430 */
2431
2432 return (1);
2433 }
2434
2435
2436 /*
2437 * 'copy_attribute()' - Copy a single attribute.
2438 */
2439
2440 static ipp_attribute_t * /* O - New attribute */
2441 copy_attribute(
2442 ipp_t *to, /* O - Destination request/response */
2443 ipp_attribute_t *attr, /* I - Attribute to copy */
2444 int quickcopy) /* I - Do a quick copy? */
2445 {
2446 int i; /* Looping var */
2447 ipp_attribute_t *toattr; /* Destination attribute */
2448
2449
2450 LogMessage(L_DEBUG2, "copy_attribute(%p, %p[%s,%x,%x])\n", to, attr,
2451 attr->name ? attr->name : "(null)", attr->group_tag,
2452 attr->value_tag);
2453
2454 switch (attr->value_tag & ~IPP_TAG_COPY)
2455 {
2456 case IPP_TAG_ZERO :
2457 toattr = ippAddSeparator(to);
2458 break;
2459
2460 case IPP_TAG_INTEGER :
2461 case IPP_TAG_ENUM :
2462 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
2463 attr->name, attr->num_values, NULL);
2464
2465 for (i = 0; i < attr->num_values; i ++)
2466 toattr->values[i].integer = attr->values[i].integer;
2467 break;
2468
2469 case IPP_TAG_BOOLEAN :
2470 toattr = ippAddBooleans(to, attr->group_tag, attr->name,
2471 attr->num_values, NULL);
2472
2473 for (i = 0; i < attr->num_values; i ++)
2474 toattr->values[i].boolean = attr->values[i].boolean;
2475 break;
2476
2477 case IPP_TAG_STRING :
2478 case IPP_TAG_TEXT :
2479 case IPP_TAG_NAME :
2480 case IPP_TAG_KEYWORD :
2481 case IPP_TAG_URI :
2482 case IPP_TAG_URISCHEME :
2483 case IPP_TAG_CHARSET :
2484 case IPP_TAG_LANGUAGE :
2485 case IPP_TAG_MIMETYPE :
2486 toattr = ippAddStrings(to, attr->group_tag,
2487 (ipp_tag_t)(attr->value_tag | quickcopy),
2488 attr->name, attr->num_values, NULL, NULL);
2489
2490 if (quickcopy)
2491 {
2492 for (i = 0; i < attr->num_values; i ++)
2493 toattr->values[i].string.text = attr->values[i].string.text;
2494 }
2495 else
2496 {
2497 for (i = 0; i < attr->num_values; i ++)
2498 toattr->values[i].string.text = strdup(attr->values[i].string.text);
2499 }
2500 break;
2501
2502 case IPP_TAG_DATE :
2503 toattr = ippAddDate(to, attr->group_tag, attr->name,
2504 attr->values[0].date);
2505 break;
2506
2507 case IPP_TAG_RESOLUTION :
2508 toattr = ippAddResolutions(to, attr->group_tag, attr->name,
2509 attr->num_values, IPP_RES_PER_INCH,
2510 NULL, NULL);
2511
2512 for (i = 0; i < attr->num_values; i ++)
2513 {
2514 toattr->values[i].resolution.xres = attr->values[i].resolution.xres;
2515 toattr->values[i].resolution.yres = attr->values[i].resolution.yres;
2516 toattr->values[i].resolution.units = attr->values[i].resolution.units;
2517 }
2518 break;
2519
2520 case IPP_TAG_RANGE :
2521 toattr = ippAddRanges(to, attr->group_tag, attr->name,
2522 attr->num_values, NULL, NULL);
2523
2524 for (i = 0; i < attr->num_values; i ++)
2525 {
2526 toattr->values[i].range.lower = attr->values[i].range.lower;
2527 toattr->values[i].range.upper = attr->values[i].range.upper;
2528 }
2529 break;
2530
2531 case IPP_TAG_TEXTLANG :
2532 case IPP_TAG_NAMELANG :
2533 toattr = ippAddStrings(to, attr->group_tag,
2534 (ipp_tag_t)(attr->value_tag | quickcopy),
2535 attr->name, attr->num_values, NULL, NULL);
2536
2537 if (quickcopy)
2538 {
2539 for (i = 0; i < attr->num_values; i ++)
2540 {
2541 toattr->values[i].string.charset = attr->values[i].string.charset;
2542 toattr->values[i].string.text = attr->values[i].string.text;
2543 }
2544 }
2545 else
2546 {
2547 for (i = 0; i < attr->num_values; i ++)
2548 {
2549 if (!i)
2550 toattr->values[i].string.charset =
2551 strdup(attr->values[i].string.charset);
2552 else
2553 toattr->values[i].string.charset =
2554 toattr->values[0].string.charset;
2555
2556 toattr->values[i].string.text = strdup(attr->values[i].string.text);
2557 }
2558 }
2559 break;
2560
2561 case IPP_TAG_BEGIN_COLLECTION :
2562 toattr = ippAddCollections(to, attr->group_tag, attr->name,
2563 attr->num_values, NULL);
2564
2565 for (i = 0; i < attr->num_values; i ++)
2566 {
2567 toattr->values[i].collection = ippNew();
2568 copy_attrs(toattr->values[i].collection, attr->values[i].collection,
2569 NULL, IPP_TAG_ZERO, 0);
2570 }
2571 break;
2572
2573 default :
2574 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
2575 attr->name, attr->num_values, NULL);
2576
2577 for (i = 0; i < attr->num_values; i ++)
2578 {
2579 toattr->values[i].unknown.length = attr->values[i].unknown.length;
2580
2581 if (toattr->values[i].unknown.length > 0)
2582 {
2583 if ((toattr->values[i].unknown.data = malloc(toattr->values[i].unknown.length)) == NULL)
2584 toattr->values[i].unknown.length = 0;
2585 else
2586 memcpy(toattr->values[i].unknown.data,
2587 attr->values[i].unknown.data,
2588 toattr->values[i].unknown.length);
2589 }
2590 }
2591 break; /* anti-compiler-warning-code */
2592 }
2593
2594 return (toattr);
2595 }
2596
2597
2598 /*
2599 * 'copy_attrs()' - Copy attributes from one request to another.
2600 */
2601
2602 static void
2603 copy_attrs(ipp_t *to, /* I - Destination request */
2604 ipp_t *from, /* I - Source request */
2605 ipp_attribute_t *req, /* I - Requested attributes */
2606 ipp_tag_t group, /* I - Group to copy */
2607 int quickcopy) /* I - Do a quick copy? */
2608 {
2609 int i; /* Looping var */
2610 ipp_attribute_t *fromattr; /* Source attribute */
2611
2612
2613 LogMessage(L_DEBUG2, "copy_attrs(%p, %p, %p, %x)\n", to, from, req, group);
2614
2615 if (to == NULL || from == NULL)
2616 return;
2617
2618 if (req != NULL && strcmp(req->values[0].string.text, "all") == 0)
2619 req = NULL; /* "all" means no filter... */
2620
2621 for (fromattr = from->attrs; fromattr != NULL; fromattr = fromattr->next)
2622 {
2623 /*
2624 * Filter attributes as needed...
2625 */
2626
2627 if (group != IPP_TAG_ZERO && fromattr->group_tag != group &&
2628 fromattr->group_tag != IPP_TAG_ZERO)
2629 continue;
2630
2631 if (req != NULL && fromattr->name != NULL)
2632 {
2633 for (i = 0; i < req->num_values; i ++)
2634 if (strcmp(fromattr->name, req->values[i].string.text) == 0)
2635 break;
2636
2637 if (i == req->num_values)
2638 continue;
2639 }
2640
2641 copy_attribute(to, fromattr, quickcopy);
2642 }
2643 }
2644
2645
2646 /*
2647 * 'copy_banner()' - Copy a banner file to the requests directory for the
2648 * specified job.
2649 */
2650
2651 static int /* O - Size of banner file in kbytes */
2652 copy_banner(client_t *con, /* I - Client connection */
2653 job_t *job, /* I - Job information */
2654 const char *name) /* I - Name of banner */
2655 {
2656 int i; /* Looping var */
2657 int kbytes; /* Size of banner file in kbytes */
2658 char filename[1024]; /* Job filename */
2659 banner_t *banner; /* Pointer to banner */
2660 cups_file_t *in; /* Input file */
2661 cups_file_t *out; /* Output file */
2662 int ch; /* Character from file */
2663 char attrname[255], /* Name of attribute */
2664 *s; /* Pointer into name */
2665 ipp_attribute_t *attr; /* Attribute */
2666
2667
2668 LogMessage(L_DEBUG2, "copy_banner(%p[%d], %p[%d], %s)",
2669 con, con->http.fd, job, job->id, name ? name : "(null)");
2670
2671 /*
2672 * Find the banner; return if not found or "none"...
2673 */
2674
2675 if (name == NULL ||
2676 strcmp(name, "none") == 0 ||
2677 (banner = FindBanner(name)) == NULL)
2678 return (0);
2679
2680 /*
2681 * Open the banner and job files...
2682 */
2683
2684 if (add_file(con, job, banner->filetype, 0))
2685 return (0);
2686
2687 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
2688 job->num_files);
2689 if ((out = cupsFileOpen(filename, "w")) == NULL)
2690 {
2691 LogMessage(L_ERROR, "copy_banner: Unable to create banner job file %s - %s",
2692 filename, strerror(errno));
2693 job->num_files --;
2694 return (0);
2695 }
2696
2697 fchmod(cupsFileNumber(out), 0640);
2698 fchown(cupsFileNumber(out), RunUser, Group);
2699
2700 if (con->language)
2701 {
2702 /*
2703 * Try the localized banner file under the subdirectory...
2704 */
2705
2706 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
2707 con->language->language, name);
2708
2709 if (access(filename, 0) && con->language->language[2])
2710 {
2711 /*
2712 * Wasn't able to find "ll_CC" locale file; try the non-national
2713 * localization banner directory.
2714 */
2715
2716 attrname[0] = con->language->language[0];
2717 attrname[1] = con->language->language[1];
2718 attrname[2] = '\0';
2719
2720 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
2721 attrname, name);
2722 }
2723
2724 if (access(filename, 0))
2725 {
2726 /*
2727 * Use the non-localized banner file.
2728 */
2729
2730 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
2731 }
2732 }
2733 else
2734 {
2735 /*
2736 * Use the non-localized banner file.
2737 */
2738
2739 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
2740 }
2741
2742 if ((in = cupsFileOpen(filename, "r")) == NULL)
2743 {
2744 cupsFileClose(out);
2745 unlink(filename);
2746 LogMessage(L_ERROR, "copy_banner: Unable to open banner template file %s - %s",
2747 filename, strerror(errno));
2748 job->num_files --;
2749 return (0);
2750 }
2751
2752 /*
2753 * Parse the file to the end...
2754 */
2755
2756 while ((ch = cupsFileGetChar(in)) != EOF)
2757 if (ch == '{')
2758 {
2759 /*
2760 * Get an attribute name...
2761 */
2762
2763 for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
2764 if (!isalpha(ch & 255) && ch != '-' && ch != '?')
2765 break;
2766 else if (s < (attrname + sizeof(attrname) - 1))
2767 *s++ = ch;
2768 else
2769 break;
2770
2771 *s = '\0';
2772
2773 if (ch != '}')
2774 {
2775 /*
2776 * Ignore { followed by stuff that is not an attribute name...
2777 */
2778
2779 cupsFilePrintf(out, "{%s%c", attrname, ch);
2780 continue;
2781 }
2782
2783 /*
2784 * See if it is defined...
2785 */
2786
2787 if (attrname[0] == '?')
2788 s = attrname + 1;
2789 else
2790 s = attrname;
2791
2792 if (strcmp(s, "printer-name") == 0)
2793 {
2794 cupsFilePuts(out, job->dest);
2795 continue;
2796 }
2797 else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
2798 {
2799 /*
2800 * See if we have a leading question mark...
2801 */
2802
2803 if (attrname[0] != '?')
2804 {
2805 /*
2806 * Nope, write to file as-is; probably a PostScript procedure...
2807 */
2808
2809 cupsFilePrintf(out, "{%s}", attrname);
2810 }
2811
2812 continue;
2813 }
2814
2815 /*
2816 * Output value(s)...
2817 */
2818
2819 for (i = 0; i < attr->num_values; i ++)
2820 {
2821 if (i)
2822 cupsFilePutChar(out, ',');
2823
2824 switch (attr->value_tag)
2825 {
2826 case IPP_TAG_INTEGER :
2827 case IPP_TAG_ENUM :
2828 if (strncmp(s, "time-at-", 8) == 0)
2829 cupsFilePuts(out, GetDateTime(attr->values[i].integer));
2830 else
2831 cupsFilePrintf(out, "%d", attr->values[i].integer);
2832 break;
2833
2834 case IPP_TAG_BOOLEAN :
2835 cupsFilePrintf(out, "%d", attr->values[i].boolean);
2836 break;
2837
2838 case IPP_TAG_NOVALUE :
2839 cupsFilePuts(out, "novalue");
2840 break;
2841
2842 case IPP_TAG_RANGE :
2843 cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
2844 attr->values[i].range.upper);
2845 break;
2846
2847 case IPP_TAG_RESOLUTION :
2848 cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
2849 attr->values[i].resolution.yres,
2850 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
2851 "dpi" : "dpc");
2852 break;
2853
2854 case IPP_TAG_URI :
2855 case IPP_TAG_STRING :
2856 case IPP_TAG_TEXT :
2857 case IPP_TAG_NAME :
2858 case IPP_TAG_KEYWORD :
2859 case IPP_TAG_CHARSET :
2860 case IPP_TAG_LANGUAGE :
2861 if (strcasecmp(banner->filetype->type, "postscript") == 0)
2862 {
2863 /*
2864 * Need to quote strings for PS banners...
2865 */
2866
2867 const char *p;
2868
2869 for (p = attr->values[i].string.text; *p; p ++)
2870 {
2871 if (*p == '(' || *p == ')' || *p == '\\')
2872 {
2873 cupsFilePutChar(out, '\\');
2874 cupsFilePutChar(out, *p);
2875 }
2876 else if (*p < 32 || *p > 126)
2877 cupsFilePrintf(out, "\\%03o", *p & 255);
2878 else
2879 cupsFilePutChar(out, *p);
2880 }
2881 }
2882 else
2883 cupsFilePuts(out, attr->values[i].string.text);
2884 break;
2885
2886 default :
2887 break; /* anti-compiler-warning-code */
2888 }
2889 }
2890 }
2891 else if (ch == '\\') /* Quoted char */
2892 {
2893 ch = cupsFileGetChar(in);
2894
2895 if (ch != '{') /* Only do special handling for \{ */
2896 cupsFilePutChar(out, '\\');
2897
2898 cupsFilePutChar(out, ch);
2899 }
2900 else
2901 cupsFilePutChar(out, ch);
2902
2903 cupsFileClose(in);
2904
2905 kbytes = (cupsFileTell(out) + 1023) / 1024;
2906
2907 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
2908 attr->values[0].integer += kbytes;
2909
2910 cupsFileClose(out);
2911
2912 return (kbytes);
2913 }
2914
2915
2916 /*
2917 * 'copy_file()' - Copy a PPD file or interface script...
2918 */
2919
2920 static int /* O - 0 = success, -1 = error */
2921 copy_file(const char *from, /* I - Source file */
2922 const char *to) /* I - Destination file */
2923 {
2924 cups_file_t *src, /* Source file */
2925 *dst; /* Destination file */
2926 int bytes; /* Bytes to read/write */
2927 char buffer[2048]; /* Copy buffer */
2928
2929
2930 LogMessage(L_DEBUG2, "copy_file(\"%s\", \"%s\")\n", from, to);
2931
2932 /*
2933 * Open the source and destination file for a copy...
2934 */
2935
2936 if ((src = cupsFileOpen(from, "rb")) == NULL)
2937 return (-1);
2938
2939 if ((dst = cupsFileOpen(to, "wb")) == NULL)
2940 {
2941 cupsFileClose(src);
2942 return (-1);
2943 }
2944
2945 /*
2946 * Copy the source file to the destination...
2947 */
2948
2949 while ((bytes = cupsFileRead(src, buffer, sizeof(buffer))) > 0)
2950 if (cupsFileWrite(dst, buffer, bytes) < bytes)
2951 {
2952 cupsFileClose(src);
2953 cupsFileClose(dst);
2954 return (-1);
2955 }
2956
2957 /*
2958 * Close both files and return...
2959 */
2960
2961 cupsFileClose(src);
2962
2963 return (cupsFileClose(dst));
2964 }
2965
2966
2967 /*
2968 * 'copy_model()' - Copy a PPD model file, substituting default values
2969 * as needed...
2970 */
2971
2972 static int /* O - 0 = success, -1 = error */
2973 copy_model(const char *from, /* I - Source file */
2974 const char *to) /* I - Destination file */
2975 {
2976 cups_file_t *src, /* Source file */
2977 *dst; /* Destination file */
2978 char buffer[2048]; /* Copy buffer */
2979 int i; /* Looping var */
2980 char option[PPD_MAX_NAME], /* Option name */
2981 choice[PPD_MAX_NAME]; /* Choice name */
2982 int num_defaults; /* Number of default options */
2983 ppd_default_t *defaults; /* Default options */
2984 char cups_protocol[PPD_MAX_LINE];
2985 /* cupsProtocol attribute */
2986 #ifdef HAVE_LIBPAPER
2987 char *paper_result; /* Paper size name from libpaper */
2988 char system_paper[64]; /* Paper size name buffer */
2989 #endif /* HAVE_LIBPAPER */
2990
2991
2992 LogMessage(L_DEBUG2, "copy_model(\"%s\", \"%s\")\n", from, to);
2993
2994 /*
2995 * Open the destination (if possible) and set the default options...
2996 */
2997
2998 num_defaults = 0;
2999 defaults = NULL;
3000 cups_protocol[0] = '\0';
3001
3002 if ((dst = cupsFileOpen(to, "rb")) != NULL)
3003 {
3004 /*
3005 * Read all of the default lines from the old PPD...
3006 */
3007
3008 while (cupsFileGets(dst, buffer, sizeof(buffer)) != NULL)
3009 if (!strncmp(buffer, "*Default", 8))
3010 {
3011 /*
3012 * Add the default option...
3013 */
3014
3015 if (!ppd_parse_line(buffer, option, sizeof(option),
3016 choice, sizeof(choice)))
3017 num_defaults = ppd_add_default(option, choice, num_defaults,
3018 &defaults);
3019 }
3020 else if (!strncmp(buffer, "*cupsProtocol:", 14))
3021 strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
3022
3023 cupsFileClose(dst);
3024 }
3025 #ifdef HAVE_LIBPAPER
3026 else if ((paper_result = systempapername()) != NULL)
3027 {
3028 /*
3029 * Set the default media sizes from the systemwide default...
3030 */
3031
3032 strlcpy(system_paper, paper_result, sizeof(system_paper));
3033 system_paper[0] = toupper(system_paper[0] & 255);
3034
3035 num_defaults = ppd_add_default("PageSize", system_paper,
3036 num_defaults, &defaults);
3037 num_defaults = ppd_add_default("PageRegion", system_paper,
3038 num_defaults, &defaults);
3039 num_defaults = ppd_add_default("PaperDimension", system_paper,
3040 num_defaults, &defaults);
3041 num_defaults = ppd_add_default("ImageableArea", system_paper,
3042 num_defaults, &defaults);
3043 }
3044 #endif /* HAVE_LIBPAPER */
3045 else
3046 {
3047 /*
3048 * Add the default media sizes...
3049 *
3050 * Note: These values are generally not valid for large-format devices
3051 * like plotters, however it is probably safe to say that those
3052 * users will configure the media size after initially adding
3053 * the device anyways...
3054 */
3055
3056 if (!DefaultLanguage ||
3057 !strcasecmp(DefaultLanguage, "C") ||
3058 !strcasecmp(DefaultLanguage, "POSIX") ||
3059 !strcasecmp(DefaultLanguage, "en") ||
3060 !strncasecmp(DefaultLanguage, "en_US", 5) ||
3061 !strncasecmp(DefaultLanguage, "en_CA", 5) ||
3062 !strncasecmp(DefaultLanguage, "fr_CA", 5))
3063 {
3064 /*
3065 * These are the only locales that will default to "letter" size...
3066 */
3067
3068 num_defaults = ppd_add_default("PageSize", "Letter", num_defaults,
3069 &defaults);
3070 num_defaults = ppd_add_default("PageRegion", "Letter", num_defaults,
3071 &defaults);
3072 num_defaults = ppd_add_default("PaperDimension", "Letter", num_defaults,
3073 &defaults);
3074 num_defaults = ppd_add_default("ImageableArea", "Letter", num_defaults,
3075 &defaults);
3076 }
3077 else
3078 {
3079 /*
3080 * The rest default to "a4" size...
3081 */
3082
3083 num_defaults = ppd_add_default("PageSize", "A4", num_defaults,
3084 &defaults);
3085 num_defaults = ppd_add_default("PageRegion", "A4", num_defaults,
3086 &defaults);
3087 num_defaults = ppd_add_default("PaperDimension", "A4", num_defaults,
3088 &defaults);
3089 num_defaults = ppd_add_default("ImageableArea", "A4", num_defaults,
3090 &defaults);
3091 }
3092 }
3093
3094 /*
3095 * Open the source and destination file for a copy...
3096 */
3097
3098 if ((src = cupsFileOpen(from, "rb")) == NULL)
3099 {
3100 if (num_defaults > 0)
3101 free(defaults);
3102
3103 return (-1);
3104 }
3105
3106 if ((dst = cupsFileOpen(to, "wb")) == NULL)
3107 {
3108 if (num_defaults > 0)
3109 free(defaults);
3110
3111 cupsFileClose(src);
3112 return (-1);
3113 }
3114
3115 /*
3116 * Copy the source file to the destination...
3117 */
3118
3119 while (cupsFileGets(src, buffer, sizeof(buffer)) != NULL)
3120 {
3121 if (!strncmp(buffer, "*Default", 8))
3122 {
3123 /*
3124 * Check for an previous default option choice...
3125 */
3126
3127 if (!ppd_parse_line(buffer, option, sizeof(option),
3128 choice, sizeof(choice)))
3129 {
3130 for (i = 0; i < num_defaults; i ++)
3131 if (!strcmp(option, defaults[i].option))
3132 {
3133 /*
3134 * Substitute the previous choice...
3135 */
3136
3137 snprintf(buffer, sizeof(buffer), "*Default%s: %s", option,
3138 defaults[i].choice);
3139 break;
3140 }
3141 }
3142 }
3143
3144 cupsFilePrintf(dst, "%s\n", buffer);
3145 }
3146
3147 if (cups_protocol[0])
3148 cupsFilePrintf(dst, "%s\n", cups_protocol);
3149
3150 if (num_defaults > 0)
3151 free(defaults);
3152
3153 /*
3154 * Close both files and return...
3155 */
3156
3157 cupsFileClose(src);
3158
3159 return (cupsFileClose(dst));
3160 }
3161
3162
3163 /*
3164 * 'create_job()' - Print a file to a printer or class.
3165 */
3166
3167 static void
3168 create_job(client_t *con, /* I - Client connection */
3169 ipp_attribute_t *uri) /* I - Printer URI */
3170 {
3171 ipp_attribute_t *attr; /* Current attribute */
3172 const char *dest; /* Destination */
3173 cups_ptype_t dtype; /* Destination type (printer or class) */
3174 int priority; /* Job priority */
3175 char *title; /* Job name/title */
3176 job_t *job; /* Current job */
3177 char job_uri[HTTP_MAX_URI], /* Job URI */
3178 method[HTTP_MAX_URI], /* Method portion of URI */
3179 username[HTTP_MAX_URI], /* Username portion of URI */
3180 host[HTTP_MAX_URI], /* Host portion of URI */
3181 resource[HTTP_MAX_URI]; /* Resource portion of URI */
3182 int port; /* Port portion of URI */
3183 printer_t *printer; /* Printer data */
3184 int kbytes; /* Size of print file */
3185 int i; /* Looping var */
3186 int lowerpagerange; /* Page range bound */
3187
3188
3189 LogMessage(L_DEBUG2, "create_job(%p[%d], %s)\n", con, con->http.fd,
3190 uri->values[0].string.text);
3191
3192 /*
3193 * Verify that the POST operation was done to a valid URI.
3194 */
3195
3196 if (strncmp(con->uri, "/classes/", 9) != 0 &&
3197 strncmp(con->uri, "/printers/", 10) != 0)
3198 {
3199 LogMessage(L_ERROR, "create_job: cancel request on bad resource \'%s\'!",
3200 con->uri);
3201 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3202 return;
3203 }
3204
3205 /*
3206 * Is the destination valid?
3207 */
3208
3209 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3210
3211 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
3212 {
3213 /*
3214 * Bad URI...
3215 */
3216
3217 LogMessage(L_ERROR, "create_job: resource name \'%s\' no good!", resource);
3218 send_ipp_error(con, IPP_NOT_FOUND);
3219 return;
3220 }
3221
3222 /*
3223 * Check policy...
3224 */
3225
3226 if (!CheckPolicy(printer->op_policy_ptr, con, NULL))
3227 {
3228 LogMessage(L_ERROR, "create_job: not authorized!");
3229 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3230 return;
3231 }
3232
3233 /*
3234 * See if the printer is accepting jobs...
3235 */
3236
3237 if (!printer->accepting)
3238 {
3239 LogMessage(L_INFO, "create_job: destination \'%s\' is not accepting jobs.",
3240 dest);
3241 send_ipp_error(con, IPP_NOT_ACCEPTING);
3242 return;
3243 }
3244
3245 /*
3246 * Validate job template attributes; for now just copies and page-ranges...
3247 */
3248
3249 if ((attr = ippFindAttribute(con->request, "copies", IPP_TAG_INTEGER)) != NULL)
3250 {
3251 if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
3252 {
3253 LogMessage(L_INFO, "create_job: bad copies value %d.",
3254 attr->values[0].integer);
3255 send_ipp_error(con, IPP_BAD_REQUEST);
3256 return;
3257 }
3258 }
3259
3260 if ((attr = ippFindAttribute(con->request, "page-ranges", IPP_TAG_RANGE)) != NULL)
3261 {
3262 for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
3263 {
3264 if (attr->values[i].range.lower < lowerpagerange ||
3265 attr->values[i].range.lower > attr->values[i].range.upper)
3266 {
3267 LogMessage(L_ERROR, "create_job: bad page-ranges values %d-%d.",
3268 attr->values[i].range.lower, attr->values[i].range.upper);
3269 send_ipp_error(con, IPP_BAD_REQUEST);
3270 return;
3271 }
3272
3273 lowerpagerange = attr->values[i].range.upper + 1;
3274 }
3275 }
3276
3277 /*
3278 * Make sure we aren't over our limit...
3279 */
3280
3281 if (NumJobs >= MaxJobs && MaxJobs)
3282 CleanJobs();
3283
3284 if (NumJobs >= MaxJobs && MaxJobs)
3285 {
3286 LogMessage(L_INFO, "create_job: too many jobs.");
3287 send_ipp_error(con, IPP_NOT_POSSIBLE);
3288 return;
3289 }
3290
3291 if (!check_quotas(con, printer))
3292 {
3293 send_ipp_error(con, IPP_NOT_POSSIBLE);
3294 return;
3295 }
3296
3297 /*
3298 * Create the job and set things up...
3299 */
3300
3301 if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
3302 priority = attr->values[0].integer;
3303 else
3304 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
3305 priority = 50);
3306
3307 if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
3308 title = attr->values[0].string.text;
3309 else
3310 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
3311 title = "Untitled");
3312
3313 if ((job = AddJob(priority, printer->name)) == NULL)
3314 {
3315 LogMessage(L_ERROR, "create_job: unable to add job for destination \'%s\'!",
3316 dest);
3317 send_ipp_error(con, IPP_INTERNAL_ERROR);
3318 return;
3319 }
3320
3321 job->dtype = dtype;
3322 job->attrs = con->request;
3323 con->request = NULL;
3324
3325 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
3326
3327 if (con->username[0])
3328 SetString(&job->username, con->username);
3329 else if (attr != NULL)
3330 {
3331 LogMessage(L_DEBUG, "create_job: requesting-user-name = \'%s\'",
3332 attr->values[0].string.text);
3333
3334 SetString(&job->username, attr->values[0].string.text);
3335 }
3336 else
3337 SetString(&job->username, "anonymous");
3338
3339 if (attr == NULL)
3340 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
3341 NULL, job->username);
3342 else
3343 {
3344 attr->group_tag = IPP_TAG_JOB;
3345 SetString(&attr->name, "job-originating-user-name");
3346 }
3347
3348 if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
3349 IPP_TAG_ZERO)) != NULL)
3350 {
3351 /*
3352 * Request contains a job-originating-host-name attribute; validate it...
3353 */
3354
3355 if (attr->value_tag != IPP_TAG_NAME ||
3356 attr->num_values != 1 ||
3357 strcmp(con->http.hostname, "localhost") != 0)
3358 {
3359 /*
3360 * Can't override the value if we aren't connected via localhost.
3361 * Also, we can only have 1 value and it must be a name value.
3362 */
3363
3364 switch (attr->value_tag)
3365 {
3366 case IPP_TAG_STRING :
3367 case IPP_TAG_TEXTLANG :
3368 case IPP_TAG_NAMELANG :
3369 case IPP_TAG_TEXT :
3370 case IPP_TAG_NAME :
3371 case IPP_TAG_KEYWORD :
3372 case IPP_TAG_URI :
3373 case IPP_TAG_URISCHEME :
3374 case IPP_TAG_CHARSET :
3375 case IPP_TAG_LANGUAGE :
3376 case IPP_TAG_MIMETYPE :
3377 /*
3378 * Free old strings...
3379 */
3380
3381 for (i = 0; i < attr->num_values; i ++)
3382 {
3383 free(attr->values[i].string.text);
3384 attr->values[i].string.text = NULL;
3385 if (attr->values[i].string.charset)
3386 {
3387 free(attr->values[i].string.charset);
3388 attr->values[i].string.charset = NULL;
3389 }
3390 }
3391
3392 default :
3393 break;
3394 }
3395
3396 /*
3397 * Use the default connection hostname instead...
3398 */
3399
3400 attr->value_tag = IPP_TAG_NAME;
3401 attr->num_values = 1;
3402 attr->values[0].string.text = strdup(con->http.hostname);
3403 }
3404
3405 attr->group_tag = IPP_TAG_JOB;
3406 }
3407 else
3408 {
3409 /*
3410 * No job-originating-host-name attribute, so use the hostname from
3411 * the connection...
3412 */
3413
3414 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
3415 "job-originating-host-name", NULL, con->http.hostname);
3416 }
3417
3418 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
3419 time(NULL));
3420 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3421 "time-at-processing", 0);
3422 attr->value_tag = IPP_TAG_NOVALUE;
3423 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3424 "time-at-completed", 0);
3425 attr->value_tag = IPP_TAG_NOVALUE;
3426
3427 /*
3428 * Add remaining job attributes...
3429 */
3430
3431 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
3432 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
3433 "job-state", IPP_JOB_STOPPED);
3434 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3435 "job-media-sheets-completed", 0);
3436 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
3437 printer->uri);
3438 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
3439 title);
3440
3441 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
3442 attr->values[0].integer = 0;
3443 else
3444 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3445 "job-k-octets", 0);
3446
3447 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
3448 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
3449 if (attr == NULL)
3450 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
3451 "job-hold-until", NULL, "no-hold");
3452 if (attr != NULL && strcmp(attr->values[0].string.text, "no-hold") != 0 &&
3453 !(printer->type & CUPS_PRINTER_REMOTE))
3454 {
3455 /*
3456 * Hold job until specified time...
3457 */
3458
3459 SetJobHoldUntil(job->id, attr->values[0].string.text);
3460 }
3461 else
3462 job->hold_until = time(NULL) + 60;
3463
3464 job->state->values[0].integer = IPP_JOB_HELD;
3465
3466 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
3467 Classification)
3468 {
3469 /*
3470 * Add job sheets options...
3471 */
3472
3473 if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) == NULL)
3474 {
3475 LogMessage(L_DEBUG, "Adding default job-sheets values \"%s,%s\"...",
3476 printer->job_sheets[0], printer->job_sheets[1]);
3477
3478 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
3479 2, NULL, NULL);
3480 attr->values[0].string.text = strdup(printer->job_sheets[0]);
3481 attr->values[1].string.text = strdup(printer->job_sheets[1]);
3482 }
3483
3484 job->job_sheets = attr;
3485
3486 /*
3487 * Enforce classification level if set...
3488 */
3489
3490 if (Classification)
3491 {
3492 LogMessage(L_INFO, "Classification=\"%s\", ClassifyOverride=%d",
3493 Classification ? Classification : "(null)", ClassifyOverride);
3494
3495 if (ClassifyOverride)
3496 {
3497 if (strcmp(attr->values[0].string.text, "none") == 0 &&
3498 (attr->num_values == 1 ||
3499 strcmp(attr->values[1].string.text, "none") == 0))
3500 {
3501 /*
3502 * Force the leading banner to have the classification on it...
3503 */
3504
3505 SetString(&attr->values[0].string.text, Classification);
3506
3507 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
3508 "job-sheets=\"%s,none\", "
3509 "job-originating-user-name=\"%s\"",
3510 job->id, Classification,
3511 job->username);
3512 }
3513 else if (attr->num_values == 2 &&
3514 strcmp(attr->values[0].string.text, attr->values[1].string.text) != 0 &&
3515 strcmp(attr->values[0].string.text, "none") != 0 &&
3516 strcmp(attr->values[1].string.text, "none") != 0)
3517 {
3518 /*
3519 * Can't put two different security markings on the same document!
3520 */
3521
3522 SetString(&attr->values[1].string.text, attr->values[0].string.text);
3523
3524 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
3525 "job-sheets=\"%s,%s\", "
3526 "job-originating-user-name=\"%s\"",
3527 job->id, attr->values[0].string.text,
3528 attr->values[1].string.text,
3529 job->username);
3530 }
3531 else if (strcmp(attr->values[0].string.text, Classification) &&
3532 strcmp(attr->values[0].string.text, "none") &&
3533 (attr->num_values == 1 ||
3534 (strcmp(attr->values[1].string.text, Classification) &&
3535 strcmp(attr->values[1].string.text, "none"))))
3536 {
3537 if (attr->num_values == 1)
3538 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION OVERRIDDEN "
3539 "job-sheets=\"%s\", "
3540 "job-originating-user-name=\"%s\"",
3541 job->id, attr->values[0].string.text,
3542 job->username);
3543 else
3544 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION OVERRIDDEN "
3545 "job-sheets=\"%s,%s\",fffff "
3546 "job-originating-user-name=\"%s\"",
3547 job->id, attr->values[0].string.text,
3548 attr->values[1].string.text,
3549 job->username);
3550 }
3551 }
3552 else if (strcmp(attr->values[0].string.text, Classification) != 0 &&
3553 (attr->num_values == 1 ||
3554 strcmp(attr->values[1].string.text, Classification) != 0))
3555 {
3556 /*
3557 * Force the banner to have the classification on it...
3558 */
3559
3560 if (attr->num_values > 1 &&
3561 !strcmp(attr->values[0].string.text, attr->values[1].string.text))
3562 {
3563 SetString(&(attr->values[0].string.text), Classification);
3564 SetString(&(attr->values[1].string.text), Classification);
3565 }
3566 else
3567 {
3568 if (attr->num_values == 1 ||
3569 strcmp(attr->values[0].string.text, "none"))
3570 SetString(&(attr->values[0].string.text), Classification);
3571
3572 if (attr->num_values > 1 &&
3573 strcmp(attr->values[1].string.text, "none"))
3574 SetString(&(attr->values[1].string.text), Classification);
3575 }
3576
3577 if (attr->num_values > 1)
3578 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
3579 "job-sheets=\"%s,%s\", "
3580 "job-originating-user-name=\"%s\"",
3581 job->id, attr->values[0].string.text,
3582 attr->values[1].string.text,
3583 job->username);
3584 else
3585 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
3586 "job-sheets=\"%s\", "
3587 "job-originating-user-name=\"%s\"",
3588 job->id, Classification,
3589 job->username);
3590 }
3591 }
3592
3593 /*
3594 * See if we need to add the starting sheet...
3595 */
3596
3597 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
3598 {
3599 LogMessage(L_INFO, "Adding start banner page \"%s\" to job %d.",
3600 attr->values[0].string.text, job->id);
3601
3602 kbytes = copy_banner(con, job, attr->values[0].string.text);
3603
3604 UpdateQuota(printer, job->username, 0, kbytes);
3605 }
3606 }
3607 else if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
3608 job->sheets = attr;
3609
3610 /*
3611 * Add any job subscriptions...
3612 */
3613
3614 add_job_subscriptions(con, job);
3615
3616 /*
3617 * Set all but the first two attributes to the job attributes group...
3618 */
3619
3620 for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
3621 attr->group_tag = IPP_TAG_JOB;
3622
3623 /*
3624 * Save and log the job...
3625 */
3626
3627 SaveJob(job->id);
3628
3629 LogMessage(L_INFO, "Job %d created on \'%s\' by \'%s\'.", job->id,
3630 job->dest, job->username);
3631
3632 cupsdAddEvent(CUPSD_EVENT_JOB_CREATED, printer, job, "Job created.");
3633
3634 /*
3635 * Fill in the response info...
3636 */
3637
3638 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
3639 LocalPort, job->id);
3640
3641 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
3642
3643 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
3644
3645 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
3646 job->state->values[0].integer);
3647
3648 con->response->request.status.status_code = IPP_OK;
3649 }
3650
3651
3652 /*
3653 * 'create_subscription()' - Create a notification subscription.
3654 */
3655
3656 static void
3657 create_subscription(
3658 client_t *con, /* I - Client connection */
3659 ipp_attribute_t *uri) /* I - Printer URI */
3660 {
3661 }
3662
3663
3664 /*
3665 * 'delete_printer()' - Remove a printer or class from the system.
3666 */
3667
3668 static void
3669 delete_printer(client_t *con, /* I - Client connection */
3670 ipp_attribute_t *uri) /* I - URI of printer or class */
3671 {
3672 const char *dest; /* Destination */
3673 cups_ptype_t dtype; /* Destination type (printer or class) */
3674 char method[HTTP_MAX_URI], /* Method portion of URI */
3675 username[HTTP_MAX_URI], /* Username portion of URI */
3676 host[HTTP_MAX_URI], /* Host portion of URI */
3677 resource[HTTP_MAX_URI]; /* Resource portion of URI */
3678 int port; /* Port portion of URI */
3679 printer_t *printer; /* Printer/class */
3680 char filename[1024]; /* Script/PPD filename */
3681
3682
3683 LogMessage(L_DEBUG2, "delete_printer(%p[%d], %s)\n", con, con->http.fd,
3684 uri->values[0].string.text);
3685
3686 /*
3687 * Was this operation called from the correct URI?
3688 */
3689
3690 if (strncmp(con->uri, "/admin/", 7) != 0)
3691 {
3692 LogMessage(L_ERROR, "delete_printer: admin request on bad resource \'%s\'!",
3693 con->uri);
3694 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3695 return;
3696 }
3697
3698 /*
3699 * Do we have a valid URI?
3700 */
3701
3702 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3703
3704 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
3705 {
3706 /*
3707 * Bad URI...
3708 */
3709
3710 LogMessage(L_ERROR, "delete_printer: resource name \'%s\' no good!", resource);
3711 send_ipp_error(con, IPP_NOT_FOUND);
3712 return;
3713 }
3714
3715 /*
3716 * Check policy...
3717 */
3718
3719 if (!CheckPolicy(DefaultPolicyPtr, con, NULL))
3720 {
3721 LogMessage(L_ERROR, "delete_printer: not authorized!");
3722 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3723 return;
3724 }
3725
3726 /*
3727 * Remove old jobs...
3728 */
3729
3730 CancelJobs(dest, NULL, 1);
3731
3732 /*
3733 * Remove old subscriptions and send a "deleted printer" event...
3734 */
3735
3736 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
3737 "%s \'%s\' deleted by \'%s\'.",
3738 (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
3739 dest, con->username);
3740
3741 cupsdExpireSubscriptions(printer, NULL);
3742
3743 /*
3744 * Remove any old PPD or script files...
3745 */
3746
3747 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot, dest);
3748 unlink(filename);
3749
3750 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest);
3751 unlink(filename);
3752
3753 if (dtype & CUPS_PRINTER_CLASS)
3754 {
3755 LogMessage(L_INFO, "Class \'%s\' deleted by \'%s\'.", dest,
3756 con->username);
3757
3758 DeletePrinter(printer, 0);
3759 SaveAllClasses();
3760 }
3761 else
3762 {
3763 LogMessage(L_INFO, "Printer \'%s\' deleted by \'%s\'.", dest,
3764 con->username);
3765
3766 DeletePrinter(printer, 0);
3767 SaveAllPrinters();
3768 }
3769
3770 /*
3771 * Return with no errors...
3772 */
3773
3774 con->response->request.status.status_code = IPP_OK;
3775 }
3776
3777
3778 /*
3779 * 'get_default()' - Get the default destination.
3780 */
3781
3782 static void
3783 get_default(client_t *con) /* I - Client connection */
3784 {
3785 int i; /* Looping var */
3786 ipp_attribute_t *requested, /* requested-attributes */
3787 *history; /* History collection */
3788 int need_history; /* Need to send history collection? */
3789
3790
3791 LogMessage(L_DEBUG2, "get_default(%p[%d])\n", con, con->http.fd);
3792
3793 /*
3794 * Check policy...
3795 */
3796
3797 if (!CheckPolicy(DefaultPolicyPtr, con, NULL))
3798 {
3799 LogMessage(L_ERROR, "get_default: not authorized!");
3800 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3801 return;
3802 }
3803
3804 if (DefaultPrinter != NULL)
3805 {
3806 requested = ippFindAttribute(con->request, "requested-attributes",
3807 IPP_TAG_KEYWORD);
3808
3809 copy_attrs(con->response, DefaultPrinter->attrs, requested, IPP_TAG_ZERO, 0);
3810 copy_attrs(con->response, CommonData, requested, IPP_TAG_ZERO, IPP_TAG_COPY);
3811
3812 need_history = 0;
3813
3814 if (MaxPrinterHistory > 0 && DefaultPrinter->num_history > 0 && requested)
3815 {
3816 for (i = 0; i < requested->num_values; i ++)
3817 if (!strcmp(requested->values[i].string.text, "all") ||
3818 !strcmp(requested->values[i].string.text, "printer-state-history"))
3819 {
3820 need_history = 1;
3821 break;
3822 }
3823 }
3824
3825 if (need_history)
3826 {
3827 history = ippAddCollections(con->response, IPP_TAG_PRINTER,
3828 "printer-state-history",
3829 DefaultPrinter->num_history, NULL);
3830
3831 for (i = 0; i < DefaultPrinter->num_history; i ++)
3832 copy_attrs(history->values[i].collection = ippNew(),
3833 DefaultPrinter->history[i],
3834 NULL, IPP_TAG_ZERO, 0);
3835 }
3836
3837 con->response->request.status.status_code = requested ? IPP_OK_SUBST : IPP_OK;
3838 }
3839 else
3840 con->response->request.status.status_code = IPP_NOT_FOUND;
3841 }
3842
3843
3844 /*
3845 * 'get_devices()' - Get the list of available devices on the local system.
3846 */
3847
3848 static void
3849 get_devices(client_t *con) /* I - Client connection */
3850 {
3851 ipp_attribute_t *limit; /* Limit attribute */
3852 char command[1024], /* cups-deviced command */
3853 options[1024]; /* Options to pass to command */
3854
3855
3856 LogMessage(L_DEBUG2, "get_devices(%p[%d])\n", con, con->http.fd);
3857
3858 /*
3859 * Check policy...
3860 */
3861
3862 if (!CheckPolicy(DefaultPolicyPtr, con, NULL))
3863 {
3864 LogMessage(L_ERROR, "get_devices: not authorized!");
3865 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3866 return;
3867 }
3868
3869 /*
3870 * Run cups-deviced command with the given options...
3871 */
3872
3873 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
3874
3875 snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
3876 snprintf(options, sizeof(options), "cups-deviced %d+%d",
3877 con->request->request.op.request_id,
3878 limit ? limit->values[0].integer : 0);
3879
3880 if (SendCommand(con, command, options))
3881 {
3882 /*
3883 * Command started successfully, don't send an IPP response here...
3884 */
3885
3886 ippDelete(con->response);
3887 con->response = NULL;
3888 }
3889 else
3890 {
3891 /*
3892 * Command failed, return "internal error" so the user knows something
3893 * went wrong...
3894 */
3895
3896 send_ipp_error(con, IPP_INTERNAL_ERROR);
3897 }
3898 }
3899
3900
3901 /*
3902 * 'get_jobs()' - Get a list of jobs for the specified printer.
3903 */
3904
3905 static void
3906 get_jobs(client_t *con, /* I - Client connection */
3907 ipp_attribute_t *uri) /* I - Printer URI */
3908 {
3909 ipp_attribute_t *attr, /* Current attribute */
3910 *requested; /* Requested attributes */
3911 const char *dest; /* Destination */
3912 cups_ptype_t dtype; /* Destination type (printer or class) */
3913 cups_ptype_t dmask; /* Destination type mask */
3914 char method[HTTP_MAX_URI], /* Method portion of URI */
3915 username[HTTP_MAX_URI], /* Username portion of URI */
3916 host[HTTP_MAX_URI], /* Host portion of URI */
3917 resource[HTTP_MAX_URI]; /* Resource portion of URI */
3918 int port; /* Port portion of URI */
3919 int completed; /* Completed jobs? */
3920 int limit; /* Maximum number of jobs to return */
3921 int count; /* Number of jobs that match */
3922 job_t *job; /* Current job pointer */
3923 char job_uri[HTTP_MAX_URI]; /* Job URI... */
3924 printer_t *printer; /* Printer */
3925
3926
3927 LogMessage(L_DEBUG2, "get_jobs(%p[%d], %s)\n", con, con->http.fd,
3928 uri->values[0].string.text);
3929
3930 /*
3931 * Is the destination valid?
3932 */
3933
3934 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3935
3936 if (strcmp(resource, "/") == 0 ||
3937 (strncmp(resource, "/jobs", 5) == 0 && strlen(resource) <= 6))
3938 {
3939 dest = NULL;
3940 dtype = (cups_ptype_t)0;
3941 dmask = (cups_ptype_t)0;
3942 printer = NULL;
3943 }
3944 else if (strncmp(resource, "/printers", 9) == 0 && strlen(resource) <= 10)
3945 {
3946 dest = NULL;
3947 dtype = (cups_ptype_t)0;
3948 dmask = CUPS_PRINTER_CLASS;
3949 printer = NULL;
3950 }
3951 else if (strncmp(resource, "/classes", 8) == 0 && strlen(resource) <= 9)
3952 {
3953 dest = NULL;
3954 dtype = CUPS_PRINTER_CLASS;
3955 dmask = CUPS_PRINTER_CLASS;
3956 printer = NULL;
3957 }
3958 else if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
3959 {
3960 /*
3961 * Bad URI...
3962 */
3963
3964 LogMessage(L_ERROR, "get_jobs: resource name \'%s\' no good!", resource);
3965 send_ipp_error(con, IPP_NOT_FOUND);
3966 return;
3967 }
3968 else
3969 dmask = CUPS_PRINTER_CLASS;
3970
3971 /*
3972 * Check policy...
3973 */
3974
3975 if (printer)
3976 {
3977 if (!CheckPolicy(printer->op_policy_ptr, con, NULL))
3978 {
3979 LogMessage(L_ERROR, "get_jobs: not authorized!");
3980 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3981 return;
3982 }
3983 }
3984 else if (!CheckPolicy(DefaultPolicyPtr, con, NULL))
3985 {
3986 LogMessage(L_ERROR, "get_jobs: not authorized!");
3987 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3988 return;
3989 }
3990
3991 /*
3992 * See if the "which-jobs" attribute have been specified...
3993 */
3994
3995 if ((attr = ippFindAttribute(con->request, "which-jobs", IPP_TAG_KEYWORD)) != NULL &&
3996 strcmp(attr->values[0].string.text, "completed") == 0)
3997 completed = 1;
3998 else
3999 completed = 0;
4000
4001 /*
4002 * See if they want to limit the number of jobs reported...
4003 */
4004
4005 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
4006 limit = attr->values[0].integer;
4007 else
4008 limit = 1000000;
4009
4010 /*
4011 * See if we only want to see jobs for a specific user...
4012 */
4013
4014 if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL &&
4015 attr->values[0].boolean)
4016 {
4017 if (con->username[0])
4018 strlcpy(username, con->username, sizeof(username));
4019 else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
4020 strlcpy(username, attr->values[0].string.text, sizeof(username));
4021 else
4022 strcpy(username, "anonymous");
4023 }
4024 else
4025 username[0] = '\0';
4026
4027 requested = ippFindAttribute(con->request, "requested-attributes",
4028 IPP_TAG_KEYWORD);
4029
4030 /*
4031 * OK, build a list of jobs for this printer...
4032 */
4033
4034 for (count = 0, job = Jobs; count < limit && job != NULL; job = job->next)
4035 {
4036 /*
4037 * Filter out jobs that don't match...
4038 */
4039
4040 LogMessage(L_DEBUG2, "get_jobs: job->id = %d", job->id);
4041
4042 if ((dest != NULL && strcmp(job->dest, dest) != 0) &&
4043 (job->printer == NULL || dest == NULL ||
4044 strcmp(job->printer->name, dest) != 0))
4045 continue;
4046 if ((job->dtype & dmask) != dtype &&
4047 (job->printer == NULL || (job->printer->type & dmask) != dtype))
4048 continue;
4049 if (username[0] != '\0' && strcmp(username, job->username) != 0)
4050 continue;
4051
4052 if (completed && job->state->values[0].integer <= IPP_JOB_STOPPED)
4053 continue;
4054 if (!completed && job->state->values[0].integer > IPP_JOB_STOPPED)
4055 continue;
4056
4057 count ++;
4058
4059 LogMessage(L_DEBUG2, "get_jobs: count = %d", count);
4060
4061 /*
4062 * Send the requested attributes for each job...
4063 */
4064
4065 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
4066 LocalPort, job->id);
4067
4068 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4069 "job-more-info", NULL, job_uri);
4070
4071 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4072 "job-uri", NULL, job_uri);
4073
4074 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
4075 "job-printer-up-time", time(NULL));
4076
4077 /*
4078 * Copy the job attributes to the response using the requested-attributes
4079 * attribute that may be provided by the client.
4080 */
4081
4082 copy_attrs(con->response, job->attrs, requested, IPP_TAG_JOB, 0);
4083
4084 add_job_state_reasons(con, job);
4085
4086 ippAddSeparator(con->response);
4087 }
4088
4089 if (requested != NULL)
4090 con->response->request.status.status_code = IPP_OK_SUBST;
4091 else
4092 con->response->request.status.status_code = IPP_OK;
4093 }
4094
4095
4096 /*
4097 * 'get_job_attrs()' - Get job attributes.
4098 */
4099
4100 static void
4101 get_job_attrs(client_t *con, /* I - Client connection */
4102 ipp_attribute_t *uri) /* I - Job URI */
4103 {
4104 ipp_attribute_t *attr, /* Current attribute */
4105 *requested; /* Requested attributes */
4106 int jobid; /* Job ID */
4107 job_t *job; /* Current job */
4108 char method[HTTP_MAX_URI], /* Method portion of URI */
4109 username[HTTP_MAX_URI], /* Username portion of URI */
4110 host[HTTP_MAX_URI], /* Host portion of URI */
4111 resource[HTTP_MAX_URI]; /* Resource portion of URI */
4112 int port; /* Port portion of URI */
4113 char job_uri[HTTP_MAX_URI]; /* Job URI... */
4114
4115
4116 LogMessage(L_DEBUG2, "get_job_attrs(%p[%d], %s)\n", con, con->http.fd,
4117 uri->values[0].string.text);
4118
4119 /*
4120 * See if we have a job URI or a printer URI...
4121 */
4122
4123 if (strcmp(uri->name, "printer-uri") == 0)
4124 {
4125 /*
4126 * Got a printer URI; see if we also have a job-id attribute...
4127 */
4128
4129 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4130 {
4131 LogMessage(L_ERROR, "get_job_attrs: got a printer-uri attribute but no job-id!");
4132 send_ipp_error(con, IPP_BAD_REQUEST);
4133 return;
4134 }
4135
4136 jobid = attr->values[0].integer;
4137 }
4138 else
4139 {
4140 /*
4141 * Got a job URI; parse it to get the job ID...
4142 */
4143
4144 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4145
4146 if (strncmp(resource, "/jobs/", 6) != 0)
4147 {
4148 /*
4149 * Not a valid URI!
4150 */
4151
4152 LogMessage(L_ERROR, "get_job_attrs: bad job-uri attribute \'%s\'!\n",
4153 uri->values[0].string.text);
4154 send_ipp_error(con, IPP_BAD_REQUEST);
4155 return;
4156 }
4157
4158 jobid = atoi(resource + 6);
4159 }
4160
4161 /*
4162 * See if the job exists...
4163 */
4164
4165 if ((job = FindJob(jobid)) == NULL)
4166 {
4167 /*
4168 * Nope - return a "not found" error...
4169 */
4170
4171 LogMessage(L_ERROR, "get_job_attrs: job #%d doesn't exist!", jobid);
4172 send_ipp_error(con, IPP_NOT_FOUND);
4173 return;
4174 }
4175
4176 /*
4177 * Check policy...
4178 */
4179
4180 if (!CheckPolicy(DefaultPolicyPtr, con, NULL))
4181 {
4182 LogMessage(L_ERROR, "get_job_attrs: not authorized!");
4183 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4184 return;
4185 }
4186
4187 /*
4188 * Put out the standard attributes...
4189 */
4190
4191 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d",
4192 ServerName, LocalPort, job->id);
4193
4194 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
4195
4196 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4197 "job-more-info", NULL, job_uri);
4198
4199 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4200 "job-uri", NULL, job_uri);
4201
4202 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
4203 "job-printer-up-time", time(NULL));
4204
4205 /*
4206 * Copy the job attributes to the response using the requested-attributes
4207 * attribute that may be provided by the client.
4208 */
4209
4210 requested = ippFindAttribute(con->request, "requested-attributes",
4211 IPP_TAG_KEYWORD);
4212
4213 copy_attrs(con->response, job->attrs, requested, IPP_TAG_JOB, 0);
4214
4215 add_job_state_reasons(con, job);
4216
4217 if (requested != NULL)
4218 con->response->request.status.status_code = IPP_OK_SUBST;
4219 else
4220 con->response->request.status.status_code = IPP_OK;
4221 }
4222
4223
4224 /*
4225 * 'get_notifications()' - Get events for a subscription.
4226 */
4227
4228 static void
4229 get_notifications(client_t *con, /* I - Client connection */
4230 int id) /* I - Subscription ID */
4231 {
4232 }
4233
4234
4235 /*
4236 * 'get_ppds()' - Get the list of PPD files on the local system.
4237 */
4238
4239 static void
4240 get_ppds(client_t *con) /* I - Client connection */
4241 {
4242 LogMessage(L_DEBUG2, "get_ppds(%p[%d])\n", con, con->http.fd);
4243
4244 /*
4245 * Check policy...
4246 */
4247
4248 if (!CheckPolicy(DefaultPolicyPtr, con, NULL))
4249 {
4250 LogMessage(L_ERROR, "get_ppds: not authorized!");
4251 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4252 return;
4253 }
4254
4255 /*
4256 * Copy the PPD attributes to the response using the requested-attributes
4257 * attribute that may be provided by the client.
4258 */
4259
4260 copy_attrs(con->response, PPDs,
4261 ippFindAttribute(con->request, "requested-attributes",
4262 IPP_TAG_KEYWORD), IPP_TAG_ZERO, IPP_TAG_COPY);
4263
4264 con->response->request.status.status_code = IPP_OK;
4265 }
4266
4267
4268 /*
4269 * 'get_printer_attrs()' - Get printer attributes.
4270 */
4271
4272 static void
4273 get_printer_attrs(client_t *con, /* I - Client connection */
4274 ipp_attribute_t *uri) /* I - Printer URI */
4275 {
4276 const char *dest; /* Destination */
4277 cups_ptype_t dtype; /* Destination type (printer or class) */
4278 char method[HTTP_MAX_URI],
4279 /* Method portion of URI */
4280 username[HTTP_MAX_URI],
4281 /* Username portion of URI */
4282 host[HTTP_MAX_URI],
4283 /* Host portion of URI */
4284 resource[HTTP_MAX_URI];
4285 /* Resource portion of URI */
4286 int port; /* Port portion of URI */
4287 printer_t *printer; /* Printer/class */
4288 time_t curtime; /* Current time */
4289 int i; /* Looping var */
4290 ipp_attribute_t *requested, /* requested-attributes */
4291 *history; /* History collection */
4292 int need_history; /* Need to send history collection? */
4293
4294
4295 LogMessage(L_DEBUG2, "get_printer_attrs(%p[%d], %s)\n", con, con->http.fd,
4296 uri->values[0].string.text);
4297
4298 /*
4299 * Is the destination valid?
4300 */
4301
4302 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4303
4304 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
4305 {
4306 /*
4307 * Bad URI...
4308 */
4309
4310 LogMessage(L_ERROR, "get_printer_attrs: resource name \'%s\' no good!", resource);
4311 send_ipp_error(con, IPP_NOT_FOUND);
4312 return;
4313 }
4314
4315 /*
4316 * Check policy...
4317 */
4318
4319 if (!CheckPolicy(printer->op_policy_ptr, con, NULL))
4320 {
4321 LogMessage(L_ERROR, "get_printer_attrs: not authorized!");
4322 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4323 return;
4324 }
4325
4326 curtime = time(NULL);
4327
4328 /*
4329 * Copy the printer attributes to the response using requested-attributes
4330 * and document-format attributes that may be provided by the client.
4331 */
4332
4333 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
4334 printer->state);
4335
4336 add_printer_state_reasons(con, printer);
4337
4338 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
4339 "printer-state-message", NULL, printer->state_message);
4340
4341 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
4342 printer->accepting);
4343
4344 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4345 "printer-up-time", curtime);
4346 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4347 "printer-state-time", printer->state_time);
4348 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
4349 ippTimeToDate(curtime));
4350
4351 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4352 "printer-error-policy", NULL, printer->op_policy);
4353 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4354 "printer-op-policy", NULL, printer->op_policy);
4355
4356 add_queued_job_count(con, printer);
4357
4358 requested = ippFindAttribute(con->request, "requested-attributes",
4359 IPP_TAG_KEYWORD);
4360
4361 copy_attrs(con->response, printer->attrs, requested, IPP_TAG_ZERO, 0);
4362 copy_attrs(con->response, CommonData, requested, IPP_TAG_ZERO, IPP_TAG_COPY);
4363
4364 need_history = 0;
4365
4366 if (MaxPrinterHistory > 0 && printer->num_history > 0 && requested)
4367 {
4368 for (i = 0; i < requested->num_values; i ++)
4369 if (!strcmp(requested->values[i].string.text, "all") ||
4370 !strcmp(requested->values[i].string.text, "printer-state-history"))
4371 {
4372 need_history = 1;
4373 break;
4374 }
4375 }
4376
4377 if (need_history)
4378 {
4379 history = ippAddCollections(con->response, IPP_TAG_PRINTER,
4380 "printer-state-history",
4381 printer->num_history, NULL);
4382
4383 for (i = 0; i < printer->num_history; i ++)
4384 copy_attrs(history->values[i].collection = ippNew(), printer->history[i],
4385 NULL, IPP_TAG_ZERO, 0);
4386 }
4387
4388 con->response->request.status.status_code = requested ? IPP_OK_SUBST : IPP_OK;
4389 }
4390
4391
4392 /*
4393 * 'get_printers()' - Get a list of printers or classes.
4394 */
4395
4396 static void
4397 get_printers(client_t *con, /* I - Client connection */
4398 int type) /* I - 0 or CUPS_PRINTER_CLASS */
4399 {
4400 int i; /* Looping var */
4401 ipp_attribute_t *requested, /* requested-attributes */
4402 *history, /* History collection */
4403 *attr; /* Current attribute */
4404 int need_history; /* Need to send history collection? */
4405 int limit; /* Maximum number of printers to return */
4406 int count; /* Number of printers that match */
4407 printer_t *printer; /* Current printer pointer */
4408 time_t curtime; /* Current time */
4409 int printer_type, /* printer-type attribute */
4410 printer_mask; /* printer-type-mask attribute */
4411 char *location; /* Location string */
4412 char name[IPP_MAX_NAME], /* Printer name */
4413 *nameptr; /* Pointer into name */
4414 printer_t *iclass; /* Implicit class */
4415 const char *username; /* Current user */
4416
4417
4418 LogMessage(L_DEBUG2, "get_printers(%p[%d], %x)\n", con, con->http.fd, type);
4419
4420 /*
4421 * Check policy...
4422 */
4423
4424 if (!CheckPolicy(DefaultPolicyPtr, con, NULL))
4425 {
4426 LogMessage(L_ERROR, "get_printers: not authorized!");
4427 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4428 return;
4429 }
4430
4431 /*
4432 * See if they want to limit the number of printers reported...
4433 */
4434
4435 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
4436 limit = attr->values[0].integer;
4437 else
4438 limit = 10000000;
4439
4440 /*
4441 * Support filtering...
4442 */
4443
4444 if ((attr = ippFindAttribute(con->request, "printer-type", IPP_TAG_ENUM)) != NULL)
4445 printer_type = attr->values[0].integer;
4446 else
4447 printer_type = 0;
4448
4449 if ((attr = ippFindAttribute(con->request, "printer-type-mask", IPP_TAG_ENUM)) != NULL)
4450 printer_mask = attr->values[0].integer;
4451 else
4452 printer_mask = 0;
4453
4454 if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
4455 location = attr->values[0].string.text;
4456 else
4457 location = NULL;
4458
4459 if (con->username[0])
4460 username = con->username;
4461 else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
4462 username = attr->values[0].string.text;
4463 else
4464 username = NULL;
4465
4466 requested = ippFindAttribute(con->request, "requested-attributes",
4467 IPP_TAG_KEYWORD);
4468
4469 need_history = 0;
4470
4471 if (MaxPrinterHistory > 0 && requested)
4472 {
4473 for (i = 0; i < requested->num_values; i ++)
4474 if (!strcmp(requested->values[i].string.text, "all") ||
4475 !strcmp(requested->values[i].string.text, "printer-state-history"))
4476 {
4477 need_history = 1;
4478 break;
4479 }
4480 }
4481
4482 /*
4483 * OK, build a list of printers for this printer...
4484 */
4485
4486 curtime = time(NULL);
4487
4488 for (count = 0, printer = Printers;
4489 count < limit && printer != NULL;
4490 printer = printer->next)
4491 if ((printer->type & CUPS_PRINTER_CLASS) == type &&
4492 (printer->type & printer_mask) == printer_type &&
4493 (location == NULL || printer->location == NULL ||
4494 strcasecmp(printer->location, location) == 0))
4495 {
4496 /*
4497 * If HideImplicitMembers is enabled, see if this printer or class
4498 * is a member of an implicit class...
4499 */
4500
4501 if (ImplicitClasses && HideImplicitMembers &&
4502 (printer->type & CUPS_PRINTER_REMOTE))
4503 {
4504 /*
4505 * Make a copy of the printer name...
4506 */
4507
4508 strlcpy(name, printer->name, sizeof(name));
4509
4510 if ((nameptr = strchr(name, '@')) != NULL)
4511 {
4512 /*
4513 * Strip trailing @server...
4514 */
4515
4516 *nameptr = '\0';
4517
4518 /*
4519 * Find the core printer, if any...
4520 */
4521
4522 if ((iclass = FindPrinter(name)) != NULL &&
4523 (iclass->type & CUPS_PRINTER_IMPLICIT))
4524 continue;
4525 }
4526 }
4527
4528 /*
4529 * If a username is specified, see if it is allowed or denied
4530 * access...
4531 */
4532
4533 if (printer->num_users && username && !user_allowed(printer, username))
4534 continue;
4535
4536 /*
4537 * Add the group separator as needed...
4538 */
4539
4540 if (count > 0)
4541 ippAddSeparator(con->response);
4542
4543 count ++;
4544
4545 /*
4546 * Send the following attributes for each printer:
4547 *
4548 * printer-state
4549 * printer-state-message
4550 * printer-is-accepting-jobs
4551 * + all printer attributes
4552 */
4553
4554 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
4555 "printer-state", printer->state);
4556
4557 add_printer_state_reasons(con, printer);
4558
4559 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
4560 "printer-state-message", NULL, printer->state_message);
4561
4562 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
4563 printer->accepting);
4564
4565 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4566 "printer-up-time", curtime);
4567 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4568 "printer-state-time", printer->state_time);
4569 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
4570 ippTimeToDate(curtime));
4571
4572 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4573 "printer-error-policy", NULL, printer->op_policy);
4574 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4575 "printer-op-policy", NULL, printer->op_policy);
4576
4577 add_queued_job_count(con, printer);
4578
4579 copy_attrs(con->response, printer->attrs, requested, IPP_TAG_ZERO, 0);
4580
4581 copy_attrs(con->response, CommonData, requested, IPP_TAG_ZERO,
4582 IPP_TAG_COPY);
4583
4584 if (need_history && printer->num_history > 0)
4585 {
4586 history = ippAddCollections(con->response, IPP_TAG_PRINTER,
4587 "printer-state-history",
4588 printer->num_history, NULL);
4589
4590 for (i = 0; i < printer->num_history; i ++)
4591 copy_attrs(history->values[i].collection = ippNew(),
4592 printer->history[i], NULL, IPP_TAG_ZERO, 0);
4593 }
4594 }
4595
4596 con->response->request.status.status_code = requested ? IPP_OK_SUBST : IPP_OK;
4597 }
4598
4599
4600 /*
4601 * 'get_subscription_attrs()' - Get subscription attributes.
4602 */
4603
4604 static void
4605 get_subscription_attrs(client_t *con, /* I - Client connection */
4606 int sub_id) /* I - Subscription ID */
4607 {
4608 }
4609
4610
4611 /*
4612 * 'get_subscriptions()' - Get subscriptions.
4613 */
4614
4615 static void
4616 get_subscriptions(client_t *con, /* I - Client connection */
4617 ipp_attribute_t *uri) /* I - Printer URI */
4618 {
4619 }
4620
4621
4622 /*
4623 * 'hold_job()' - Hold a print job.
4624 */
4625
4626 static void
4627 hold_job(client_t *con, /* I - Client connection */
4628 ipp_attribute_t *uri) /* I - Job or Printer URI */
4629 {
4630 ipp_attribute_t *attr, /* Current job-hold-until */
4631 *newattr; /* New job-hold-until */
4632 int jobid; /* Job ID */
4633 char method[HTTP_MAX_URI], /* Method portion of URI */
4634 username[HTTP_MAX_URI], /* Username portion of URI */
4635 host[HTTP_MAX_URI], /* Host portion of URI */
4636 resource[HTTP_MAX_URI]; /* Resource portion of URI */
4637 int port; /* Port portion of URI */
4638 job_t *job; /* Job information */
4639
4640
4641 LogMessage(L_DEBUG2, "hold_job(%p[%d], %s)\n", con, con->http.fd,
4642 uri->values[0].string.text);
4643
4644 /*
4645 * Verify that the POST operation was done to a valid URI.
4646 */
4647
4648 if (strncmp(con->uri, "/classes/", 9) != 0 &&
4649 strncmp(con->uri, "/jobs/", 5) != 0 &&
4650 strncmp(con->uri, "/printers/", 10) != 0)
4651 {
4652 LogMessage(L_ERROR, "hold_job: hold request on bad resource \'%s\'!",
4653 con->uri);
4654 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4655 return;
4656 }
4657
4658 /*
4659 * See if we have a job URI or a printer URI...
4660 */
4661
4662 if (strcmp(uri->name, "printer-uri") == 0)
4663 {
4664 /*
4665 * Got a printer URI; see if we also have a job-id attribute...
4666 */
4667
4668 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4669 {
4670 LogMessage(L_ERROR, "hold_job: got a printer-uri attribute but no job-id!");
4671 send_ipp_error(con, IPP_BAD_REQUEST);
4672 return;
4673 }
4674
4675 jobid = attr->values[0].integer;
4676 }
4677 else
4678 {
4679 /*
4680 * Got a job URI; parse it to get the job ID...
4681 */
4682
4683 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4684
4685 if (strncmp(resource, "/jobs/", 6) != 0)
4686 {
4687 /*
4688 * Not a valid URI!
4689 */
4690
4691 LogMessage(L_ERROR, "hold_job: bad job-uri attribute \'%s\'!",
4692 uri->values[0].string.text);
4693 send_ipp_error(con, IPP_BAD_REQUEST);
4694 return;
4695 }
4696
4697 jobid = atoi(resource + 6);
4698 }
4699
4700 /*
4701 * See if the job exists...
4702 */
4703
4704 if ((job = FindJob(jobid)) == NULL)
4705 {
4706 /*
4707 * Nope - return a "not found" error...
4708 */
4709
4710 LogMessage(L_ERROR, "hold_job: job #%d doesn't exist!", jobid);
4711 send_ipp_error(con, IPP_NOT_FOUND);
4712 return;
4713 }
4714
4715 /*
4716 * See if the job is owned by the requesting user...
4717 */
4718
4719 if (!validate_user(job, con, job->username, username, sizeof(username)))
4720 {
4721 LogMessage(L_ERROR, "hold_job: \"%s\" not authorized to hold job id %d owned by \"%s\"!",
4722 username, jobid, job->username);
4723 send_ipp_error(con, IPP_FORBIDDEN);
4724 return;
4725 }
4726
4727 /*
4728 * Hold the job and return...
4729 */
4730
4731 HoldJob(jobid);
4732
4733 if ((newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4734 newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
4735
4736 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4737 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4738
4739 if (attr != NULL)
4740 {
4741 /*
4742 * Free the old hold value and copy the new one over...
4743 */
4744
4745 free(attr->values[0].string.text);
4746
4747 if (newattr != NULL)
4748 {
4749 attr->value_tag = newattr->value_tag;
4750 attr->values[0].string.text = strdup(newattr->values[0].string.text);
4751 }
4752 else
4753 {
4754 attr->value_tag = IPP_TAG_KEYWORD;
4755 attr->values[0].string.text = strdup("indefinite");
4756 }
4757
4758 /*
4759 * Hold job until specified time...
4760 */
4761
4762 SetJobHoldUntil(job->id, attr->values[0].string.text);
4763 }
4764
4765 LogMessage(L_INFO, "Job %d was held by \'%s\'.", jobid, username);
4766
4767 con->response->request.status.status_code = IPP_OK;
4768 }
4769
4770
4771 /*
4772 * 'move_job()' - Move a job to a new destination.
4773 */
4774
4775 static void
4776 move_job(client_t *con, /* I - Client connection */
4777 ipp_attribute_t *uri) /* I - Job URI */
4778 {
4779 ipp_attribute_t *attr; /* Current attribute */
4780 int jobid; /* Job ID */
4781 job_t *job; /* Current job */
4782 const char *dest; /* Destination */
4783 cups_ptype_t dtype; /* Destination type (printer or class) */
4784 char method[HTTP_MAX_URI], /* Method portion of URI */
4785 username[HTTP_MAX_URI], /* Username portion of URI */
4786 host[HTTP_MAX_URI], /* Host portion of URI */
4787 resource[HTTP_MAX_URI]; /* Resource portion of URI */
4788 int port; /* Port portion of URI */
4789 printer_t *printer; /* Printer */
4790
4791
4792 LogMessage(L_DEBUG2, "move_job(%p[%d], %s)\n", con, con->http.fd,
4793 uri->values[0].string.text);
4794
4795 /*
4796 * See if we have a job URI or a printer URI...
4797 */
4798
4799 if (strcmp(uri->name, "printer-uri") == 0)
4800 {
4801 /*
4802 * Got a printer URI; see if we also have a job-id attribute...
4803 */
4804
4805 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4806 {
4807 LogMessage(L_ERROR, "move_job: got a printer-uri attribute but no job-id!");
4808 send_ipp_error(con, IPP_BAD_REQUEST);
4809 return;
4810 }
4811
4812 jobid = attr->values[0].integer;
4813 }
4814 else
4815 {
4816 /*
4817 * Got a job URI; parse it to get the job ID...
4818 */
4819
4820 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4821
4822 if (strncmp(resource, "/jobs/", 6) != 0)
4823 {
4824 /*
4825 * Not a valid URI!
4826 */
4827
4828 LogMessage(L_ERROR, "move_job: bad job-uri attribute \'%s\'!\n",
4829 uri->values[0].string.text);
4830 send_ipp_error(con, IPP_BAD_REQUEST);
4831 return;
4832 }
4833
4834 jobid = atoi(resource + 6);
4835 }
4836
4837 /*
4838 * See if the job exists...
4839 */
4840
4841 if ((job = FindJob(jobid)) == NULL)
4842 {
4843 /*
4844 * Nope - return a "not found" error...
4845 */
4846
4847 LogMessage(L_ERROR, "move_job: job #%d doesn't exist!", jobid);
4848 send_ipp_error(con, IPP_NOT_FOUND);
4849 return;
4850 }
4851
4852 /*
4853 * See if the job has been completed...
4854 */
4855
4856 if (job->state->values[0].integer > IPP_JOB_STOPPED)
4857 {
4858 /*
4859 * Return a "not-possible" error...
4860 */
4861
4862 LogMessage(L_ERROR, "move_job: job #%d is finished and cannot be altered!", jobid);
4863 send_ipp_error(con, IPP_NOT_POSSIBLE);
4864 return;
4865 }
4866
4867 /*
4868 * See if the job is owned by the requesting user...
4869 */
4870
4871 if (!validate_user(job, con, job->username, username, sizeof(username)))
4872 {
4873 LogMessage(L_ERROR, "move_job: \"%s\" not authorized to move job id %d owned by \"%s\"!",
4874 username, jobid, job->username);
4875 send_ipp_error(con, IPP_FORBIDDEN);
4876 return;
4877 }
4878
4879 if ((attr = ippFindAttribute(con->request, "job-printer-uri", IPP_TAG_URI)) == NULL)
4880 {
4881 /*
4882 * Need job-printer-uri...
4883 */
4884
4885 LogMessage(L_ERROR, "move_job: job-printer-uri attribute missing!");
4886 send_ipp_error(con, IPP_BAD_REQUEST);
4887 return;
4888 }
4889
4890 /*
4891 * Get the new printer or class...
4892 */
4893
4894 httpSeparate(attr->values[0].string.text, method, username, host, &port,
4895 resource);
4896 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
4897 {
4898 /*
4899 * Bad URI...
4900 */
4901
4902 LogMessage(L_ERROR, "move_job: resource name \'%s\' no good!", resource);
4903 send_ipp_error(con, IPP_NOT_FOUND);
4904 return;
4905 }
4906
4907 /*
4908 * Check policy...
4909 */
4910
4911 if (!CheckPolicy(printer->op_policy_ptr, con, NULL))
4912 {
4913 LogMessage(L_ERROR, "move_job: not authorized!");
4914 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4915 return;
4916 }
4917
4918 /*
4919 * Move the job to a different printer or class...
4920 */
4921
4922 MoveJob(jobid, dest);
4923
4924 /*
4925 * Start jobs if possible...
4926 */
4927
4928 CheckJobs();
4929
4930 /*
4931 * Return with "everything is OK" status...
4932 */
4933
4934 con->response->request.status.status_code = IPP_OK;
4935 }
4936
4937
4938 /*
4939 * 'ppd_add_default()' - Add a PPD default choice.
4940 */
4941
4942 static int /* O - Number of defaults */
4943 ppd_add_default(
4944 const char *option, /* I - Option name */
4945 const char *choice, /* I - Choice name */
4946 int num_defaults, /* I - Number of defaults */
4947 ppd_default_t **defaults) /* IO - Defaults */
4948 {
4949 int i; /* Looping var */
4950 ppd_default_t *temp; /* Temporary defaults array */
4951
4952
4953 /*
4954 * First check if the option already has a default value; the PPD spec
4955 * says that the first one is used...
4956 */
4957
4958 for (i = 0, temp = *defaults; i < num_defaults; i ++)
4959 if (!strcmp(option, temp[i].option))
4960 return (num_defaults);
4961
4962 /*
4963 * Now add the option...
4964 */
4965
4966 if (num_defaults == 0)
4967 temp = malloc(sizeof(ppd_default_t));
4968 else
4969 temp = realloc(*defaults, (num_defaults + 1) * sizeof(ppd_default_t));
4970
4971 if (!temp)
4972 {
4973 LogMessage(L_ERROR, "ppd_add_default: Unable to add default value for \"%s\" - %s",
4974 option, strerror(errno));
4975 return (num_defaults);
4976 }
4977
4978 *defaults = temp;
4979 temp += num_defaults;
4980
4981 strlcpy(temp->option, option, sizeof(temp->option));
4982 strlcpy(temp->choice, choice, sizeof(temp->choice));
4983
4984 return (num_defaults + 1);
4985 }
4986
4987
4988 /*
4989 * 'ppd_parse_line()' - Parse a PPD default line.
4990 */
4991
4992 static int /* O - 0 on success, -1 on failure */
4993 ppd_parse_line(const char *line, /* I - Line */
4994 char *option, /* O - Option name */
4995 int olen, /* I - Size of option name */
4996 char *choice, /* O - Choice name */
4997 int clen) /* I - Size of choice name */
4998 {
4999 /*
5000 * Verify this is a default option line...
5001 */
5002
5003 if (strncmp(line, "*Default", 8))
5004 return (-1);
5005
5006 /*
5007 * Read the option name...
5008 */
5009
5010 for (line += 8, olen --; isalnum(*line & 255); line ++)
5011 if (olen > 0)
5012 {
5013 *option++ = *line;
5014 olen --;
5015 }
5016
5017 *option = '\0';
5018
5019 /*
5020 * Skip everything else up to the colon (:)...
5021 */
5022
5023 while (*line && *line != ':')
5024 line ++;
5025
5026 if (!*line)
5027 return (-1);
5028
5029 line ++;
5030
5031 /*
5032 * Now grab the option choice, skipping leading whitespace...
5033 */
5034
5035 while (isspace(*line & 255))
5036 line ++;
5037
5038 for (clen --; isalnum(*line & 255); line ++)
5039 if (clen > 0)
5040 {
5041 *choice++ = *line;
5042 clen --;
5043 }
5044
5045 *choice = '\0';
5046
5047 /*
5048 * Return with no errors...
5049 */
5050
5051 return (0);
5052 }
5053
5054
5055 /*
5056 * 'print_job()' - Print a file to a printer or class.
5057 */
5058
5059 static void
5060 print_job(client_t *con, /* I - Client connection */
5061 ipp_attribute_t *uri) /* I - Printer URI */
5062 {
5063 ipp_attribute_t *attr; /* Current attribute */
5064 ipp_attribute_t *format; /* Document-format attribute */
5065 const char *dest; /* Destination */
5066 cups_ptype_t dtype; /* Destination type (printer or class) */
5067 int priority; /* Job priority */
5068 char *title; /* Job name/title */
5069 job_t *job; /* Current job */
5070 int jobid; /* Job ID number */
5071 char job_uri[HTTP_MAX_URI], /* Job URI */
5072 method[HTTP_MAX_URI], /* Method portion of URI */
5073 username[HTTP_MAX_URI], /* Username portion of URI */
5074 host[HTTP_MAX_URI], /* Host portion of URI */
5075 resource[HTTP_MAX_URI], /* Resource portion of URI */
5076 filename[1024]; /* Job filename */
5077 int port; /* Port portion of URI */
5078 mime_type_t *filetype; /* Type of file */
5079 char super[MIME_MAX_SUPER], /* Supertype of file */
5080 type[MIME_MAX_TYPE], /* Subtype of file */
5081 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
5082 /* Textual name of mime type */
5083 printer_t *printer; /* Printer data */
5084 struct stat fileinfo; /* File information */
5085 int kbytes; /* Size of file */
5086 int i; /* Looping var */
5087 int lowerpagerange; /* Page range bound */
5088 int compression; /* Document compression */
5089
5090
5091 LogMessage(L_DEBUG2, "print_job(%p[%d], %s)\n", con, con->http.fd,
5092 uri->values[0].string.text);
5093
5094 /*
5095 * Verify that the POST operation was done to a valid URI.
5096 */
5097
5098 if (strncmp(con->uri, "/classes/", 9) != 0 &&
5099 strncmp(con->uri, "/printers/", 10) != 0)
5100 {
5101 LogMessage(L_ERROR, "print_job: cancel request on bad resource \'%s\'!",
5102 con->uri);
5103 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5104 return;
5105 }
5106
5107 /*
5108 * Validate job template attributes; for now just copies and page-ranges...
5109 */
5110
5111 if ((attr = ippFindAttribute(con->request, "copies", IPP_TAG_INTEGER)) != NULL)
5112 {
5113 if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
5114 {
5115 LogMessage(L_INFO, "print_job: bad copies value %d.",
5116 attr->values[0].integer);
5117 send_ipp_error(con, IPP_BAD_REQUEST);
5118 return;
5119 }
5120 }
5121
5122 if ((attr = ippFindAttribute(con->request, "page-ranges", IPP_TAG_RANGE)) != NULL)
5123 {
5124 for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
5125 {
5126 if (attr->values[i].range.lower < lowerpagerange ||
5127 attr->values[i].range.lower > attr->values[i].range.upper)
5128 {
5129 LogMessage(L_ERROR, "print_job: bad page-ranges values %d-%d.",
5130 attr->values[i].range.lower, attr->values[i].range.upper);
5131 send_ipp_error(con, IPP_BAD_REQUEST);
5132 return;
5133 }
5134
5135 lowerpagerange = attr->values[i].range.upper + 1;
5136 }
5137 }
5138
5139 /*
5140 * OK, see if the client is sending the document compressed - CUPS
5141 * only supports "none" and "gzip".
5142 */
5143
5144 compression = CUPS_FILE_NONE;
5145
5146 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL)
5147 {
5148 if (strcmp(attr->values[0].string.text, "none")
5149 #ifdef HAVE_LIBZ
5150 && strcmp(attr->values[0].string.text, "gzip")
5151 #endif /* HAVE_LIBZ */
5152 )
5153 {
5154 LogMessage(L_ERROR, "print_job: Unsupported compression \"%s\"!",
5155 attr->values[0].string.text);
5156 send_ipp_error(con, IPP_ATTRIBUTES);
5157 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
5158 "compression", NULL, attr->values[0].string.text);
5159 return;
5160 }
5161
5162 #ifdef HAVE_LIBZ
5163 if (!strcmp(attr->values[0].string.text, "gzip"))
5164 compression = CUPS_FILE_GZIP;
5165 #endif /* HAVE_LIBZ */
5166 }
5167
5168 /*
5169 * Do we have a file to print?
5170 */
5171
5172 if (!con->filename)
5173 {
5174 LogMessage(L_ERROR, "print_job: No file!?!");
5175 send_ipp_error(con, IPP_BAD_REQUEST);
5176 return;
5177 }
5178
5179 /*
5180 * Is it a format we support?
5181 */
5182
5183 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
5184 {
5185 /*
5186 * Grab format from client...
5187 */
5188
5189 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
5190 {
5191 LogMessage(L_ERROR, "print_job: could not scan type \'%s\'!",
5192 format->values[0].string.text);
5193 send_ipp_error(con, IPP_BAD_REQUEST);
5194 return;
5195 }
5196 }
5197 else
5198 {
5199 /*
5200 * No document format attribute? Auto-type it!
5201 */
5202
5203 strcpy(super, "application");
5204 strcpy(type, "octet-stream");
5205 }
5206
5207 if (strcmp(super, "application") == 0 &&
5208 strcmp(type, "octet-stream") == 0)
5209 {
5210 /*
5211 * Auto-type the file...
5212 */
5213
5214 LogMessage(L_DEBUG, "print_job: auto-typing file...");
5215
5216 filetype = mimeFileType(MimeDatabase, con->filename, &compression);
5217
5218 if (filetype != NULL)
5219 {
5220 /*
5221 * Replace the document-format attribute value with the auto-typed one.
5222 */
5223
5224 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
5225 filetype->type);
5226
5227 if (format != NULL)
5228 {
5229 free(format->values[0].string.text);
5230 format->values[0].string.text = strdup(mimetype);
5231 }
5232 else
5233 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
5234 "document-format", NULL, mimetype);
5235 }
5236 else
5237 filetype = mimeType(MimeDatabase, super, type);
5238 }
5239 else
5240 filetype = mimeType(MimeDatabase, super, type);
5241
5242 if (filetype == NULL)
5243 {
5244 LogMessage(L_ERROR, "print_job: Unsupported format \'%s/%s\'!",
5245 super, type);
5246 LogMessage(L_INFO, "Hint: Do you have the raw file printing rules enabled?");
5247 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
5248
5249 if (format)
5250 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
5251 "document-format", NULL, format->values[0].string.text);
5252
5253 return;
5254 }
5255
5256 LogMessage(L_DEBUG, "print_job: request file type is %s/%s.",
5257 filetype->super, filetype->type);
5258
5259 /*
5260 * Read any embedded job ticket info from PS files...
5261 */
5262
5263 if (strcasecmp(filetype->super, "application") == 0 &&
5264 strcasecmp(filetype->type, "postscript") == 0)
5265 read_ps_job_ticket(con);
5266
5267 /*
5268 * Is the destination valid?
5269 */
5270
5271 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5272
5273 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
5274 {
5275 /*
5276 * Bad URI...
5277 */
5278
5279 LogMessage(L_ERROR, "print_job: resource name \'%s\' no good!", resource);
5280 send_ipp_error(con, IPP_NOT_FOUND);
5281 return;
5282 }
5283
5284 /*
5285 * Check policy...
5286 */
5287
5288 if (!CheckPolicy(printer->op_policy_ptr, con, NULL))
5289 {
5290 LogMessage(L_ERROR, "print_job: not authorized!");
5291 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5292 return;
5293 }
5294
5295 /*
5296 * See if the printer is accepting jobs...
5297 */
5298
5299 if (!printer->accepting)
5300 {
5301 LogMessage(L_INFO, "print_job: destination \'%s\' is not accepting jobs.",
5302 dest);
5303 send_ipp_error(con, IPP_NOT_ACCEPTING);
5304 return;
5305 }
5306
5307 /*
5308 * Make sure we aren't over our limit...
5309 */
5310
5311 if (NumJobs >= MaxJobs && MaxJobs)
5312 CleanJobs();
5313
5314 if (NumJobs >= MaxJobs && MaxJobs)
5315 {
5316 LogMessage(L_INFO, "print_job: too many jobs - %d jobs, max jobs is %d.",
5317 NumJobs, MaxJobs);
5318 send_ipp_error(con, IPP_NOT_POSSIBLE);
5319 return;
5320 }
5321
5322 if (!check_quotas(con, printer))
5323 {
5324 send_ipp_error(con, IPP_NOT_POSSIBLE);
5325 return;
5326 }
5327
5328 /*
5329 * Create the job and set things up...
5330 */
5331
5332 if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
5333 priority = attr->values[0].integer;
5334 else
5335 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
5336 priority = 50);
5337
5338 if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
5339 title = attr->values[0].string.text;
5340 else
5341 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
5342 title = "Untitled");
5343
5344 if ((job = AddJob(priority, printer->name)) == NULL)
5345 {
5346 LogMessage(L_ERROR, "print_job: unable to add job for destination \'%s\'!",
5347 dest);
5348 send_ipp_error(con, IPP_INTERNAL_ERROR);
5349 return;
5350 }
5351
5352 job->dtype = dtype;
5353 job->attrs = con->request;
5354 con->request = NULL;
5355
5356 /*
5357 * Copy the rest of the job info...
5358 */
5359
5360 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
5361
5362 if (con->username[0])
5363 SetString(&job->username, con->username);
5364 else if (attr != NULL)
5365 {
5366 LogMessage(L_DEBUG, "print_job: requesting-user-name = \'%s\'",
5367 attr->values[0].string.text);
5368
5369 SetString(&job->username, attr->values[0].string.text);
5370 }
5371 else
5372 SetString(&job->username, "anonymous");
5373
5374 if (attr == NULL)
5375 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
5376 NULL, job->username);
5377 else
5378 {
5379 attr->group_tag = IPP_TAG_JOB;
5380 SetString(&attr->name, "job-originating-user-name");
5381 }
5382
5383 /*
5384 * Add remaining job attributes...
5385 */
5386
5387 if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
5388 IPP_TAG_ZERO)) != NULL)
5389 {
5390 /*
5391 * Request contains a job-originating-host-name attribute; validate it...
5392 */
5393
5394 if (attr->value_tag != IPP_TAG_NAME ||
5395 attr->num_values != 1 ||
5396 strcmp(con->http.hostname, "localhost") != 0)
5397 {
5398 /*
5399 * Can't override the value if we aren't connected via localhost.
5400 * Also, we can only have 1 value and it must be a name value.
5401 */
5402
5403 switch (attr->value_tag)
5404 {
5405 case IPP_TAG_STRING :
5406 case IPP_TAG_TEXTLANG :
5407 case IPP_TAG_NAMELANG :
5408 case IPP_TAG_TEXT :
5409 case IPP_TAG_NAME :
5410 case IPP_TAG_KEYWORD :
5411 case IPP_TAG_URI :
5412 case IPP_TAG_URISCHEME :
5413 case IPP_TAG_CHARSET :
5414 case IPP_TAG_LANGUAGE :
5415 case IPP_TAG_MIMETYPE :
5416 /*
5417 * Free old strings...
5418 */
5419
5420 for (i = 0; i < attr->num_values; i ++)
5421 {
5422 free(attr->values[i].string.text);
5423 attr->values[i].string.text = NULL;
5424 if (attr->values[i].string.charset)
5425 {
5426 free(attr->values[i].string.charset);
5427 attr->values[i].string.charset = NULL;
5428 }
5429 }
5430
5431 default :
5432 break;
5433 }
5434
5435 /*
5436 * Use the default connection hostname instead...
5437 */
5438
5439 attr->value_tag = IPP_TAG_NAME;
5440 attr->num_values = 1;
5441 attr->values[0].string.text = strdup(con->http.hostname);
5442 }
5443
5444 attr->group_tag = IPP_TAG_JOB;
5445 }
5446 else
5447 {
5448 /*
5449 * No job-originating-host-name attribute, so use the hostname from
5450 * the connection...
5451 */
5452
5453 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
5454 "job-originating-host-name", NULL, con->http.hostname);
5455 }
5456
5457 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
5458 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
5459 "job-state", IPP_JOB_PENDING);
5460 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
5461 "job-media-sheets-completed", 0);
5462 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
5463 printer->uri);
5464 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
5465 title);
5466
5467 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) == NULL)
5468 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
5469 "job-k-octets", 0);
5470
5471 if (stat(con->filename, &fileinfo))
5472 kbytes = 0;
5473 else
5474 kbytes = (fileinfo.st_size + 1023) / 1024;
5475
5476 UpdateQuota(printer, job->username, 0, kbytes);
5477 attr->values[0].integer += kbytes;
5478
5479 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
5480 time(NULL));
5481 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
5482 "time-at-processing", 0);
5483 attr->value_tag = IPP_TAG_NOVALUE;
5484 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
5485 "time-at-completed", 0);
5486 attr->value_tag = IPP_TAG_NOVALUE;
5487
5488 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
5489 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
5490 if (attr == NULL)
5491 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
5492 "job-hold-until", NULL, "no-hold");
5493
5494 if (attr != NULL && strcmp(attr->values[0].string.text, "no-hold") != 0 &&
5495 !(printer->type & CUPS_PRINTER_REMOTE))
5496 {
5497 /*
5498 * Hold job until specified time...
5499 */
5500
5501 job->state->values[0].integer = IPP_JOB_HELD;
5502 SetJobHoldUntil(job->id, attr->values[0].string.text);
5503 }
5504
5505 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
5506 Classification)
5507 {
5508 /*
5509 * Add job sheets options...
5510 */
5511
5512 if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) == NULL)
5513 {
5514 LogMessage(L_DEBUG, "Adding default job-sheets values \"%s,%s\"...",
5515 printer->job_sheets[0], printer->job_sheets[1]);
5516
5517 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
5518 2, NULL, NULL);
5519 attr->values[0].string.text = strdup(printer->job_sheets[0]);
5520 attr->values[1].string.text = strdup(printer->job_sheets[1]);
5521 }
5522
5523 job->job_sheets = attr;
5524
5525 /*
5526 * Enforce classification level if set...
5527 */
5528
5529 if (Classification)
5530 {
5531 LogMessage(L_INFO, "Classification=\"%s\", ClassifyOverride=%d",
5532 Classification ? Classification : "(null)", ClassifyOverride);
5533
5534 if (ClassifyOverride)
5535 {
5536 if (strcmp(attr->values[0].string.text, "none") == 0 &&
5537 (attr->num_values == 1 ||
5538 strcmp(attr->values[1].string.text, "none") == 0))
5539 {
5540 /*
5541 * Force the leading banner to have the classification on it...
5542 */
5543
5544 SetString(&attr->values[0].string.text, Classification);
5545
5546 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
5547 "job-sheets=\"%s,none\", "
5548 "job-originating-user-name=\"%s\"",
5549 job->id, Classification,
5550 job->username);
5551 }
5552 else if (attr->num_values == 2 &&
5553 strcmp(attr->values[0].string.text, attr->values[1].string.text) != 0 &&
5554 strcmp(attr->values[0].string.text, "none") != 0 &&
5555 strcmp(attr->values[1].string.text, "none") != 0)
5556 {
5557 /*
5558 * Can't put two different security markings on the same document!
5559 */
5560
5561 SetString(&attr->values[1].string.text, attr->values[0].string.text);
5562
5563 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
5564 "job-sheets=\"%s,%s\", "
5565 "job-originating-user-name=\"%s\"",
5566 job->id, attr->values[0].string.text,
5567 attr->values[1].string.text,
5568 job->username);
5569 }
5570 else if (strcmp(attr->values[0].string.text, Classification) &&
5571 strcmp(attr->values[0].string.text, "none") &&
5572 (attr->num_values == 1 ||
5573 (strcmp(attr->values[1].string.text, Classification) &&
5574 strcmp(attr->values[1].string.text, "none"))))
5575 {
5576 if (attr->num_values == 1)
5577 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION OVERRIDDEN "
5578 "job-sheets=\"%s\", "
5579 "job-originating-user-name=\"%s\"",
5580 job->id, attr->values[0].string.text,
5581 job->username);
5582 else
5583 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION OVERRIDDEN "
5584 "job-sheets=\"%s,%s\", "
5585 "job-originating-user-name=\"%s\"",
5586 job->id, attr->values[0].string.text,
5587 attr->values[1].string.text,
5588 job->username);
5589 }
5590 }
5591 else if (strcmp(attr->values[0].string.text, Classification) != 0 &&
5592 (attr->num_values == 1 ||
5593 strcmp(attr->values[1].string.text, Classification) != 0))
5594 {
5595 /*
5596 * Force the banner to have the classification on it...
5597 */
5598
5599 if (attr->num_values > 1 &&
5600 !strcmp(attr->values[0].string.text, attr->values[1].string.text))
5601 {
5602 SetString(&(attr->values[0].string.text), Classification);
5603 SetString(&(attr->values[1].string.text), Classification);
5604 }
5605 else
5606 {
5607 if (attr->num_values == 1 ||
5608 strcmp(attr->values[0].string.text, "none"))
5609 SetString(&(attr->values[0].string.text), Classification);
5610
5611 if (attr->num_values > 1 &&
5612 strcmp(attr->values[1].string.text, "none"))
5613 SetString(&(attr->values[1].string.text), Classification);
5614 }
5615
5616 if (attr->num_values > 1)
5617 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
5618 "job-sheets=\"%s,%s\", "
5619 "job-originating-user-name=\"%s\"",
5620 job->id, attr->values[0].string.text,
5621 attr->values[1].string.text,
5622 job->username);
5623 else
5624 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
5625 "job-sheets=\"%s\", "
5626 "job-originating-user-name=\"%s\"",
5627 job->id, Classification,
5628 job->username);
5629 }
5630 }
5631
5632 /*
5633 * Add the starting sheet...
5634 */
5635
5636 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
5637 {
5638 LogMessage(L_INFO, "Adding start banner page \"%s\" to job %d.",
5639 attr->values[0].string.text, job->id);
5640
5641 kbytes = copy_banner(con, job, attr->values[0].string.text);
5642
5643 UpdateQuota(printer, job->username, 0, kbytes);
5644 }
5645 }
5646 else if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
5647 job->sheets = attr;
5648
5649 /*
5650 * Add the job file...
5651 */
5652
5653 if (add_file(con, job, filetype, compression))
5654 return;
5655
5656 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
5657 job->num_files);
5658 rename(con->filename, filename);
5659 ClearString(&con->filename);
5660
5661 /*
5662 * See if we need to add the ending sheet...
5663 */
5664
5665 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
5666 attr->num_values > 1)
5667 {
5668 /*
5669 * Yes...
5670 */
5671
5672 LogMessage(L_INFO, "Adding end banner page \"%s\" to job %d.",
5673 attr->values[1].string.text, job->id);
5674
5675 kbytes = copy_banner(con, job, attr->values[1].string.text);
5676
5677 UpdateQuota(printer, job->username, 0, kbytes);
5678 }
5679
5680 /*
5681 * Add any job subscriptions...
5682 */
5683
5684 add_job_subscriptions(con, job);
5685
5686 /*
5687 * Set all but the first two attributes to the job attributes group...
5688 */
5689
5690 for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
5691 attr->group_tag = IPP_TAG_JOB;
5692
5693 /*
5694 * Log and save the job...
5695 */
5696
5697 LogMessage(L_INFO, "Job %d queued on \'%s\' by \'%s\'.", job->id,
5698 job->dest, job->username);
5699 LogMessage(L_DEBUG, "Job %d hold_until = %d", job->id, (int)job->hold_until);
5700
5701 SaveJob(job->id);
5702
5703 cupsdAddEvent(CUPSD_EVENT_JOB_CREATED, printer, job, "Job created.");
5704
5705 /*
5706 * Start the job if possible... Since CheckJobs() can cancel a job if it
5707 * doesn't print, we need to re-find the job afterwards...
5708 */
5709
5710 jobid = job->id;
5711
5712 CheckJobs();
5713
5714 job = FindJob(jobid);
5715
5716 /*
5717 * Fill in the response info...
5718 */
5719
5720 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
5721 LocalPort, jobid);
5722
5723 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
5724
5725 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
5726
5727 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
5728 job ? job->state->values[0].integer : IPP_JOB_CANCELLED);
5729 add_job_state_reasons(con, job);
5730
5731 con->response->request.status.status_code = IPP_OK;
5732 }
5733
5734
5735 /*
5736 * 'read_ps_job_ticket()' - Reads a job ticket embedded in a PS file.
5737 *
5738 * This function only gets called when printing a single PostScript
5739 * file using the Print-Job operation. It doesn't work for Create-Job +
5740 * Send-File, since the job attributes need to be set at job creation
5741 * time for banners to work. The embedded PS job ticket stuff is here
5742 * only to allow the Windows printer driver for CUPS to pass in JCL
5743 * options and IPP attributes which otherwise would be lost.
5744 *
5745 * The format of a PS job ticket is simple:
5746 *
5747 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
5748 *
5749 * %cupsJobTicket: attr1=value1
5750 * %cupsJobTicket: attr2=value2
5751 * ...
5752 * %cupsJobTicket: attrN=valueN
5753 *
5754 * Job ticket lines must appear immediately after the first line that
5755 * specifies PostScript format (%!PS-Adobe-3.0), and CUPS will stop
5756 * looking for job ticket info when it finds a line that does not begin
5757 * with "%cupsJobTicket:".
5758 *
5759 * The maximum length of a job ticket line, including the prefix, is
5760 * 255 characters to conform with the Adobe DSC.
5761 *
5762 * Read-only attributes are rejected with a notice to the error log in
5763 * case a malicious user tries anything. Since the job ticket is read
5764 * prior to attribute validation in print_job(), job ticket attributes
5765 * will go through the same validation as IPP attributes...
5766 */
5767
5768 static void
5769 read_ps_job_ticket(client_t *con) /* I - Client connection */
5770 {
5771 cups_file_t *fp; /* File to read from */
5772 char line[256]; /* Line data */
5773 int num_options; /* Number of options */
5774 cups_option_t *options; /* Options */
5775 ipp_t *ticket; /* New attributes */
5776 ipp_attribute_t *attr, /* Current attribute */
5777 *attr2, /* Job attribute */
5778 *prev2; /* Previous job attribute */
5779
5780
5781 /*
5782 * First open the print file...
5783 */
5784
5785 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
5786 {
5787 LogMessage(L_ERROR, "read_ps_job_ticket: Unable to open PostScript print file - %s",
5788 strerror(errno));
5789 return;
5790 }
5791
5792 /*
5793 * Skip the first line...
5794 */
5795
5796 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
5797 {
5798 LogMessage(L_ERROR, "read_ps_job_ticket: Unable to read from PostScript print file - %s",
5799 strerror(errno));
5800 cupsFileClose(fp);
5801 return;
5802 }
5803
5804 if (strncmp(line, "%!PS-Adobe-", 11) != 0)
5805 {
5806 /*
5807 * Not a DSC-compliant file, so no job ticket info will be available...
5808 */
5809
5810 cupsFileClose(fp);
5811 return;
5812 }
5813
5814 /*
5815 * Read job ticket info from the file...
5816 */
5817
5818 num_options = 0;
5819 options = NULL;
5820
5821 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
5822 {
5823 /*
5824 * Stop at the first non-ticket line...
5825 */
5826
5827 if (strncmp(line, "%cupsJobTicket:", 15) != 0)
5828 break;
5829
5830 /*
5831 * Add the options to the option array...
5832 */
5833
5834 num_options = cupsParseOptions(line + 15, num_options, &options);
5835 }
5836
5837 /*
5838 * Done with the file; see if we have any options...
5839 */
5840
5841 cupsFileClose(fp);
5842
5843 if (num_options == 0)
5844 return;
5845
5846 /*
5847 * OK, convert the options to an attribute list, and apply them to
5848 * the request...
5849 */
5850
5851 ticket = ippNew();
5852 cupsEncodeOptions(ticket, num_options, options);
5853
5854 /*
5855 * See what the user wants to change.
5856 */
5857
5858 for (attr = ticket->attrs; attr != NULL; attr = attr->next)
5859 {
5860 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
5861 continue;
5862
5863 if (strcmp(attr->name, "job-originating-host-name") == 0 ||
5864 strcmp(attr->name, "job-originating-user-name") == 0 ||
5865 strcmp(attr->name, "job-media-sheets-completed") == 0 ||
5866 strcmp(attr->name, "job-k-octets") == 0 ||
5867 strcmp(attr->name, "job-id") == 0 ||
5868 strncmp(attr->name, "job-state", 9) == 0 ||
5869 strncmp(attr->name, "time-at-", 8) == 0)
5870 continue; /* Read-only attrs */
5871
5872 if ((attr2 = ippFindAttribute(con->request, attr->name, IPP_TAG_ZERO)) != NULL)
5873 {
5874 /*
5875 * Some other value; first free the old value...
5876 */
5877
5878 if (con->request->attrs == attr2)
5879 {
5880 con->request->attrs = attr2->next;
5881 prev2 = NULL;
5882 }
5883 else
5884 {
5885 for (prev2 = con->request->attrs; prev2 != NULL; prev2 = prev2->next)
5886 if (prev2->next == attr2)
5887 {
5888 prev2->next = attr2->next;
5889 break;
5890 }
5891 }
5892
5893 if (con->request->last == attr2)
5894 con->request->last = prev2;
5895
5896 _ipp_free_attr(attr2);
5897 }
5898
5899 /*
5900 * Add new option by copying it...
5901 */
5902
5903 copy_attribute(con->request, attr, 0);
5904 }
5905
5906 /*
5907 * Then free the attribute list and option array...
5908 */
5909
5910 ippDelete(ticket);
5911 cupsFreeOptions(num_options, options);
5912 }
5913
5914
5915 /*
5916 * 'reject_jobs()' - Reject print jobs to a printer.
5917 */
5918
5919 static void
5920 reject_jobs(client_t *con, /* I - Client connection */
5921 ipp_attribute_t *uri) /* I - Printer or class URI */
5922 {
5923 cups_ptype_t dtype; /* Destination type (printer or class) */
5924 char method[HTTP_MAX_URI], /* Method portion of URI */
5925 username[HTTP_MAX_URI], /* Username portion of URI */
5926 host[HTTP_MAX_URI], /* Host portion of URI */
5927 resource[HTTP_MAX_URI]; /* Resource portion of URI */
5928 int port; /* Port portion of URI */
5929 const char *name; /* Printer name */
5930 printer_t *printer; /* Printer data */
5931 ipp_attribute_t *attr; /* printer-state-message text */
5932
5933
5934 LogMessage(L_DEBUG2, "reject_jobs(%p[%d], %s)\n", con, con->http.fd,
5935 uri->values[0].string.text);
5936
5937 /*
5938 * Was this operation called from the correct URI?
5939 */
5940
5941 if (strncmp(con->uri, "/admin/", 7) != 0)
5942 {
5943 LogMessage(L_ERROR, "reject_jobs: admin request on bad resource \'%s\'!",
5944 con->uri);
5945 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5946 return;
5947 }
5948
5949 /*
5950 * Is the destination valid?
5951 */
5952
5953 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5954
5955 if ((name = ValidateDest(host, resource, &dtype, &printer)) == NULL)
5956 {
5957 /*
5958 * Bad URI...
5959 */
5960
5961 LogMessage(L_ERROR, "reject_jobs: resource name \'%s\' no good!", resource);
5962 send_ipp_error(con, IPP_NOT_FOUND);
5963 return;
5964 }
5965
5966 /*
5967 * Check policy...
5968 */
5969
5970 if (!CheckPolicy(printer->op_policy_ptr, con, NULL))
5971 {
5972 LogMessage(L_ERROR, "reject_jobs: not authorized!");
5973 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5974 return;
5975 }
5976
5977 /*
5978 * Reject jobs sent to the printer...
5979 */
5980
5981 printer->accepting = 0;
5982
5983 if ((attr = ippFindAttribute(con->request, "printer-state-message",
5984 IPP_TAG_TEXT)) == NULL)
5985 strcpy(printer->state_message, "Rejecting Jobs");
5986 else
5987 strlcpy(printer->state_message, attr->values[0].string.text,
5988 sizeof(printer->state_message));
5989
5990 AddPrinterHistory(printer);
5991
5992 if (dtype & CUPS_PRINTER_CLASS)
5993 {
5994 SaveAllClasses();
5995
5996 LogMessage(L_INFO, "Class \'%s\' rejecting jobs (\'%s\').", name,
5997 con->username);
5998 }
5999 else
6000 {
6001 SaveAllPrinters();
6002
6003 LogMessage(L_INFO, "Printer \'%s\' rejecting jobs (\'%s\').", name,
6004 con->username);
6005 }
6006
6007 /*
6008 * Everything was ok, so return OK status...
6009 */
6010
6011 con->response->request.status.status_code = IPP_OK;
6012 }
6013
6014
6015 /*
6016 * 'release_job()' - Release a held print job.
6017 */
6018
6019 static void
6020 release_job(client_t *con, /* I - Client connection */
6021 ipp_attribute_t *uri) /* I - Job or Printer URI */
6022 {
6023 ipp_attribute_t *attr; /* Current attribute */
6024 int jobid; /* Job ID */
6025 char method[HTTP_MAX_URI], /* Method portion of URI */
6026 username[HTTP_MAX_URI], /* Username portion of URI */
6027 host[HTTP_MAX_URI], /* Host portion of URI */
6028 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6029 int port; /* Port portion of URI */
6030 job_t *job; /* Job information */
6031
6032
6033 LogMessage(L_DEBUG2, "release_job(%p[%d], %s)\n", con, con->http.fd,
6034 uri->values[0].string.text);
6035
6036 /*
6037 * Verify that the POST operation was done to a valid URI.
6038 */
6039
6040 if (strncmp(con->uri, "/classes/", 9) != 0 &&
6041 strncmp(con->uri, "/jobs/", 5) != 0 &&
6042 strncmp(con->uri, "/printers/", 10) != 0)
6043 {
6044 LogMessage(L_ERROR, "release_job: release request on bad resource \'%s\'!",
6045 con->uri);
6046 send_ipp_error(con, IPP_NOT_AUTHORIZED);
6047 return;
6048 }
6049
6050 /*
6051 * See if we have a job URI or a printer URI...
6052 */
6053
6054 if (strcmp(uri->name, "printer-uri") == 0)
6055 {
6056 /*
6057 * Got a printer URI; see if we also have a job-id attribute...
6058 */
6059
6060 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
6061 {
6062 LogMessage(L_ERROR, "release_job: got a printer-uri attribute but no job-id!");
6063 send_ipp_error(con, IPP_BAD_REQUEST);
6064 return;
6065 }
6066
6067 jobid = attr->values[0].integer;
6068 }
6069 else
6070 {
6071 /*
6072 * Got a job URI; parse it to get the job ID...
6073 */
6074
6075 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6076
6077 if (strncmp(resource, "/jobs/", 6) != 0)
6078 {
6079 /*
6080 * Not a valid URI!
6081 */
6082
6083 LogMessage(L_ERROR, "release_job: bad job-uri attribute \'%s\'!",
6084 uri->values[0].string.text);
6085 send_ipp_error(con, IPP_BAD_REQUEST);
6086 return;
6087 }
6088
6089 jobid = atoi(resource + 6);
6090 }
6091
6092 /*
6093 * See if the job exists...
6094 */
6095
6096 if ((job = FindJob(jobid)) == NULL)
6097 {
6098 /*
6099 * Nope - return a "not found" error...
6100 */
6101
6102 LogMessage(L_ERROR, "release_job: job #%d doesn't exist!", jobid);
6103 send_ipp_error(con, IPP_NOT_FOUND);
6104 return;
6105 }
6106
6107 /*
6108 * See if job is "held"...
6109 */
6110
6111 if (job->state->values[0].integer != IPP_JOB_HELD)
6112 {
6113 /*
6114 * Nope - return a "not possible" error...
6115 */
6116
6117 LogMessage(L_ERROR, "release_job: job #%d is not held!", jobid);
6118 send_ipp_error(con, IPP_NOT_POSSIBLE);
6119 return;
6120 }
6121
6122 /*
6123 * See if the job is owned by the requesting user...
6124 */
6125
6126 if (!validate_user(job, con, job->username, username, sizeof(username)))
6127 {
6128 LogMessage(L_ERROR, "release_job: \"%s\" not authorized to release job id %d owned by \"%s\"!",
6129 username, jobid, job->username);
6130 send_ipp_error(con, IPP_FORBIDDEN);
6131 return;
6132 }
6133
6134 /*
6135 * Reset the job-hold-until value to "no-hold"...
6136 */
6137
6138 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
6139 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
6140
6141 if (attr != NULL)
6142 {
6143 free(attr->values[0].string.text);
6144 attr->value_tag = IPP_TAG_KEYWORD;
6145 attr->values[0].string.text = strdup("no-hold");
6146 }
6147
6148 /*
6149 * Release the job and return...
6150 */
6151
6152 ReleaseJob(jobid);
6153
6154 LogMessage(L_INFO, "Job %d was released by \'%s\'.", jobid, username);
6155
6156 con->response->request.status.status_code = IPP_OK;
6157 }
6158
6159
6160 /*
6161 * 'renew_subscription()' - Renew an existing subscription...
6162 */
6163
6164 static void
6165 renew_subscription(client_t *con, /* I - Client connection */
6166 int sub_id) /* I - Subscription ID */
6167 {
6168 }
6169
6170
6171 /*
6172 * 'restart_job()' - Restart an old print job.
6173 */
6174
6175 static void
6176 restart_job(client_t *con, /* I - Client connection */
6177 ipp_attribute_t *uri) /* I - Job or Printer URI */
6178 {
6179 ipp_attribute_t *attr; /* Current attribute */
6180 int jobid; /* Job ID */
6181 char method[HTTP_MAX_URI], /* Method portion of URI */
6182 username[HTTP_MAX_URI], /* Username portion of URI */
6183 host[HTTP_MAX_URI], /* Host portion of URI */
6184 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6185 int port; /* Port portion of URI */
6186 job_t *job; /* Job information */
6187
6188
6189 LogMessage(L_DEBUG2, "restart_job(%p[%d], %s)\n", con, con->http.fd,
6190 uri->values[0].string.text);
6191
6192 /*
6193 * Verify that the POST operation was done to a valid URI.
6194 */
6195
6196 if (strncmp(con->uri, "/classes/", 9) != 0 &&
6197 strncmp(con->uri, "/jobs/", 5) != 0 &&
6198 strncmp(con->uri, "/printers/", 10) != 0)
6199 {
6200 LogMessage(L_ERROR, "restart_job: restart request on bad resource \'%s\'!",
6201 con->uri);
6202 send_ipp_error(con, IPP_NOT_AUTHORIZED);
6203 return;
6204 }
6205
6206 /*
6207 * See if we have a job URI or a printer URI...
6208 */
6209
6210 if (strcmp(uri->name, "printer-uri") == 0)
6211 {
6212 /*
6213 * Got a printer URI; see if we also have a job-id attribute...
6214 */
6215
6216 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
6217 {
6218 LogMessage(L_ERROR, "restart_job: got a printer-uri attribute but no job-id!");
6219 send_ipp_error(con, IPP_BAD_REQUEST);
6220 return;
6221 }
6222
6223 jobid = attr->values[0].integer;
6224 }
6225 else
6226 {
6227 /*
6228 * Got a job URI; parse it to get the job ID...
6229 */
6230
6231 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6232
6233 if (strncmp(resource, "/jobs/", 6) != 0)
6234 {
6235 /*
6236 * Not a valid URI!
6237 */
6238
6239 LogMessage(L_ERROR, "restart_job: bad job-uri attribute \'%s\'!",
6240 uri->values[0].string.text);
6241 send_ipp_error(con, IPP_BAD_REQUEST);
6242 return;
6243 }
6244
6245 jobid = atoi(resource + 6);
6246 }
6247
6248 /*
6249 * See if the job exists...
6250 */
6251
6252 if ((job = FindJob(jobid)) == NULL)
6253 {
6254 /*
6255 * Nope - return a "not found" error...
6256 */
6257
6258 LogMessage(L_ERROR, "restart_job: job #%d doesn't exist!", jobid);
6259 send_ipp_error(con, IPP_NOT_FOUND);
6260 return;
6261 }
6262
6263 /*
6264 * See if job is in any of the "completed" states...
6265 */
6266
6267 if (job->state->values[0].integer <= IPP_JOB_PROCESSING)
6268 {
6269 /*
6270 * Nope - return a "not possible" error...
6271 */
6272
6273 LogMessage(L_ERROR, "restart_job: job #%d is not complete!", jobid);
6274 send_ipp_error(con, IPP_NOT_POSSIBLE);
6275 return;
6276 }
6277
6278 /*
6279 * See if we have retained the job files...
6280 */
6281
6282 if (!JobFiles && job->state->values[0].integer > IPP_JOB_STOPPED)
6283 {
6284 /*
6285 * Nope - return a "not possible" error...
6286 */
6287
6288 LogMessage(L_ERROR, "restart_job: job #%d cannot be restarted - no files!", jobid);
6289 send_ipp_error(con, IPP_NOT_POSSIBLE);
6290 return;
6291 }
6292
6293 /*
6294 * See if the job is owned by the requesting user...
6295 */
6296
6297 if (!validate_user(job, con, job->username, username, sizeof(username)))
6298 {
6299 LogMessage(L_ERROR, "restart_job: \"%s\" not authorized to restart job id %d owned by \"%s\"!",
6300 username, jobid, job->username);
6301 send_ipp_error(con, IPP_FORBIDDEN);
6302 return;
6303 }
6304
6305 /*
6306 * Restart the job and return...
6307 */
6308
6309 RestartJob(jobid);
6310
6311 LogMessage(L_INFO, "Job %d was restarted by \'%s\'.", jobid, username);
6312
6313 con->response->request.status.status_code = IPP_OK;
6314 }
6315
6316
6317 /*
6318 * 'send_document()' - Send a file to a printer or class.
6319 */
6320
6321 static void
6322 send_document(client_t *con, /* I - Client connection */
6323 ipp_attribute_t *uri) /* I - Printer URI */
6324 {
6325 ipp_attribute_t *attr; /* Current attribute */
6326 ipp_attribute_t *format; /* Document-format attribute */
6327 int jobid; /* Job ID number */
6328 job_t *job; /* Current job */
6329 char job_uri[HTTP_MAX_URI],
6330 /* Job URI */
6331 method[HTTP_MAX_URI],
6332 /* Method portion of URI */
6333 username[HTTP_MAX_URI],
6334 /* Username portion of URI */
6335 host[HTTP_MAX_URI],
6336 /* Host portion of URI */
6337 resource[HTTP_MAX_URI];
6338 /* Resource portion of URI */
6339 int port; /* Port portion of URI */
6340 mime_type_t *filetype; /* Type of file */
6341 char super[MIME_MAX_SUPER],
6342 /* Supertype of file */
6343 type[MIME_MAX_TYPE],
6344 /* Subtype of file */
6345 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
6346 /* Textual name of mime type */
6347 char filename[1024]; /* Job filename */
6348 printer_t *printer; /* Current printer */
6349 struct stat fileinfo; /* File information */
6350 int kbytes; /* Size of file */
6351 int compression; /* Type of compression */
6352
6353
6354 LogMessage(L_DEBUG2, "send_document(%p[%d], %s)\n", con, con->http.fd,
6355 uri->values[0].string.text);
6356
6357 /*
6358 * Verify that the POST operation was done to a valid URI.
6359 */
6360
6361 if (strncmp(con->uri, "/classes/", 9) != 0 &&
6362 strncmp(con->uri, "/jobs/", 6) != 0 &&
6363 strncmp(con->uri, "/printers/", 10) != 0)
6364 {
6365 LogMessage(L_ERROR, "send_document: print request on bad resource \'%s\'!",
6366 con->uri);
6367 send_ipp_error(con, IPP_NOT_AUTHORIZED);
6368 return;
6369 }
6370
6371 /*
6372 * See if we have a job URI or a printer URI...
6373 */
6374
6375 if (strcmp(uri->name, "printer-uri") == 0)
6376 {
6377 /*
6378 * Got a printer URI; see if we also have a job-id attribute...
6379 */
6380
6381 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
6382 {
6383 LogMessage(L_ERROR, "send_document: got a printer-uri attribute but no job-id!");
6384 send_ipp_error(con, IPP_BAD_REQUEST);
6385 return;
6386 }
6387
6388 jobid = attr->values[0].integer;
6389 }
6390 else
6391 {
6392 /*
6393 * Got a job URI; parse it to get the job ID...
6394 */
6395
6396 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6397
6398 if (strncmp(resource, "/jobs/", 6) != 0)
6399 {
6400 /*
6401 * Not a valid URI!
6402 */
6403
6404 LogMessage(L_ERROR, "send_document: bad job-uri attribute \'%s\'!",
6405 uri->values[0].string.text);
6406 send_ipp_error(con, IPP_BAD_REQUEST);
6407 return;
6408 }
6409
6410 jobid = atoi(resource + 6);
6411 }
6412
6413 /*
6414 * See if the job exists...
6415 */
6416
6417 if ((job = FindJob(jobid)) == NULL)
6418 {
6419 /*
6420 * Nope - return a "not found" error...
6421 */
6422
6423 LogMessage(L_ERROR, "send_document: job #%d doesn't exist!", jobid);
6424 send_ipp_error(con, IPP_NOT_FOUND);
6425 return;
6426 }
6427
6428 /*
6429 * See if the job is owned by the requesting user...
6430 */
6431
6432 if (!validate_user(job, con, job->username, username, sizeof(username)))
6433 {
6434 LogMessage(L_ERROR, "send_document: \"%s\" not authorized to send document for job id %d owned by \"%s\"!",
6435 username, jobid, job->username);
6436 send_ipp_error(con, IPP_FORBIDDEN);
6437 return;
6438 }
6439
6440 /*
6441 * OK, see if the client is sending the document compressed - CUPS
6442 * only supports "none" and "gzip".
6443 */
6444
6445 compression = CUPS_FILE_NONE;
6446
6447 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL)
6448 {
6449 if (strcmp(attr->values[0].string.text, "none")
6450 #ifdef HAVE_LIBZ
6451 && strcmp(attr->values[0].string.text, "gzip")
6452 #endif /* HAVE_LIBZ */
6453 )
6454 {
6455 LogMessage(L_ERROR, "print_job: Unsupported compression \"%s\"!",
6456 attr->values[0].string.text);
6457 send_ipp_error(con, IPP_ATTRIBUTES);
6458 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
6459 "compression", NULL, attr->values[0].string.text);
6460 return;
6461 }
6462
6463 #ifdef HAVE_LIBZ
6464 if (!strcmp(attr->values[0].string.text, "gzip"))
6465 compression = CUPS_FILE_GZIP;
6466 #endif /* HAVE_LIBZ */
6467 }
6468
6469 /*
6470 * Do we have a file to print?
6471 */
6472
6473 if (!con->filename)
6474 {
6475 LogMessage(L_ERROR, "send_document: No file!?!");
6476 send_ipp_error(con, IPP_BAD_REQUEST);
6477 return;
6478 }
6479
6480 /*
6481 * Is it a format we support?
6482 */
6483
6484 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
6485 {
6486 /*
6487 * Grab format from client...
6488 */
6489
6490 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
6491 {
6492 LogMessage(L_ERROR, "send_document: could not scan type \'%s\'!",
6493 format->values[0].string.text);
6494 send_ipp_error(con, IPP_BAD_REQUEST);
6495 return;
6496 }
6497 }
6498 else
6499 {
6500 /*
6501 * No document format attribute? Auto-type it!
6502 */
6503
6504 strcpy(super, "application");
6505 strcpy(type, "octet-stream");
6506 }
6507
6508 if (strcmp(super, "application") == 0 &&
6509 strcmp(type, "octet-stream") == 0)
6510 {
6511 /*
6512 * Auto-type the file...
6513 */
6514
6515 LogMessage(L_DEBUG, "send_document: auto-typing file...");
6516
6517 filetype = mimeFileType(MimeDatabase, con->filename, &compression);
6518
6519 if (filetype != NULL)
6520 {
6521 /*
6522 * Replace the document-format attribute value with the auto-typed one.
6523 */
6524
6525 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
6526 filetype->type);
6527
6528 if (format != NULL)
6529 {
6530 free(format->values[0].string.text);
6531 format->values[0].string.text = strdup(mimetype);
6532 }
6533 else
6534 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
6535 "document-format", NULL, mimetype);
6536 }
6537 else
6538 filetype = mimeType(MimeDatabase, super, type);
6539 }
6540 else
6541 filetype = mimeType(MimeDatabase, super, type);
6542
6543 if (filetype == NULL)
6544 {
6545 LogMessage(L_ERROR, "send_document: Unsupported format \'%s/%s\'!",
6546 super, type);
6547 LogMessage(L_INFO, "Hint: Do you have the raw file printing rules enabled?");
6548 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
6549
6550 if (format)
6551 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
6552 "document-format", NULL, format->values[0].string.text);
6553
6554 return;
6555 }
6556
6557 LogMessage(L_DEBUG, "send_document: request file type is %s/%s.",
6558 filetype->super, filetype->type);
6559
6560 /*
6561 * Add the file to the job...
6562 */
6563
6564 if (add_file(con, job, filetype, compression))
6565 return;
6566
6567 if (job->dtype & CUPS_PRINTER_CLASS)
6568 printer = FindClass(job->dest);
6569 else
6570 printer = FindPrinter(job->dest);
6571
6572 if (stat(con->filename, &fileinfo))
6573 kbytes = 0;
6574 else
6575 kbytes = (fileinfo.st_size + 1023) / 1024;
6576
6577 UpdateQuota(printer, job->username, 0, kbytes);
6578
6579 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
6580 attr->values[0].integer += kbytes;
6581
6582 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
6583 job->num_files);
6584 rename(con->filename, filename);
6585
6586 ClearString(&con->filename);
6587
6588 LogMessage(L_INFO, "File of type %s/%s queued in job #%d by \'%s\'.",
6589 filetype->super, filetype->type, job->id, job->username);
6590
6591 /*
6592 * Start the job if this is the last document...
6593 */
6594
6595 if ((attr = ippFindAttribute(con->request, "last-document", IPP_TAG_BOOLEAN)) != NULL &&
6596 attr->values[0].boolean)
6597 {
6598 /*
6599 * See if we need to add the ending sheet...
6600 */
6601
6602 if (printer != NULL &&
6603 !(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
6604 (attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL &&
6605 attr->num_values > 1)
6606 {
6607 /*
6608 * Yes...
6609 */
6610
6611 LogMessage(L_INFO, "Adding end banner page \"%s\" to job %d.",
6612 attr->values[1].string.text, job->id);
6613
6614 kbytes = copy_banner(con, job, attr->values[1].string.text);
6615
6616 UpdateQuota(printer, job->username, 0, kbytes);
6617 }
6618
6619 if (job->state->values[0].integer == IPP_JOB_STOPPED)
6620 job->state->values[0].integer = IPP_JOB_PENDING;
6621 else if (job->state->values[0].integer == IPP_JOB_HELD)
6622 {
6623 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
6624 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
6625
6626 if (attr == NULL || strcmp(attr->values[0].string.text, "no-hold") == 0)
6627 job->state->values[0].integer = IPP_JOB_PENDING;
6628 }
6629
6630 SaveJob(job->id);
6631
6632 /*
6633 * Start the job if possible... Since CheckJobs() can cancel a job if it
6634 * doesn't print, we need to re-find the job afterwards...
6635 */
6636
6637 jobid = job->id;
6638
6639 CheckJobs();
6640
6641 job = FindJob(jobid);
6642 }
6643 else
6644 {
6645 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
6646 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
6647
6648 if (attr == NULL || strcmp(attr->values[0].string.text, "no-hold") == 0)
6649 {
6650 job->state->values[0].integer = IPP_JOB_HELD;
6651 job->hold_until = time(NULL) + 60;
6652 SaveJob(job->id);
6653 }
6654 }
6655
6656 /*
6657 * Fill in the response info...
6658 */
6659
6660 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
6661 LocalPort, jobid);
6662
6663 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
6664 job_uri);
6665
6666 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
6667
6668 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
6669 job ? job->state->values[0].integer : IPP_JOB_CANCELLED);
6670 add_job_state_reasons(con, job);
6671
6672 con->response->request.status.status_code = IPP_OK;
6673 }
6674
6675
6676 /*
6677 * 'send_ipp_error()' - Send an error status back to the IPP client.
6678 */
6679
6680 static void
6681 send_ipp_error(client_t *con, /* I - Client connection */
6682 ipp_status_t status) /* I - IPP status code */
6683 {
6684 LogMessage(L_DEBUG2, "send_ipp_error(%p[%d], %x)\n", con, con->http.fd,
6685 status);
6686
6687 LogMessage(L_DEBUG, "Sending error: %s", ippErrorString(status));
6688
6689 con->response->request.status.status_code = status;
6690
6691 if (ippFindAttribute(con->response, "attributes-charset", IPP_TAG_ZERO) == NULL)
6692 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
6693 "attributes-charset", NULL, DefaultCharset);
6694
6695 if (ippFindAttribute(con->response, "attributes-natural-language",
6696 IPP_TAG_ZERO) == NULL)
6697 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
6698 "attributes-natural-language", NULL, DefaultLanguage);
6699 }
6700
6701
6702 /*
6703 * 'set_default()' - Set the default destination...
6704 */
6705
6706 static void
6707 set_default(client_t *con, /* I - Client connection */
6708 ipp_attribute_t *uri) /* I - Printer URI */
6709 {
6710 cups_ptype_t dtype; /* Destination type (printer or class) */
6711 char method[HTTP_MAX_URI],
6712 /* Method portion of URI */
6713 username[HTTP_MAX_URI],
6714 /* Username portion of URI */
6715 host[HTTP_MAX_URI],
6716 /* Host portion of URI */
6717 resource[HTTP_MAX_URI];
6718 /* Resource portion of URI */
6719 int port; /* Port portion of URI */
6720 const char *name; /* Printer name */
6721 printer_t *printer; /* Printer */
6722
6723
6724 LogMessage(L_DEBUG2, "set_default(%p[%d], %s)\n", con, con->http.fd,
6725 uri->values[0].string.text);
6726
6727 /*
6728 * Was this operation called from the correct URI?
6729 */
6730
6731 if (strncmp(con->uri, "/admin/", 7) != 0)
6732 {
6733 LogMessage(L_ERROR, "set_default: admin request on bad resource \'%s\'!",
6734 con->uri);
6735 send_ipp_error(con, IPP_NOT_AUTHORIZED);
6736 return;
6737 }
6738
6739 /*
6740 * Is the destination valid?
6741 */
6742
6743 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6744
6745 if ((name = ValidateDest(host, resource, &dtype, &printer)) == NULL)
6746 {
6747 /*
6748 * Bad URI...
6749 */
6750
6751 LogMessage(L_ERROR, "set_default: resource name \'%s\' no good!", resource);
6752 send_ipp_error(con, IPP_NOT_FOUND);
6753 return;
6754 }
6755
6756 /*
6757 * Check policy...
6758 */
6759
6760 if (!CheckPolicy(DefaultPolicyPtr, con, NULL))
6761 {
6762 LogMessage(L_ERROR, "set_default: not authorized!");
6763 send_ipp_error(con, IPP_NOT_AUTHORIZED);
6764 return;
6765 }
6766
6767 /*
6768 * Set it as the default...
6769 */
6770
6771 DefaultPrinter = printer;
6772
6773 SaveAllPrinters();
6774 SaveAllClasses();
6775
6776 LogMessage(L_INFO, "Default destination set to \'%s\' by \'%s\'.", name,
6777 con->username);
6778
6779 /*
6780 * Everything was ok, so return OK status...
6781 */
6782
6783 con->response->request.status.status_code = IPP_OK;
6784 }
6785
6786
6787 /*
6788 * 'set_job_attrs()' - Set job attributes.
6789 */
6790
6791 static void
6792 set_job_attrs(client_t *con, /* I - Client connection */
6793 ipp_attribute_t *uri) /* I - Job URI */
6794 {
6795 ipp_attribute_t *attr, /* Current attribute */
6796 *attr2, /* Job attribute */
6797 *prev2; /* Previous job attribute */
6798 int jobid; /* Job ID */
6799 job_t *job; /* Current job */
6800 char method[HTTP_MAX_URI],
6801 /* Method portion of URI */
6802 username[HTTP_MAX_URI],
6803 /* Username portion of URI */
6804 host[HTTP_MAX_URI],
6805 /* Host portion of URI */
6806 resource[HTTP_MAX_URI];
6807 /* Resource portion of URI */
6808 int port; /* Port portion of URI */
6809
6810
6811 LogMessage(L_DEBUG2, "set_job_attrs(%p[%d], %s)\n", con, con->http.fd,
6812 uri->values[0].string.text);
6813
6814 /*
6815 * Start with "everything is OK" status...
6816 */
6817
6818 con->response->request.status.status_code = IPP_OK;
6819
6820 /*
6821 * See if we have a job URI or a printer URI...
6822 */
6823
6824 if (strcmp(uri->name, "printer-uri") == 0)
6825 {
6826 /*
6827 * Got a printer URI; see if we also have a job-id attribute...
6828 */
6829
6830 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
6831 {
6832 LogMessage(L_ERROR, "set_job_attrs: got a printer-uri attribute but no job-id!");
6833 send_ipp_error(con, IPP_BAD_REQUEST);
6834 return;
6835 }
6836
6837 jobid = attr->values[0].integer;
6838 }
6839 else
6840 {
6841 /*
6842 * Got a job URI; parse it to get the job ID...
6843 */
6844
6845 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6846
6847 if (strncmp(resource, "/jobs/", 6) != 0)
6848 {
6849 /*
6850 * Not a valid URI!
6851 */
6852
6853 LogMessage(L_ERROR, "set_job_attrs: bad job-uri attribute \'%s\'!\n",
6854 uri->values[0].string.text);
6855 send_ipp_error(con, IPP_BAD_REQUEST);
6856 return;
6857 }
6858
6859 jobid = atoi(resource + 6);
6860 }
6861
6862 /*
6863 * See if the job exists...
6864 */
6865
6866 if ((job = FindJob(jobid)) == NULL)
6867 {
6868 /*
6869 * Nope - return a "not found" error...
6870 */
6871
6872 LogMessage(L_ERROR, "set_job_attrs: job #%d doesn't exist!", jobid);
6873 send_ipp_error(con, IPP_NOT_FOUND);
6874 return;
6875 }
6876
6877 /*
6878 * See if the job has been completed...
6879 */
6880
6881 if (job->state->values[0].integer > IPP_JOB_STOPPED)
6882 {
6883 /*
6884 * Return a "not-possible" error...
6885 */
6886
6887 LogMessage(L_ERROR, "set_job_attrs: job #%d is finished and cannot be altered!", jobid);
6888 send_ipp_error(con, IPP_NOT_POSSIBLE);
6889 return;
6890 }
6891
6892 /*
6893 * See if the job is owned by the requesting user...
6894 */
6895
6896 if (!validate_user(job, con, job->username, username, sizeof(username)))
6897 {
6898 LogMessage(L_ERROR, "set_job_attrs: \"%s\" not authorized to alter job id %d owned by \"%s\"!",
6899 username, jobid, job->username);
6900 send_ipp_error(con, IPP_FORBIDDEN);
6901 return;
6902 }
6903
6904 /*
6905 * See what the user wants to change.
6906 */
6907
6908 for (attr = con->request->attrs; attr != NULL; attr = attr->next)
6909 {
6910 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
6911 continue;
6912
6913 if (!strcmp(attr->name, "attributes-charset") ||
6914 !strcmp(attr->name, "attributes-natural-language") ||
6915 !strcmp(attr->name, "document-compression") ||
6916 !strcmp(attr->name, "document-format") ||
6917 !strcmp(attr->name, "job-detailed-status-messages") ||
6918 !strcmp(attr->name, "job-document-access-errors") ||
6919 !strcmp(attr->name, "job-id") ||
6920 !strcmp(attr->name, "job-k-octets") ||
6921 !strcmp(attr->name, "job-originating-host-name") ||
6922 !strcmp(attr->name, "job-originating-user-name") ||
6923 !strcmp(attr->name, "job-printer-up-time") ||
6924 !strcmp(attr->name, "job-printer-uri") ||
6925 !strcmp(attr->name, "job-sheets") ||
6926 !strcmp(attr->name, "job-state-message") ||
6927 !strcmp(attr->name, "job-state-reasons") ||
6928 !strcmp(attr->name, "job-uri") ||
6929 !strcmp(attr->name, "number-of-documents") ||
6930 !strcmp(attr->name, "number-of-intervening-jobs") ||
6931 !strcmp(attr->name, "output-device-assigned") ||
6932 !strncmp(attr->name, "date-time-at-", 13) ||
6933 !strncmp(attr->name, "job-impressions", 15) ||
6934 !strncmp(attr->name, "job-k-octets", 12) ||
6935 !strncmp(attr->name, "job-media-sheets", 16) ||
6936 !strncmp(attr->name, "time-at-", 8))
6937 {
6938 /*
6939 * Read-only attrs!
6940 */
6941
6942 send_ipp_error(con, IPP_ATTRIBUTES_NOT_SETTABLE);
6943
6944 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
6945 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
6946
6947 continue;
6948 }
6949
6950 if (!strcmp(attr->name, "job-priority"))
6951 {
6952 /*
6953 * Change the job priority...
6954 */
6955
6956 if (attr->value_tag != IPP_TAG_INTEGER)
6957 {
6958 send_ipp_error(con, IPP_REQUEST_VALUE);
6959
6960 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
6961 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
6962 }
6963 else if (job->state->values[0].integer >= IPP_JOB_PROCESSING)
6964 {
6965 send_ipp_error(con, IPP_NOT_POSSIBLE);
6966 return;
6967 }
6968 else if (con->response->request.status.status_code == IPP_OK)
6969 SetJobPriority(jobid, attr->values[0].integer);
6970 }
6971 else if (!strcmp(attr->name, "job-state"))
6972 {
6973 /*
6974 * Change the job state...
6975 */
6976
6977 if (attr->value_tag != IPP_TAG_ENUM)
6978 {
6979 send_ipp_error(con, IPP_REQUEST_VALUE);
6980
6981 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
6982 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
6983 }
6984 else
6985 {
6986 switch (attr->values[0].integer)
6987 {
6988 case IPP_JOB_PENDING :
6989 case IPP_JOB_HELD :
6990 if (job->state->values[0].integer > IPP_JOB_HELD)
6991 {
6992 send_ipp_error(con, IPP_NOT_POSSIBLE);
6993 return;
6994 }
6995 else if (con->response->request.status.status_code == IPP_OK)
6996 job->state->values[0].integer = attr->values[0].integer;
6997 break;
6998
6999 case IPP_JOB_PROCESSING :
7000 case IPP_JOB_STOPPED :
7001 if (job->state->values[0].integer != attr->values[0].integer)
7002 {
7003 send_ipp_error(con, IPP_NOT_POSSIBLE);
7004 return;
7005 }
7006 break;
7007
7008 case IPP_JOB_CANCELLED :
7009 case IPP_JOB_ABORTED :
7010 case IPP_JOB_COMPLETED :
7011 if (job->state->values[0].integer > IPP_JOB_PROCESSING)
7012 {
7013 send_ipp_error(con, IPP_NOT_POSSIBLE);
7014 return;
7015 }
7016 else if (con->response->request.status.status_code == IPP_OK)
7017 {
7018 CancelJob(job->id, 0);
7019
7020 if (JobHistory)
7021 {
7022 job->state->values[0].integer = attr->values[0].integer;
7023 SaveJob(job->id);
7024 }
7025 }
7026 break;
7027 }
7028 }
7029 }
7030 else if (con->response->request.status.status_code != IPP_OK)
7031 continue;
7032 else if ((attr2 = ippFindAttribute(job->attrs, attr->name, IPP_TAG_ZERO)) != NULL)
7033 {
7034 /*
7035 * Some other value; first free the old value...
7036 */
7037
7038 if (job->attrs->attrs == attr2)
7039 {
7040 job->attrs->attrs = attr2->next;
7041 prev2 = NULL;
7042 }
7043 else
7044 {
7045 for (prev2 = job->attrs->attrs; prev2 != NULL; prev2 = prev2->next)
7046 if (prev2->next == attr2)
7047 {
7048 prev2->next = attr2->next;
7049 break;
7050 }
7051 }
7052
7053 if (job->attrs->last == attr2)
7054 job->attrs->last = prev2;
7055
7056 _ipp_free_attr(attr2);
7057
7058 /*
7059 * Then copy the attribute...
7060 */
7061
7062 copy_attribute(job->attrs, attr, 0);
7063
7064 /*
7065 * See if the job-name or job-hold-until is being changed.
7066 */
7067
7068 if (strcmp(attr->name, "job-hold-until") == 0)
7069 {
7070 SetJobHoldUntil(job->id, attr->values[0].string.text);
7071
7072 if (strcmp(attr->values[0].string.text, "no-hold") == 0)
7073 ReleaseJob(job->id);
7074 else
7075 HoldJob(job->id);
7076 }
7077 }
7078 else if (attr->value_tag == IPP_TAG_DELETEATTR)
7079 {
7080 /*
7081 * Delete the attribute...
7082 */
7083
7084 for (attr2 = job->attrs->attrs, prev2 = NULL;
7085 attr2 != NULL;
7086 prev2 = attr2, attr2 = attr2->next)
7087 if (attr2->name && strcmp(attr2->name, attr->name) == 0)
7088 break;
7089
7090 if (attr2)
7091 {
7092 if (prev2)
7093 prev2->next = attr2->next;
7094 else
7095 job->attrs->attrs = attr2->next;
7096
7097 if (attr2 == job->attrs->last)
7098 job->attrs->last = prev2;
7099
7100 _ipp_free_attr(attr2);
7101 }
7102 }
7103 else
7104 {
7105 /*
7106 * Add new option by copying it...
7107 */
7108
7109 copy_attribute(job->attrs, attr, 0);
7110 }
7111 }
7112
7113 /*
7114 * Save the job...
7115 */
7116
7117 SaveJob(job->id);
7118
7119 /*
7120 * Start jobs if possible...
7121 */
7122
7123 CheckJobs();
7124 }
7125
7126
7127 /*
7128 * 'start_printer()' - Start a printer.
7129 */
7130
7131 static void
7132 start_printer(client_t *con, /* I - Client connection */
7133 ipp_attribute_t *uri) /* I - Printer URI */
7134 {
7135 cups_ptype_t dtype; /* Destination type (printer or class) */
7136 char method[HTTP_MAX_URI],
7137 /* Method portion of URI */
7138 username[HTTP_MAX_URI],
7139 /* Username portion of URI */
7140 host[HTTP_MAX_URI],
7141 /* Host portion of URI */
7142 resource[HTTP_MAX_URI];
7143 /* Resource portion of URI */
7144 int port; /* Port portion of URI */
7145 const char *name; /* Printer name */
7146 printer_t *printer; /* Printer data */
7147
7148
7149 LogMessage(L_DEBUG2, "start_printer(%p[%d], %s)\n", con, con->http.fd,
7150 uri->values[0].string.text);
7151
7152 /*
7153 * Was this operation called from the correct URI?
7154 */
7155
7156 if (strncmp(con->uri, "/admin/", 7) != 0)
7157 {
7158 LogMessage(L_ERROR, "start_printer: admin request on bad resource \'%s\'!",
7159 con->uri);
7160 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7161 return;
7162 }
7163
7164 /*
7165 * Is the destination valid?
7166 */
7167
7168 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
7169
7170 if ((name = ValidateDest(host, resource, &dtype, &printer)) == NULL)
7171 {
7172 /*
7173 * Bad URI...
7174 */
7175
7176 LogMessage(L_ERROR, "start_printer: resource name \'%s\' no good!", resource);
7177 send_ipp_error(con, IPP_NOT_FOUND);
7178 return;
7179 }
7180
7181 /*
7182 * Check policy...
7183 */
7184
7185 if (!CheckPolicy(printer->op_policy_ptr, con, NULL))
7186 {
7187 LogMessage(L_ERROR, "start_printer: not authorized!");
7188 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7189 return;
7190 }
7191
7192 /*
7193 * Start the printer...
7194 */
7195
7196 printer->state_message[0] = '\0';
7197
7198 StartPrinter(printer, 1);
7199
7200 if (dtype & CUPS_PRINTER_CLASS)
7201 LogMessage(L_INFO, "Class \'%s\' started by \'%s\'.", name,
7202 con->username);
7203 LogMessage(L_INFO, "Printer \'%s\' started by \'%s\'.", name,
7204 con->username);
7205
7206 CheckJobs();
7207
7208 /*
7209 * Everything was ok, so return OK status...
7210 */
7211
7212 con->response->request.status.status_code = IPP_OK;
7213 }
7214
7215
7216 /*
7217 * 'stop_printer()' - Stop a printer.
7218 */
7219
7220 static void
7221 stop_printer(client_t *con, /* I - Client connection */
7222 ipp_attribute_t *uri) /* I - Printer URI */
7223 {
7224 cups_ptype_t dtype; /* Destination type (printer or class) */
7225 char method[HTTP_MAX_URI],
7226 /* Method portion of URI */
7227 username[HTTP_MAX_URI],
7228 /* Username portion of URI */
7229 host[HTTP_MAX_URI],
7230 /* Host portion of URI */
7231 resource[HTTP_MAX_URI];
7232 /* Resource portion of URI */
7233 int port; /* Port portion of URI */
7234 const char *name; /* Printer name */
7235 printer_t *printer; /* Printer data */
7236 ipp_attribute_t *attr; /* printer-state-message attribute */
7237
7238
7239 LogMessage(L_DEBUG2, "stop_printer(%p[%d], %s)\n", con, con->http.fd,
7240 uri->values[0].string.text);
7241
7242 /*
7243 * Was this operation called from the correct URI?
7244 */
7245
7246 if (strncmp(con->uri, "/admin/", 7) != 0)
7247 {
7248 LogMessage(L_ERROR, "stop_printer: admin request on bad resource \'%s\'!",
7249 con->uri);
7250 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7251 return;
7252 }
7253
7254 /*
7255 * Is the destination valid?
7256 */
7257
7258 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
7259
7260 if ((name = ValidateDest(host, resource, &dtype, &printer)) == NULL)
7261 {
7262 /*
7263 * Bad URI...
7264 */
7265
7266 LogMessage(L_ERROR, "stop_printer: resource name \'%s\' no good!", resource);
7267 send_ipp_error(con, IPP_NOT_FOUND);
7268 return;
7269 }
7270
7271 /*
7272 * Check policy...
7273 */
7274
7275 if (!CheckPolicy(printer->op_policy_ptr, con, NULL))
7276 {
7277 LogMessage(L_ERROR, "stop_printer: not authorized!");
7278 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7279 return;
7280 }
7281
7282 /*
7283 * Stop the printer...
7284 */
7285
7286 if ((attr = ippFindAttribute(con->request, "printer-state-message",
7287 IPP_TAG_TEXT)) == NULL)
7288 strcpy(printer->state_message, "Paused");
7289 else
7290 {
7291 strlcpy(printer->state_message, attr->values[0].string.text,
7292 sizeof(printer->state_message));
7293 }
7294
7295 StopPrinter(printer, 1);
7296
7297 if (dtype & CUPS_PRINTER_CLASS)
7298 LogMessage(L_INFO, "Class \'%s\' stopped by \'%s\'.", name,
7299 con->username);
7300 else
7301 LogMessage(L_INFO, "Printer \'%s\' stopped by \'%s\'.", name,
7302 con->username);
7303
7304 /*
7305 * Everything was ok, so return OK status...
7306 */
7307
7308 con->response->request.status.status_code = IPP_OK;
7309 }
7310
7311
7312 /*
7313 * 'user_allowed()' - See if a user is allowed to print to a queue.
7314 */
7315
7316 static int /* O - 0 if not allowed, 1 if allowed */
7317 user_allowed(printer_t *p, /* I - Printer or class */
7318 const char *username) /* I - Username */
7319 {
7320 int i, j; /* Looping vars */
7321 struct passwd *pw; /* User password data */
7322 struct group *grp; /* Group data */
7323
7324
7325 if (p->num_users == 0)
7326 return (1);
7327
7328 if (!strcmp(username, "root"))
7329 return (1);
7330
7331 pw = getpwnam(username);
7332 endpwent();
7333
7334 for (i = 0; i < p->num_users; i ++)
7335 {
7336 if (p->users[i][0] == '@')
7337 {
7338 /*
7339 * Check group membership...
7340 */
7341
7342 grp = getgrnam(p->users[i] + 1);
7343 endgrent();
7344
7345 if (grp)
7346 {
7347 /*
7348 * Check primary group...
7349 */
7350
7351 if (pw && grp->gr_gid == pw->pw_gid)
7352 break;
7353
7354 /*
7355 * Check usernames in group...
7356 */
7357
7358 for (j = 0; grp->gr_mem[j]; j ++)
7359 if (!strcasecmp(username, grp->gr_mem[j]))
7360 break;
7361
7362 if (grp->gr_mem[j])
7363 break;
7364 }
7365 }
7366 else if (!strcasecmp(username, p->users[i]))
7367 break;
7368 }
7369
7370 return ((i < p->num_users) != p->deny_users);
7371 }
7372
7373
7374 /*
7375 * 'validate_job()' - Validate printer options and destination.
7376 */
7377
7378 static void
7379 validate_job(client_t *con, /* I - Client connection */
7380 ipp_attribute_t *uri) /* I - Printer URI */
7381 {
7382 ipp_attribute_t *attr; /* Current attribute */
7383 ipp_attribute_t *format; /* Document-format attribute */
7384 cups_ptype_t dtype; /* Destination type (printer or class) */
7385 char method[HTTP_MAX_URI],
7386 /* Method portion of URI */
7387 username[HTTP_MAX_URI],
7388 /* Username portion of URI */
7389 host[HTTP_MAX_URI],
7390 /* Host portion of URI */
7391 resource[HTTP_MAX_URI];
7392 /* Resource portion of URI */
7393 int port; /* Port portion of URI */
7394 char super[MIME_MAX_SUPER],
7395 /* Supertype of file */
7396 type[MIME_MAX_TYPE];
7397 /* Subtype of file */
7398 printer_t *printer; /* Printer */
7399
7400
7401 LogMessage(L_DEBUG2, "validate_job(%p[%d], %s)\n", con, con->http.fd,
7402 uri->values[0].string.text);
7403
7404 /*
7405 * Verify that the POST operation was done to a valid URI.
7406 */
7407
7408 if (strncmp(con->uri, "/classes/", 9) != 0 &&
7409 strncmp(con->uri, "/printers/", 10) != 0)
7410 {
7411 LogMessage(L_ERROR, "validate_job: request on bad resource \'%s\'!",
7412 con->uri);
7413 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7414 return;
7415 }
7416
7417 /*
7418 * OK, see if the client is sending the document compressed - CUPS
7419 * doesn't support compression yet...
7420 */
7421
7422 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL &&
7423 strcmp(attr->values[0].string.text, "none") == 0)
7424 {
7425 LogMessage(L_ERROR, "validate_job: Unsupported compression attribute %s!",
7426 attr->values[0].string.text);
7427 send_ipp_error(con, IPP_ATTRIBUTES);
7428 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
7429 "compression", NULL, attr->values[0].string.text);
7430 return;
7431 }
7432
7433 /*
7434 * Is it a format we support?
7435 */
7436
7437 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
7438 {
7439 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
7440 {
7441 LogMessage(L_ERROR, "validate_job: could not scan type \'%s\'!\n",
7442 format->values[0].string.text);
7443 send_ipp_error(con, IPP_BAD_REQUEST);
7444 return;
7445 }
7446
7447 if ((strcmp(super, "application") != 0 ||
7448 strcmp(type, "octet-stream") != 0) &&
7449 mimeType(MimeDatabase, super, type) == NULL)
7450 {
7451 LogMessage(L_ERROR, "validate_job: Unsupported format \'%s\'!\n",
7452 format->values[0].string.text);
7453 LogMessage(L_INFO, "Hint: Do you have the raw file printing rules enabled?");
7454 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
7455 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
7456 "document-format", NULL, format->values[0].string.text);
7457 return;
7458 }
7459 }
7460
7461 /*
7462 * Is the destination valid?
7463 */
7464
7465 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
7466
7467 if (ValidateDest(host, resource, &dtype, &printer) == NULL)
7468 {
7469 /*
7470 * Bad URI...
7471 */
7472
7473 LogMessage(L_ERROR, "validate_job: resource name \'%s\' no good!", resource);
7474 send_ipp_error(con, IPP_NOT_FOUND);
7475 return;
7476 }
7477
7478 /*
7479 * Check policy...
7480 */
7481
7482 if (!CheckPolicy(printer->op_policy_ptr, con, NULL))
7483 {
7484 LogMessage(L_ERROR, "validate_job: not authorized!");
7485 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7486 return;
7487 }
7488
7489 /*
7490 * Everything was ok, so return OK status...
7491 */
7492
7493 con->response->request.status.status_code = IPP_OK;
7494 }
7495
7496
7497 /*
7498 * 'validate_name()' - Make sure the printer name only contains valid chars.
7499 */
7500
7501 static int /* O - 0 if name is no good, 1 if name is good */
7502 validate_name(const char *name) /* I - Name to check */
7503 {
7504 const char *ptr; /* Pointer into name */
7505
7506
7507 /*
7508 * Scan the whole name...
7509 */
7510
7511 for (ptr = name; *ptr; ptr ++)
7512 if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
7513 return (0);
7514
7515 /*
7516 * All the characters are good; validate the length, too...
7517 */
7518
7519 return ((ptr - name) < 128);
7520 }
7521
7522
7523 /*
7524 * 'validate_user()' - Validate the user for the request.
7525 */
7526
7527 static int /* O - 1 if permitted, 0 otherwise */
7528 validate_user(job_t *job, /* I - Job */
7529 client_t *con, /* I - Client connection */
7530 const char *owner, /* I - Owner of job/resource */
7531 char *username, /* O - Authenticated username */
7532 int userlen) /* I - Length of username */
7533 {
7534 ipp_attribute_t *attr; /* requesting-user-name attribute */
7535 printer_t *printer; /* Printer for job */
7536
7537
7538 LogMessage(L_DEBUG2, "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, userlen=%d)\n",
7539 job ? job->id : 0, con->http.fd, owner ? owner : "(null)",
7540 username, userlen);
7541
7542 /*
7543 * Validate input...
7544 */
7545
7546 if (!con || !owner || !username || userlen <= 0)
7547 return (0);
7548
7549 /*
7550 * Get the best authenticated username that is available.
7551 */
7552
7553 if (con->username[0])
7554 strlcpy(username, con->username, userlen);
7555 else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
7556 strlcpy(username, attr->values[0].string.text, userlen);
7557 else
7558 strlcpy(username, "anonymous", userlen);
7559
7560 /*
7561 * Check the username against the owner...
7562 */
7563
7564 if (job->dtype & CUPS_PRINTER_CLASS)
7565 printer = FindClass(job->dest);
7566 else
7567 printer = FindPrinter(job->dest);
7568
7569 if (printer)
7570 return (CheckPolicy(printer->op_policy_ptr, con, owner));
7571 else
7572 return (CheckPolicy(DefaultPolicyPtr, con, owner));
7573 }
7574
7575
7576 /*
7577 * End of "$Id$".
7578 */