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