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