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