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