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