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