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