]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/ipp.c
Import CUPS v1.7.2
[thirdparty/cups.git] / scheduler / ipp.c
CommitLineData
ef416fc2 1/*
71f63681 2 * "$Id: ipp.c 11734 2014-03-25 18:01:47Z msweet $"
ef416fc2 3 *
61515785 4 * IPP routines for the CUPS scheduler.
ef416fc2 5 *
61515785
MS
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
f7deaa1a 8 *
61515785
MS
9 * This file contains Kerberos support code, copyright 2006 by
10 * Jelmer Vernooij.
ef416fc2 11 *
61515785
MS
12 * These coded instructions, statements, and computer programs are the
13 * property of Apple Inc. and are protected by Federal copyright
14 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
15 * which should have been included with this file. If this file is
16 * file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 17 */
18
19/*
20 * Include necessary headers...
21 */
22
23#include "cupsd.h"
a603edef 24#include <cups/ppd-private.h>
ef416fc2 25
f899b121 26#ifdef __APPLE__
a29fd7dd 27/*# include <ApplicationServices/ApplicationServices.h>
229681c1 28extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
a29fd7dd 29# include <CoreFoundation/CoreFoundation.h>*/
f899b121 30# ifdef HAVE_MEMBERSHIP_H
31# include <membership.h>
32# endif /* HAVE_MEMBERSHIP_H */
33# ifdef HAVE_MEMBERSHIPPRIV_H
34# include <membershipPriv.h>
35# else
7594b224 36extern int mbr_user_name_to_uuid(const char* name, uuid_t uu);
37extern int mbr_group_name_to_uuid(const char* name, uuid_t uu);
38extern int mbr_check_membership_by_id(uuid_t user, gid_t group, int* ismember);
f899b121 39# endif /* HAVE_MEMBERSHIPPRIV_H */
40#endif /* __APPLE__ */
7594b224 41
ef416fc2 42
ef416fc2 43/*
44 * Local functions...
45 */
46
47static void accept_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
48static void add_class(cupsd_client_t *con, ipp_attribute_t *uri);
b423cd4c 49static int add_file(cupsd_client_t *con, cupsd_job_t *job,
50 mime_type_t *filetype, int compression);
f7deaa1a 51static cupsd_job_t *add_job(cupsd_client_t *con, cupsd_printer_t *printer,
80ca4592 52 mime_type_t *filetype);
ef416fc2 53static void add_job_subscriptions(cupsd_client_t *con, cupsd_job_t *job);
321d8d57 54static void add_job_uuid(cupsd_job_t *job);
ef416fc2 55static void add_printer(cupsd_client_t *con, ipp_attribute_t *uri);
b423cd4c 56static void add_printer_state_reasons(cupsd_client_t *con,
57 cupsd_printer_t *p);
ef416fc2 58static void add_queued_job_count(cupsd_client_t *con, cupsd_printer_t *p);
b423cd4c 59static void apply_printer_defaults(cupsd_printer_t *printer,
60 cupsd_job_t *job);
ef416fc2 61static void authenticate_job(cupsd_client_t *con, ipp_attribute_t *uri);
62static void cancel_all_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
63static void cancel_job(cupsd_client_t *con, ipp_attribute_t *uri);
64static void cancel_subscription(cupsd_client_t *con, int id);
3dfe78b3 65static int check_rss_recipient(const char *recipient);
ef416fc2 66static int check_quotas(cupsd_client_t *con, cupsd_printer_t *p);
aaf19ab0 67static void close_job(cupsd_client_t *con, ipp_attribute_t *uri);
fa73b229 68static void copy_attrs(ipp_t *to, ipp_t *from, cups_array_t *ra,
10d09e33
MS
69 ipp_tag_t group, int quickcopy,
70 cups_array_t *exclude);
b423cd4c 71static int copy_banner(cupsd_client_t *con, cupsd_job_t *job,
72 const char *name);
ef416fc2 73static int copy_file(const char *from, const char *to);
b423cd4c 74static int copy_model(cupsd_client_t *con, const char *from,
75 const char *to);
fa73b229 76static void copy_job_attrs(cupsd_client_t *con,
77 cupsd_job_t *job,
10d09e33 78 cups_array_t *ra, cups_array_t *exclude);
fa73b229 79static void copy_printer_attrs(cupsd_client_t *con,
80 cupsd_printer_t *printer,
81 cups_array_t *ra);
ef416fc2 82static void copy_subscription_attrs(cupsd_client_t *con,
83 cupsd_subscription_t *sub,
10d09e33
MS
84 cups_array_t *ra,
85 cups_array_t *exclude);
ef416fc2 86static void create_job(cupsd_client_t *con, ipp_attribute_t *uri);
87static cups_array_t *create_requested_array(ipp_t *request);
71f63681 88static void create_subscriptions(cupsd_client_t *con, ipp_attribute_t *uri);
ef416fc2 89static void delete_printer(cupsd_client_t *con, ipp_attribute_t *uri);
90static void get_default(cupsd_client_t *con);
91static void get_devices(cupsd_client_t *con);
2e4ff8af 92static void get_document(cupsd_client_t *con, ipp_attribute_t *uri);
ef416fc2 93static void get_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
94static void get_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
bd7854cb 95static void get_notifications(cupsd_client_t *con);
b94498cf 96static void get_ppd(cupsd_client_t *con, ipp_attribute_t *uri);
ef416fc2 97static void get_ppds(cupsd_client_t *con);
98static void get_printers(cupsd_client_t *con, int type);
99static void get_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
c168a833 100static void get_printer_supported(cupsd_client_t *con, ipp_attribute_t *uri);
ef416fc2 101static void get_subscription_attrs(cupsd_client_t *con, int sub_id);
102static void get_subscriptions(cupsd_client_t *con, ipp_attribute_t *uri);
e00b005a 103static const char *get_username(cupsd_client_t *con);
ef416fc2 104static void hold_job(cupsd_client_t *con, ipp_attribute_t *uri);
61cf44e2 105static void hold_new_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
ef416fc2 106static void move_job(cupsd_client_t *con, ipp_attribute_t *uri);
ef416fc2 107static int ppd_parse_line(const char *line, char *option, int olen,
108 char *choice, int clen);
109static void print_job(cupsd_client_t *con, ipp_attribute_t *uri);
c5571a1d 110static void read_job_ticket(cupsd_client_t *con);
ef416fc2 111static void reject_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
61cf44e2
MS
112static void release_held_new_jobs(cupsd_client_t *con,
113 ipp_attribute_t *uri);
ef416fc2 114static void release_job(cupsd_client_t *con, ipp_attribute_t *uri);
115static void renew_subscription(cupsd_client_t *con, int sub_id);
116static void restart_job(cupsd_client_t *con, ipp_attribute_t *uri);
f7deaa1a 117static void save_auth_info(cupsd_client_t *con, cupsd_job_t *job,
118 ipp_attribute_t *auth_info);
ef416fc2 119static void send_document(cupsd_client_t *con, ipp_attribute_t *uri);
f899b121 120static void send_http_error(cupsd_client_t *con, http_status_t status,
121 cupsd_printer_t *printer);
ef416fc2 122static void send_ipp_status(cupsd_client_t *con, ipp_status_t status,
123 const char *message, ...)
85dda01c 124 __attribute__((__format__(__printf__, 3, 4)));
ef416fc2 125static void set_default(cupsd_client_t *con, ipp_attribute_t *uri);
126static void set_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
c168a833 127static void set_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
b423cd4c 128static void set_printer_defaults(cupsd_client_t *con,
129 cupsd_printer_t *printer);
ef416fc2 130static void start_printer(cupsd_client_t *con, ipp_attribute_t *uri);
131static void stop_printer(cupsd_client_t *con, ipp_attribute_t *uri);
89d46774 132static void url_encode_attr(ipp_attribute_t *attr, char *buffer,
133 int bufsize);
b94498cf 134static char *url_encode_string(const char *s, char *buffer, int bufsize);
ef416fc2 135static int user_allowed(cupsd_printer_t *p, const char *username);
136static void validate_job(cupsd_client_t *con, ipp_attribute_t *uri);
137static int validate_name(const char *name);
138static int validate_user(cupsd_job_t *job, cupsd_client_t *con,
139 const char *owner, char *username,
140 int userlen);
141
142
143/*
09a101d6 144 * 'cupsdProcessIPPRequest()' - Process an incoming IPP request.
ef416fc2 145 */
146
147int /* O - 1 on success, 0 on failure */
148cupsdProcessIPPRequest(
149 cupsd_client_t *con) /* I - Client connection */
150{
151 ipp_tag_t group; /* Current group tag */
152 ipp_attribute_t *attr; /* Current attribute */
153 ipp_attribute_t *charset; /* Character set attribute */
154 ipp_attribute_t *language; /* Language attribute */
9f5eb9be 155 ipp_attribute_t *uri = NULL; /* Printer or job URI attribute */
ef416fc2 156 ipp_attribute_t *username; /* requesting-user-name attr */
157 int sub_id; /* Subscription ID */
158
159
160 cupsdLogMessage(CUPSD_LOG_DEBUG2,
161 "cupsdProcessIPPRequest(%p[%d]): operation_id = %04x",
162 con, con->http.fd, con->request->request.op.operation_id);
163
164 /*
165 * First build an empty response message for this request...
166 */
167
168 con->response = ippNew();
169
bc44d920 170 con->response->request.status.version[0] =
171 con->request->request.op.version[0];
172 con->response->request.status.version[1] =
173 con->request->request.op.version[1];
174 con->response->request.status.request_id =
175 con->request->request.op.request_id;
ef416fc2 176
177 /*
178 * Then validate the request header and required attributes...
179 */
f7deaa1a 180
c168a833
MS
181 if (con->request->request.any.version[0] != 1 &&
182 con->request->request.any.version[0] != 2)
ef416fc2 183 {
184 /*
c168a833 185 * Return an error, since we only support IPP 1.x and 2.x.
ef416fc2 186 */
187
188 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
189 "%04X %s Bad request version number %d.%d",
190 IPP_VERSION_NOT_SUPPORTED, con->http.hostname,
191 con->request->request.any.version[0],
192 con->request->request.any.version[1]);
193
194 send_ipp_status(con, IPP_VERSION_NOT_SUPPORTED,
84315f46 195 _("Bad request version number %d.%d."),
ef416fc2 196 con->request->request.any.version[0],
197 con->request->request.any.version[1]);
f7deaa1a 198 }
f0ab5bff
MS
199 else if (con->request->request.any.request_id < 1)
200 {
201 /*
202 * Return an error, since request IDs must be between 1 and 2^31-1
203 */
204
205 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
206 "%04X %s Bad request ID %d",
207 IPP_BAD_REQUEST, con->http.hostname,
208 con->request->request.any.request_id);
209
84315f46 210 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad request ID %d."),
f0ab5bff
MS
211 con->request->request.any.request_id);
212 }
fa73b229 213 else if (!con->request->attrs)
ef416fc2 214 {
215 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
216 "%04X %s No attributes in request",
217 IPP_BAD_REQUEST, con->http.hostname);
218
84315f46 219 send_ipp_status(con, IPP_BAD_REQUEST, _("No attributes in request."));
ef416fc2 220 }
221 else
222 {
223 /*
224 * Make sure that the attributes are provided in the correct order and
225 * don't repeat groups...
226 */
227
228 for (attr = con->request->attrs, group = attr->group_tag;
fa73b229 229 attr;
ef416fc2 230 attr = attr->next)
231 if (attr->group_tag < group && attr->group_tag != IPP_TAG_ZERO)
232 {
233 /*
234 * Out of order; return an error...
235 */
236
237 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
238 "%04X %s Attribute groups are out of order",
239 IPP_BAD_REQUEST, con->http.hostname);
240
241 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 242 _("Attribute groups are out of order (%x < %x)."),
ef416fc2 243 attr->group_tag, group);
244 break;
245 }
246 else
247 group = attr->group_tag;
248
fa73b229 249 if (!attr)
ef416fc2 250 {
251 /*
252 * Then make sure that the first three attributes are:
253 *
254 * attributes-charset
255 * attributes-natural-language
256 * printer-uri/job-uri
257 */
258
259 attr = con->request->attrs;
1340db2d
MS
260 if (attr && attr->name &&
261 !strcmp(attr->name, "attributes-charset") &&
fa73b229 262 (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_CHARSET)
ef416fc2 263 charset = attr;
264 else
265 charset = NULL;
266
267 if (attr)
268 attr = attr->next;
269
1340db2d
MS
270 if (attr && attr->name &&
271 !strcmp(attr->name, "attributes-natural-language") &&
fa73b229 272 (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_LANGUAGE)
1523f10d 273 {
ef416fc2 274 language = attr;
1523f10d
MS
275
276 /*
277 * Reset language for this request if different from Accept-Language.
278 */
279
280 if (!con->language ||
281 strcmp(attr->values[0].string.text, con->language->language))
282 {
283 cupsLangFree(con->language);
284 con->language = cupsLangGet(attr->values[0].string.text);
285 }
286 }
ef416fc2 287 else
288 language = NULL;
289
fa73b229 290 if ((attr = ippFindAttribute(con->request, "printer-uri",
291 IPP_TAG_URI)) != NULL)
ef416fc2 292 uri = attr;
fa73b229 293 else if ((attr = ippFindAttribute(con->request, "job-uri",
294 IPP_TAG_URI)) != NULL)
ef416fc2 295 uri = attr;
b94498cf 296 else if (con->request->request.op.operation_id == CUPS_GET_PPD)
297 uri = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME);
ef416fc2 298 else
299 uri = NULL;
300
301 if (charset)
302 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
bc44d920 303 "attributes-charset", NULL,
304 charset->values[0].string.text);
ef416fc2 305 else
306 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
f8b3a85b 307 "attributes-charset", NULL, "utf-8");
ef416fc2 308
309 if (language)
310 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
311 "attributes-natural-language", NULL,
312 language->values[0].string.text);
313 else
314 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
315 "attributes-natural-language", NULL, DefaultLanguage);
316
2e4ff8af 317 if (charset &&
88f9aafc
MS
318 _cups_strcasecmp(charset->values[0].string.text, "us-ascii") &&
319 _cups_strcasecmp(charset->values[0].string.text, "utf-8"))
2e4ff8af
MS
320 {
321 /*
322 * Bad character set...
323 */
324
4d301e69 325 cupsdLogMessage(CUPSD_LOG_ERROR, "Unsupported character set \"%s\"",
2e4ff8af
MS
326 charset->values[0].string.text);
327 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
328 "%04X %s Unsupported attributes-charset value \"%s\"",
329 IPP_CHARSET, con->http.hostname,
330 charset->values[0].string.text);
331 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 332 _("Unsupported character set \"%s\"."),
2e4ff8af
MS
333 charset->values[0].string.text);
334 }
335 else if (!charset || !language ||
336 (!uri &&
337 con->request->request.op.operation_id != CUPS_GET_DEFAULT &&
338 con->request->request.op.operation_id != CUPS_GET_PRINTERS &&
339 con->request->request.op.operation_id != CUPS_GET_CLASSES &&
340 con->request->request.op.operation_id != CUPS_GET_DEVICES &&
341 con->request->request.op.operation_id != CUPS_GET_PPDS))
ef416fc2 342 {
343 /*
344 * Return an error, since attributes-charset,
345 * attributes-natural-language, and printer-uri/job-uri are required
346 * for all operations.
347 */
348
349 if (!charset)
350 {
351 cupsdLogMessage(CUPSD_LOG_ERROR,
4d301e69 352 "Missing attributes-charset attribute");
ef416fc2 353
354 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
355 "%04X %s Missing attributes-charset attribute",
356 IPP_BAD_REQUEST, con->http.hostname);
357 }
358
359 if (!language)
360 {
361 cupsdLogMessage(CUPSD_LOG_ERROR,
4d301e69 362 "Missing attributes-natural-language attribute");
ef416fc2 363
364 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
365 "%04X %s Missing attributes-natural-language attribute",
366 IPP_BAD_REQUEST, con->http.hostname);
367 }
368
369 if (!uri)
370 {
371 cupsdLogMessage(CUPSD_LOG_ERROR,
b94498cf 372 "Missing printer-uri, job-uri, or ppd-name "
4d301e69 373 "attribute");
ef416fc2 374
375 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
b94498cf 376 "%04X %s Missing printer-uri, job-uri, or ppd-name "
377 "attribute", IPP_BAD_REQUEST, con->http.hostname);
ef416fc2 378 }
379
380 cupsdLogMessage(CUPSD_LOG_DEBUG, "Request attributes follow...");
381
fa73b229 382 for (attr = con->request->attrs; attr; attr = attr->next)
f7deaa1a 383 cupsdLogMessage(CUPSD_LOG_DEBUG,
ef416fc2 384 "attr \"%s\": group_tag = %x, value_tag = %x",
385 attr->name ? attr->name : "(null)", attr->group_tag,
386 attr->value_tag);
387
388 cupsdLogMessage(CUPSD_LOG_DEBUG, "End of attributes...");
389
390 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 391 _("Missing required attributes."));
ef416fc2 392 }
393 else
394 {
395 /*
396 * OK, all the checks pass so far; make sure requesting-user-name is
397 * not "root" from a remote host...
398 */
399
400 if ((username = ippFindAttribute(con->request, "requesting-user-name",
401 IPP_TAG_NAME)) != NULL)
402 {
403 /*
404 * Check for root user...
405 */
406
407 if (!strcmp(username->values[0].string.text, "root") &&
88f9aafc 408 _cups_strcasecmp(con->http.hostname, "localhost") &&
ef416fc2 409 strcmp(con->username, "root"))
410 {
411 /*
412 * Remote unauthenticated user masquerading as local root...
413 */
414
757d2cad 415 _cupsStrFree(username->values[0].string.text);
416 username->values[0].string.text = _cupsStrAlloc(RemoteRoot);
ef416fc2 417 }
418 }
419
420 if ((attr = ippFindAttribute(con->request, "notify-subscription-id",
421 IPP_TAG_INTEGER)) != NULL)
422 sub_id = attr->values[0].integer;
423 else
424 sub_id = 0;
425
426 /*
427 * Then try processing the operation...
428 */
429
430 if (uri)
bd7854cb 431 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s",
432 ippOpString(con->request->request.op.operation_id),
433 uri->values[0].string.text);
434 else
435 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s",
436 ippOpString(con->request->request.op.operation_id));
ef416fc2 437
438 switch (con->request->request.op.operation_id)
439 {
71f63681 440 case IPP_OP_PRINT_JOB :
ef416fc2 441 print_job(con, uri);
442 break;
443
71f63681 444 case IPP_OP_VALIDATE_JOB :
ef416fc2 445 validate_job(con, uri);
446 break;
447
71f63681 448 case IPP_OP_CREATE_JOB :
ef416fc2 449 create_job(con, uri);
450 break;
451
71f63681 452 case IPP_OP_SEND_DOCUMENT :
ef416fc2 453 send_document(con, uri);
454 break;
455
71f63681 456 case IPP_OP_CANCEL_JOB :
ef416fc2 457 cancel_job(con, uri);
458 break;
459
71f63681 460 case IPP_OP_GET_JOB_ATTRIBUTES :
ef416fc2 461 get_job_attrs(con, uri);
462 break;
463
71f63681 464 case IPP_OP_GET_JOBS :
ef416fc2 465 get_jobs(con, uri);
466 break;
467
71f63681 468 case IPP_OP_GET_PRINTER_ATTRIBUTES :
ef416fc2 469 get_printer_attrs(con, uri);
470 break;
471
71f63681 472 case IPP_OP_GET_PRINTER_SUPPORTED_VALUES :
c168a833
MS
473 get_printer_supported(con, uri);
474 break;
475
71f63681 476 case IPP_OP_HOLD_JOB :
ef416fc2 477 hold_job(con, uri);
478 break;
479
71f63681 480 case IPP_OP_RELEASE_JOB :
ef416fc2 481 release_job(con, uri);
482 break;
483
71f63681 484 case IPP_OP_RESTART_JOB :
ef416fc2 485 restart_job(con, uri);
486 break;
487
71f63681 488 case IPP_OP_PAUSE_PRINTER :
ef416fc2 489 stop_printer(con, uri);
490 break;
491
71f63681 492 case IPP_OP_RESUME_PRINTER :
ef416fc2 493 start_printer(con, uri);
494 break;
495
71f63681
MS
496 case IPP_OP_PURGE_JOBS :
497 case IPP_OP_CANCEL_JOBS :
498 case IPP_OP_CANCEL_MY_JOBS :
ef416fc2 499 cancel_all_jobs(con, uri);
500 break;
501
71f63681 502 case IPP_OP_SET_JOB_ATTRIBUTES :
ef416fc2 503 set_job_attrs(con, uri);
504 break;
505
71f63681 506 case IPP_OP_SET_PRINTER_ATTRIBUTES :
c168a833
MS
507 set_printer_attrs(con, uri);
508 break;
509
71f63681 510 case IPP_OP_HOLD_NEW_JOBS :
61cf44e2
MS
511 hold_new_jobs(con, uri);
512 break;
513
71f63681 514 case IPP_OP_RELEASE_HELD_NEW_JOBS :
61cf44e2
MS
515 release_held_new_jobs(con, uri);
516 break;
517
71f63681 518 case IPP_OP_CLOSE_JOB :
aaf19ab0
MS
519 close_job(con, uri);
520 break;
521
71f63681 522 case IPP_OP_CUPS_GET_DEFAULT :
ef416fc2 523 get_default(con);
524 break;
525
71f63681 526 case IPP_OP_CUPS_GET_PRINTERS :
ef416fc2 527 get_printers(con, 0);
528 break;
529
71f63681 530 case IPP_OP_CUPS_GET_CLASSES :
ef416fc2 531 get_printers(con, CUPS_PRINTER_CLASS);
532 break;
533
71f63681 534 case IPP_OP_CUPS_ADD_MODIFY_PRINTER :
ef416fc2 535 add_printer(con, uri);
536 break;
537
71f63681 538 case IPP_OP_CUPS_DELETE_PRINTER :
ef416fc2 539 delete_printer(con, uri);
540 break;
541
71f63681 542 case IPP_OP_CUPS_ADD_MODIFY_CLASS :
ef416fc2 543 add_class(con, uri);
544 break;
545
71f63681 546 case IPP_OP_CUPS_DELETE_CLASS :
ef416fc2 547 delete_printer(con, uri);
548 break;
549
71f63681
MS
550 case IPP_OP_CUPS_ACCEPT_JOBS :
551 case IPP_OP_ENABLE_PRINTER :
ef416fc2 552 accept_jobs(con, uri);
553 break;
554
71f63681
MS
555 case IPP_OP_CUPS_REJECT_JOBS :
556 case IPP_OP_DISABLE_PRINTER :
ef416fc2 557 reject_jobs(con, uri);
558 break;
559
71f63681 560 case IPP_OP_CUPS_SET_DEFAULT :
ef416fc2 561 set_default(con, uri);
562 break;
563
71f63681 564 case IPP_OP_CUPS_GET_DEVICES :
ef416fc2 565 get_devices(con);
566 break;
567
71f63681 568 case IPP_OP_CUPS_GET_DOCUMENT :
2e4ff8af
MS
569 get_document(con, uri);
570 break;
571
71f63681 572 case IPP_OP_CUPS_GET_PPD :
b94498cf 573 get_ppd(con, uri);
574 break;
575
71f63681 576 case IPP_OP_CUPS_GET_PPDS :
ef416fc2 577 get_ppds(con);
578 break;
579
71f63681 580 case IPP_OP_CUPS_MOVE_JOB :
ef416fc2 581 move_job(con, uri);
582 break;
583
71f63681 584 case IPP_OP_CUPS_AUTHENTICATE_JOB :
ef416fc2 585 authenticate_job(con, uri);
586 break;
587
71f63681
MS
588 case IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS :
589 case IPP_OP_CREATE_JOB_SUBSCRIPTIONS :
590 create_subscriptions(con, uri);
ef416fc2 591 break;
592
71f63681 593 case IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES :
ef416fc2 594 get_subscription_attrs(con, sub_id);
595 break;
596
71f63681 597 case IPP_OP_GET_SUBSCRIPTIONS :
ef416fc2 598 get_subscriptions(con, uri);
599 break;
600
71f63681 601 case IPP_OP_RENEW_SUBSCRIPTION :
ef416fc2 602 renew_subscription(con, sub_id);
603 break;
604
71f63681 605 case IPP_OP_CANCEL_SUBSCRIPTION :
ef416fc2 606 cancel_subscription(con, sub_id);
607 break;
608
71f63681 609 case IPP_OP_GET_NOTIFICATIONS :
bd7854cb 610 get_notifications(con);
ef416fc2 611 break;
612
613 default :
614 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
615 "%04X %s Operation %04X (%s) not supported",
616 IPP_OPERATION_NOT_SUPPORTED, con->http.hostname,
617 con->request->request.op.operation_id,
618 ippOpString(con->request->request.op.operation_id));
619
620 send_ipp_status(con, IPP_OPERATION_NOT_SUPPORTED,
84315f46 621 _("%s not supported."),
bc44d920 622 ippOpString(
623 con->request->request.op.operation_id));
ef416fc2 624 break;
625 }
626 }
627 }
628 }
629
630 if (con->response)
631 {
632 /*
633 * Sending data from the scheduler...
634 */
635
ee571f26 636 cupsdLogMessage(con->response->request.status.status_code
c5571a1d
MS
637 >= IPP_BAD_REQUEST &&
638 con->response->request.status.status_code
639 != IPP_NOT_FOUND ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
21f36711
MS
640 "[Client %d] Returning IPP %s for %s (%s) from %s",
641 con->http.fd,
ee571f26
MS
642 ippErrorString(con->response->request.status.status_code),
643 ippOpString(con->request->request.op.operation_id),
9f5eb9be 644 uri ? uri->values[0].string.text : "no URI",
ee571f26 645 con->http.hostname);
ef416fc2 646
5bd77a73 647 if (cupsdSendHeader(con, HTTP_OK, "application/ipp", CUPSD_AUTH_NONE))
ef416fc2 648 {
f301802f 649#ifdef CUPSD_USE_CHUNKING
650 /*
651 * Because older versions of CUPS (1.1.17 and older) and some IPP
d09495fa 652 * clients do not implement chunking properly, we cannot use
f301802f 653 * chunking by default. This may become the default in future
654 * CUPS releases, or we might add a configuration directive for
655 * it.
656 */
657
ef416fc2 658 if (con->http.version == HTTP_1_1)
659 {
21f36711
MS
660 cupsdLogMessage(CUPSD_LOG_DEBUG,
661 "[Client %d] Transfer-Encoding: chunked",
662 con->http.fd);
663
07725fee 664 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n") < 0)
665 return (0);
666
667 if (cupsdFlushHeader(con) < 0)
668 return (0);
d09495fa 669
670 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
ef416fc2 671 }
672 else
f301802f 673#endif /* CUPSD_USE_CHUNKING */
ef416fc2 674 {
d09495fa 675 size_t length; /* Length of response */
ef416fc2 676
d09495fa 677
678 length = ippLength(con->response);
ef416fc2 679
b94498cf 680 if (con->file >= 0 && !con->pipe_pid)
681 {
682 struct stat fileinfo; /* File information */
683
b94498cf 684 if (!fstat(con->file, &fileinfo))
685 length += fileinfo.st_size;
686 }
687
21f36711
MS
688 cupsdLogMessage(CUPSD_LOG_DEBUG,
689 "[Client %d] Content-Length: " CUPS_LLFMT,
690 con->http.fd, CUPS_LLCAST length);
07725fee 691 if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
692 CUPS_LLCAST length) < 0)
693 return (0);
694
695 if (cupsdFlushHeader(con) < 0)
696 return (0);
d09495fa 697
698 con->http.data_encoding = HTTP_ENCODE_LENGTH;
699 con->http.data_remaining = length;
b94498cf 700
701 if (con->http.data_remaining <= INT_MAX)
702 con->http._data_remaining = con->http.data_remaining;
703 else
704 con->http._data_remaining = INT_MAX;
ef416fc2 705 }
706
f7deaa1a 707 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
708 (cupsd_selfunc_t)cupsdWriteClient, con);
ef416fc2 709
710 /*
711 * Tell the caller the response header was sent successfully...
712 */
713
714 return (1);
715 }
716 else
717 {
718 /*
719 * Tell the caller the response header could not be sent...
720 */
721
722 return (0);
723 }
724 }
725 else
726 {
727 /*
728 * Sending data from a subprocess like cups-deviced; tell the caller
729 * everything is A-OK so far...
730 */
731
732 return (1);
733 }
734}
735
736
09a101d6 737/*
738 * 'cupsdTimeoutJob()' - Timeout a job waiting on job files.
739 */
740
91c84a35 741int /* O - 0 on success, -1 on error */
09a101d6 742cupsdTimeoutJob(cupsd_job_t *job) /* I - Job to timeout */
743{
744 cupsd_printer_t *printer; /* Destination printer or class */
745 ipp_attribute_t *attr; /* job-sheets attribute */
746 int kbytes; /* Kilobytes in banner */
747
748
749 job->pending_timeout = 0;
750
751 /*
752 * See if we need to add the ending sheet...
753 */
754
6d2f911b
MS
755 if (!cupsdLoadJob(job))
756 return (-1);
757
09a101d6 758 printer = cupsdFindDest(job->dest);
759 attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
760
a2326b5b 761 if (printer && !(printer->type & CUPS_PRINTER_REMOTE) &&
09a101d6 762 attr && attr->num_values > 1)
763 {
764 /*
765 * Yes...
766 */
767
75bd9771
MS
768 cupsdLogJob(job, CUPSD_LOG_INFO, "Adding end banner page \"%s\".",
769 attr->values[1].string.text);
09a101d6 770
91c84a35
MS
771 if ((kbytes = copy_banner(NULL, job, attr->values[1].string.text)) < 0)
772 return (-1);
09a101d6 773
774 cupsdUpdateQuota(printer, job->username, 0, kbytes);
775 }
91c84a35
MS
776
777 return (0);
09a101d6 778}
779
780
ef416fc2 781/*
782 * 'accept_jobs()' - Accept print jobs to a printer.
783 */
784
785static void
786accept_jobs(cupsd_client_t *con, /* I - Client connection */
787 ipp_attribute_t *uri) /* I - Printer or class URI */
788{
789 http_status_t status; /* Policy status */
bc44d920 790 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 791 cupsd_printer_t *printer; /* Printer data */
792
793
794 cupsdLogMessage(CUPSD_LOG_DEBUG2, "accept_jobs(%p[%d], %s)", con,
795 con->http.fd, uri->values[0].string.text);
796
797 /*
798 * Is the destination valid?
799 */
800
f7deaa1a 801 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 802 {
803 /*
804 * Bad URI...
805 */
806
807 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 808 _("The printer or class does not exist."));
ef416fc2 809 return;
810 }
811
812 /*
813 * Check policy...
814 */
815
816 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
817 {
f899b121 818 send_http_error(con, status, printer);
ef416fc2 819 return;
820 }
821
822 /*
823 * Accept jobs sent to the printer...
824 */
825
826 printer->accepting = 1;
827 printer->state_message[0] = '\0';
828
f8b3a85b
MS
829 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
830 "Now accepting jobs.");
831
ef416fc2 832 if (dtype & CUPS_PRINTER_CLASS)
f7deaa1a 833 {
3dfe78b3 834 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
f7deaa1a 835
836 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" now accepting jobs (\"%s\").",
837 printer->name, get_username(con));
838 }
ef416fc2 839 else
f7deaa1a 840 {
3dfe78b3 841 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 842
bc44d920 843 cupsdLogMessage(CUPSD_LOG_INFO,
844 "Printer \"%s\" now accepting jobs (\"%s\").",
f7deaa1a 845 printer->name, get_username(con));
846 }
ef416fc2 847
848 /*
849 * Everything was ok, so return OK status...
850 */
851
852 con->response->request.status.status_code = IPP_OK;
853}
854
855
856/*
857 * 'add_class()' - Add a class to the system.
858 */
859
860static void
861add_class(cupsd_client_t *con, /* I - Client connection */
862 ipp_attribute_t *uri) /* I - URI of class */
863{
864 http_status_t status; /* Policy status */
865 int i; /* Looping var */
61cf44e2 866 char scheme[HTTP_MAX_URI], /* Method portion of URI */
ef416fc2 867 username[HTTP_MAX_URI], /* Username portion of URI */
868 host[HTTP_MAX_URI], /* Host portion of URI */
869 resource[HTTP_MAX_URI]; /* Resource portion of URI */
870 int port; /* Port portion of URI */
871 cupsd_printer_t *pclass, /* Class */
872 *member; /* Member printer/class */
873 cups_ptype_t dtype; /* Destination type */
ef416fc2 874 ipp_attribute_t *attr; /* Printer attribute */
875 int modify; /* Non-zero if we just modified */
e00b005a 876 int need_restart_job; /* Need to restart job? */
ef416fc2 877
878
879 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_class(%p[%d], %s)", con,
880 con->http.fd, uri->values[0].string.text);
881
882 /*
883 * Do we have a valid URI?
884 */
885
61cf44e2
MS
886 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
887 sizeof(scheme), username, sizeof(username), host,
a4d04587 888 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 889
890
fa73b229 891 if (strncmp(resource, "/classes/", 9) || strlen(resource) == 9)
ef416fc2 892 {
893 /*
894 * No, return an error...
895 */
896
897 send_ipp_status(con, IPP_BAD_REQUEST,
898 _("The printer-uri must be of the form "
899 "\"ipp://HOSTNAME/classes/CLASSNAME\"."));
900 return;
901 }
902
903 /*
904 * Do we have a valid printer name?
905 */
906
907 if (!validate_name(resource + 9))
908 {
909 /*
910 * No, return an error...
911 */
912
913 send_ipp_status(con, IPP_BAD_REQUEST,
914 _("The printer-uri \"%s\" contains invalid characters."),
915 uri->values[0].string.text);
916 return;
917 }
918
ef416fc2 919 /*
920 * See if the class already exists; if not, create a new class...
921 */
922
923 if ((pclass = cupsdFindClass(resource + 9)) == NULL)
924 {
925 /*
926 * Class doesn't exist; see if we have a printer of the same name...
927 */
928
a2326b5b 929 if ((pclass = cupsdFindPrinter(resource + 9)) != NULL)
ef416fc2 930 {
931 /*
932 * Yes, return an error...
933 */
934
935 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 936 _("A printer named \"%s\" already exists."),
ef416fc2 937 resource + 9);
938 return;
939 }
940
941 /*
2e4ff8af 942 * No, check the default policy and then add the class...
ef416fc2 943 */
944
2e4ff8af
MS
945 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
946 {
947 send_http_error(con, status, NULL);
948 return;
949 }
950
ef416fc2 951 pclass = cupsdAddClass(resource + 9);
952 modify = 0;
953 }
2e4ff8af
MS
954 else if ((status = cupsdCheckPolicy(pclass->op_policy_ptr, con,
955 NULL)) != HTTP_OK)
956 {
2fb76298 957 send_http_error(con, status, pclass);
2e4ff8af
MS
958 return;
959 }
ef416fc2 960 else
961 modify = 1;
962
963 /*
964 * Look for attributes and copy them over as needed...
965 */
966
e00b005a 967 need_restart_job = 0;
968
fa73b229 969 if ((attr = ippFindAttribute(con->request, "printer-location",
970 IPP_TAG_TEXT)) != NULL)
ef416fc2 971 cupsdSetString(&pclass->location, attr->values[0].string.text);
972
fa73b229 973 if ((attr = ippFindAttribute(con->request, "printer-info",
974 IPP_TAG_TEXT)) != NULL)
ef416fc2 975 cupsdSetString(&pclass->info, attr->values[0].string.text);
976
fa73b229 977 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
f8b3a85b
MS
978 IPP_TAG_BOOLEAN)) != NULL &&
979 attr->values[0].boolean != pclass->accepting)
ef416fc2 980 {
bc44d920 981 cupsdLogMessage(CUPSD_LOG_INFO,
982 "Setting %s printer-is-accepting-jobs to %d (was %d.)",
983 pclass->name, attr->values[0].boolean, pclass->accepting);
ef416fc2 984
985 pclass->accepting = attr->values[0].boolean;
f8b3a85b
MS
986
987 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, pclass, NULL, "%s accepting jobs.",
988 pclass->accepting ? "Now" : "No longer");
ef416fc2 989 }
990
fa73b229 991 if ((attr = ippFindAttribute(con->request, "printer-is-shared",
992 IPP_TAG_BOOLEAN)) != NULL)
ef416fc2 993 {
994 if (pclass->shared && !attr->values[0].boolean)
f7deaa1a 995 cupsdDeregisterPrinter(pclass, 1);
ef416fc2 996
997 cupsdLogMessage(CUPSD_LOG_INFO,
998 "Setting %s printer-is-shared to %d (was %d.)",
999 pclass->name, attr->values[0].boolean, pclass->shared);
1000
1001 pclass->shared = attr->values[0].boolean;
1002 }
1003
fa73b229 1004 if ((attr = ippFindAttribute(con->request, "printer-state",
1005 IPP_TAG_ENUM)) != NULL)
ef416fc2 1006 {
1007 if (attr->values[0].integer != IPP_PRINTER_IDLE &&
1008 attr->values[0].integer != IPP_PRINTER_STOPPED)
1009 {
1010 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 1011 _("Attempt to set %s printer-state to bad value %d."),
ef416fc2 1012 pclass->name, attr->values[0].integer);
1013 return;
1014 }
1015
bc44d920 1016 cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)",
1017 pclass->name, attr->values[0].integer, pclass->state);
ef416fc2 1018
1019 if (attr->values[0].integer == IPP_PRINTER_STOPPED)
1020 cupsdStopPrinter(pclass, 0);
1021 else
e00b005a 1022 {
ef416fc2 1023 cupsdSetPrinterState(pclass, (ipp_pstate_t)(attr->values[0].integer), 0);
e00b005a 1024 need_restart_job = 1;
1025 }
ef416fc2 1026 }
fa73b229 1027 if ((attr = ippFindAttribute(con->request, "printer-state-message",
1028 IPP_TAG_TEXT)) != NULL)
ef416fc2 1029 {
1030 strlcpy(pclass->state_message, attr->values[0].string.text,
1031 sizeof(pclass->state_message));
f8b3a85b
MS
1032
1033 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, pclass, NULL, "%s",
1034 pclass->state_message);
ef416fc2 1035 }
fa73b229 1036 if ((attr = ippFindAttribute(con->request, "member-uris",
1037 IPP_TAG_URI)) != NULL)
ef416fc2 1038 {
1039 /*
1040 * Clear the printer array as needed...
1041 */
1042
e00b005a 1043 need_restart_job = 1;
1044
ef416fc2 1045 if (pclass->num_printers > 0)
1046 {
1047 free(pclass->printers);
1048 pclass->num_printers = 0;
1049 }
1050
1051 /*
1052 * Add each printer or class that is listed...
1053 */
1054
1055 for (i = 0; i < attr->num_values; i ++)
1056 {
1057 /*
1058 * Search for the printer or class URI...
1059 */
1060
f7deaa1a 1061 if (!cupsdValidateDest(attr->values[i].string.text, &dtype, &member))
ef416fc2 1062 {
1063 /*
1064 * Bad URI...
1065 */
1066
1067 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 1068 _("The printer or class does not exist."));
ef416fc2 1069 return;
1070 }
acb056cb
MS
1071 else if (dtype & CUPS_PRINTER_CLASS)
1072 {
1073 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 1074 _("Nested classes are not allowed."));
acb056cb
MS
1075 return;
1076 }
ef416fc2 1077
1078 /*
1079 * Add it to the class...
1080 */
1081
1082 cupsdAddPrinterToClass(pclass, member);
1083 }
1084 }
1085
b423cd4c 1086 set_printer_defaults(con, pclass);
1087
09a101d6 1088 if ((attr = ippFindAttribute(con->request, "auth-info-required",
1089 IPP_TAG_KEYWORD)) != NULL)
1090 cupsdSetAuthInfoRequired(pclass, NULL, attr);
1091
ef416fc2 1092 /*
1093 * Update the printer class attributes and return...
1094 */
1095
1096 cupsdSetPrinterAttrs(pclass);
3dfe78b3 1097 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
e00b005a 1098
1099 if (need_restart_job && pclass->job)
1100 {
e00b005a 1101 /*
b9faaae1 1102 * Reset the current job to a "pending" status...
e00b005a 1103 */
1104
b9faaae1
MS
1105 cupsdSetJobState(pclass->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
1106 "Job restarted because the class was modified.");
e00b005a 1107 }
1108
3dfe78b3 1109 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
ef416fc2 1110
1111 if (modify)
1112 {
c7017ecc 1113 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED,
f8b3a85b
MS
1114 pclass, NULL, "Class \"%s\" modified by \"%s\".",
1115 pclass->name, get_username(con));
ef416fc2 1116
1117 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" modified by \"%s\".",
e00b005a 1118 pclass->name, get_username(con));
ef416fc2 1119 }
1120 else
1121 {
c7017ecc 1122 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
f8b3a85b
MS
1123 pclass, NULL, "New class \"%s\" added by \"%s\".",
1124 pclass->name, get_username(con));
ef416fc2 1125
1126 cupsdLogMessage(CUPSD_LOG_INFO, "New class \"%s\" added by \"%s\".",
e00b005a 1127 pclass->name, get_username(con));
ef416fc2 1128 }
1129
1130 con->response->request.status.status_code = IPP_OK;
1131}
1132
1133
1134/*
1135 * 'add_file()' - Add a file to a job.
1136 */
1137
1138static int /* O - 0 on success, -1 on error */
1139add_file(cupsd_client_t *con, /* I - Connection to client */
1140 cupsd_job_t *job, /* I - Job to add to */
1141 mime_type_t *filetype, /* I - Type of file */
1142 int compression) /* I - Compression */
1143{
1144 mime_type_t **filetypes; /* New filetypes array... */
1145 int *compressions; /* New compressions array... */
1146
1147
1148 cupsdLogMessage(CUPSD_LOG_DEBUG2,
bc44d920 1149 "add_file(con=%p[%d], job=%d, filetype=%s/%s, "
1150 "compression=%d)", con, con ? con->http.fd : -1, job->id,
1151 filetype->super, filetype->type, compression);
ef416fc2 1152
1153 /*
1154 * Add the file to the job...
1155 */
1156
1157 if (job->num_files == 0)
1158 {
1159 compressions = (int *)malloc(sizeof(int));
1160 filetypes = (mime_type_t **)malloc(sizeof(mime_type_t *));
1161 }
1162 else
1163 {
1164 compressions = (int *)realloc(job->compressions,
1165 (job->num_files + 1) * sizeof(int));
1166 filetypes = (mime_type_t **)realloc(job->filetypes,
1167 (job->num_files + 1) *
1168 sizeof(mime_type_t *));
1169 }
1170
cb7f98ee
MS
1171 if (compressions)
1172 job->compressions = compressions;
1173
1174 if (filetypes)
1175 job->filetypes = filetypes;
1176
fa73b229 1177 if (!compressions || !filetypes)
ef416fc2 1178 {
b9faaae1
MS
1179 cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
1180 "Job aborted because the scheduler ran out of memory.");
ef416fc2 1181
09a101d6 1182 if (con)
1183 send_ipp_status(con, IPP_INTERNAL_ERROR,
84315f46 1184 _("Unable to allocate memory for file types."));
09a101d6 1185
ef416fc2 1186 return (-1);
1187 }
1188
ef416fc2 1189 job->compressions[job->num_files] = compression;
ef416fc2 1190 job->filetypes[job->num_files] = filetype;
1191
1192 job->num_files ++;
1193
3dfe78b3
MS
1194 job->dirty = 1;
1195 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
1196
ef416fc2 1197 return (0);
1198}
1199
1200
1201/*
b423cd4c 1202 * 'add_job()' - Add a job to a print queue.
ef416fc2 1203 */
1204
b423cd4c 1205static cupsd_job_t * /* O - Job object */
1206add_job(cupsd_client_t *con, /* I - Client connection */
f7deaa1a 1207 cupsd_printer_t *printer, /* I - Destination printer */
80ca4592 1208 mime_type_t *filetype) /* I - First print file type, if any */
ef416fc2 1209{
b423cd4c 1210 http_status_t status; /* Policy status */
f7deaa1a 1211 ipp_attribute_t *attr, /* Current attribute */
1212 *auth_info; /* auth-info attribute */
5a9febac 1213 const char *mandatory; /* Current mandatory job attribute */
b423cd4c 1214 const char *val; /* Default option value */
1215 int priority; /* Job priority */
b423cd4c 1216 cupsd_job_t *job; /* Current job */
f7deaa1a 1217 char job_uri[HTTP_MAX_URI]; /* Job URI */
b423cd4c 1218 int kbytes; /* Size of print file */
1219 int i; /* Looping var */
1220 int lowerpagerange; /* Page range bound */
54afec33
MS
1221 int exact; /* Did we have an exact match? */
1222 ipp_attribute_t *media_col, /* media-col attribute */
1223 *media_margin; /* media-*-margin attribute */
1224 ipp_t *unsup_col; /* media-col in unsupported response */
5a9febac
MS
1225 static const char * const readonly[] =/* List of read-only attributes */
1226 {
1227 "job-id",
cb7f98ee 1228 "job-k-octets-completed",
5a9febac 1229 "job-impressions-completed",
5a9febac
MS
1230 "job-media-sheets-completed",
1231 "job-state",
1232 "job-state-message",
1233 "job-state-reasons",
1234 "time-at-completed",
1235 "time-at-creation",
1236 "time-at-processing"
1237 };
ef416fc2 1238
1239
f7deaa1a 1240 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %p(%s), %p(%s/%s))",
1241 con, con->http.fd, printer, printer->name,
0a682745
MS
1242 filetype, filetype ? filetype->super : "none",
1243 filetype ? filetype->type : "none");
ef416fc2 1244
b423cd4c 1245 /*
1246 * Check remote printing to non-shared printer...
1247 */
ef416fc2 1248
b423cd4c 1249 if (!printer->shared &&
88f9aafc
MS
1250 _cups_strcasecmp(con->http.hostname, "localhost") &&
1251 _cups_strcasecmp(con->http.hostname, ServerName))
b423cd4c 1252 {
1253 send_ipp_status(con, IPP_NOT_AUTHORIZED,
84315f46 1254 _("The printer or class is not shared."));
b423cd4c 1255 return (NULL);
1256 }
ef416fc2 1257
b423cd4c 1258 /*
1259 * Check policy...
1260 */
ef416fc2 1261
f899b121 1262 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
7594b224 1263
b423cd4c 1264 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
1265 {
f899b121 1266 send_http_error(con, status, printer);
b423cd4c 1267 return (NULL);
1268 }
f11a948a
MS
1269 else if (printer->num_auth_info_required == 1 &&
1270 !strcmp(printer->auth_info_required[0], "negotiate") &&
1271 !con->username[0])
b423cd4c 1272 {
f899b121 1273 send_http_error(con, HTTP_UNAUTHORIZED, printer);
b423cd4c 1274 return (NULL);
1275 }
7594b224 1276#ifdef HAVE_SSL
1277 else if (auth_info && !con->http.tls &&
1278 !httpAddrLocalhost(con->http.hostaddr))
1279 {
1280 /*
1281 * Require encryption of auth-info over non-local connections...
1282 */
1283
f899b121 1284 send_http_error(con, HTTP_UPGRADE_REQUIRED, printer);
7594b224 1285 return (NULL);
1286 }
1287#endif /* HAVE_SSL */
ef416fc2 1288
b423cd4c 1289 /*
1290 * See if the printer is accepting jobs...
1291 */
1292
1293 if (!printer->accepting)
1294 {
1295 send_ipp_status(con, IPP_NOT_ACCEPTING,
1296 _("Destination \"%s\" is not accepting jobs."),
f7deaa1a 1297 printer->name);
b423cd4c 1298 return (NULL);
ef416fc2 1299 }
ef416fc2 1300
b423cd4c 1301 /*
80ca4592 1302 * Validate job template attributes; for now just document-format,
5a9febac
MS
1303 * copies, job-sheets, number-up, page-ranges, mandatory attributes, and
1304 * media...
b423cd4c 1305 */
ef416fc2 1306
5a9febac
MS
1307 for (i = 0; i < (int)(sizeof(readonly) / sizeof(readonly[0])); i ++)
1308 {
1309 if ((attr = ippFindAttribute(con->request, readonly[i],
1310 IPP_TAG_ZERO)) != NULL)
1311 {
1312 ippDeleteAttribute(con->request, attr);
1313
1314 if (StrictConformance)
1315 {
1316 send_ipp_status(con, IPP_BAD_REQUEST,
1317 _("The '%s' Job Description attribute cannot be "
1318 "supplied in a job creation request."), readonly[i]);
1319 return (NULL);
1320 }
1321
cb7f98ee 1322 cupsdLogMessage(CUPSD_LOG_INFO,
5a9febac
MS
1323 "Unexpected '%s' Job Description attribute in a job "
1324 "creation request.", readonly[i]);
1325 }
1326 }
1327
1328 if (printer->pc)
1329 {
1330 for (mandatory = (char *)cupsArrayFirst(printer->pc->mandatory);
1331 mandatory;
1332 mandatory = (char *)cupsArrayNext(printer->pc->mandatory))
1333 {
1334 if (!ippFindAttribute(con->request, mandatory, IPP_TAG_ZERO))
1335 {
1336 /*
1337 * Missing a required attribute...
1338 */
1339
1340 send_ipp_status(con, IPP_CONFLICT,
1341 _("The \"%s\" attribute is required for print jobs."),
1342 mandatory);
1343 return (NULL);
1344 }
1345 }
1346 }
1347
80ca4592 1348 if (filetype && printer->filetypes &&
1349 !cupsArrayFind(printer->filetypes, filetype))
1350 {
1351 char mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
1352 /* MIME media type string */
1353
1354
1355 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
1356 filetype->type);
1357
1358 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
84315f46 1359 _("Unsupported format \"%s\"."), mimetype);
80ca4592 1360
1361 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
1362 "document-format", NULL, mimetype);
1363
1364 return (NULL);
1365 }
1366
b423cd4c 1367 if ((attr = ippFindAttribute(con->request, "copies",
1368 IPP_TAG_INTEGER)) != NULL)
1369 {
1370 if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
1371 {
1372 send_ipp_status(con, IPP_ATTRIBUTES, _("Bad copies value %d."),
1373 attr->values[0].integer);
1374 ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
1375 "copies", attr->values[0].integer);
1376 return (NULL);
1377 }
1378 }
ef416fc2 1379
3dfe78b3
MS
1380 if ((attr = ippFindAttribute(con->request, "job-sheets",
1381 IPP_TAG_ZERO)) != NULL)
1382 {
1383 if (attr->value_tag != IPP_TAG_KEYWORD &&
1384 attr->value_tag != IPP_TAG_NAME)
1385 {
84315f46 1386 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value type."));
3dfe78b3
MS
1387 return (NULL);
1388 }
1389
1390 if (attr->num_values > 2)
1391 {
1392 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 1393 _("Too many job-sheets values (%d > 2)."),
3dfe78b3
MS
1394 attr->num_values);
1395 return (NULL);
1396 }
1397
1398 for (i = 0; i < attr->num_values; i ++)
1399 if (strcmp(attr->values[i].string.text, "none") &&
1400 !cupsdFindBanner(attr->values[i].string.text))
1401 {
84315f46 1402 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value \"%s\"."),
3dfe78b3
MS
1403 attr->values[i].string.text);
1404 return (NULL);
1405 }
1406 }
1407
f7deaa1a 1408 if ((attr = ippFindAttribute(con->request, "number-up",
1409 IPP_TAG_INTEGER)) != NULL)
1410 {
1411 if (attr->values[0].integer != 1 &&
1412 attr->values[0].integer != 2 &&
1413 attr->values[0].integer != 4 &&
1414 attr->values[0].integer != 6 &&
1415 attr->values[0].integer != 9 &&
1416 attr->values[0].integer != 16)
1417 {
1418 send_ipp_status(con, IPP_ATTRIBUTES, _("Bad number-up value %d."),
1419 attr->values[0].integer);
1420 ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
1421 "number-up", attr->values[0].integer);
1422 return (NULL);
1423 }
1424 }
1425
b423cd4c 1426 if ((attr = ippFindAttribute(con->request, "page-ranges",
1427 IPP_TAG_RANGE)) != NULL)
1428 {
1429 for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
1430 {
f7deaa1a 1431 if (attr->values[i].range.lower < lowerpagerange ||
b423cd4c 1432 attr->values[i].range.lower > attr->values[i].range.upper)
1433 {
1434 send_ipp_status(con, IPP_BAD_REQUEST,
1435 _("Bad page-ranges values %d-%d."),
1436 attr->values[i].range.lower,
1437 attr->values[i].range.upper);
1438 return (NULL);
1439 }
ef416fc2 1440
b423cd4c 1441 lowerpagerange = attr->values[i].range.upper + 1;
1442 }
1443 }
ef416fc2 1444
54afec33
MS
1445 /*
1446 * Do media selection as needed...
1447 */
1448
c7017ecc
MS
1449 if (!ippFindAttribute(con->request, "PageRegion", IPP_TAG_ZERO) &&
1450 !ippFindAttribute(con->request, "PageSize", IPP_TAG_ZERO) &&
f14324a7 1451 _ppdCacheGetPageSize(printer->pc, con->request, NULL, &exact))
54afec33 1452 {
54afec33
MS
1453 if (!exact &&
1454 (media_col = ippFindAttribute(con->request, "media-col",
1455 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1456 {
1457 send_ipp_status(con, IPP_OK_SUBST, _("Unsupported margins."));
1458
1459 unsup_col = ippNew();
54afec33
MS
1460 if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1461 "media-bottom-margin",
1462 IPP_TAG_INTEGER)) != NULL)
1463 ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1464 "media-bottom-margin", media_margin->values[0].integer);
1465
1466 if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1467 "media-left-margin",
1468 IPP_TAG_INTEGER)) != NULL)
1469 ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1470 "media-left-margin", media_margin->values[0].integer);
1471
1472 if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1473 "media-right-margin",
1474 IPP_TAG_INTEGER)) != NULL)
1475 ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1476 "media-right-margin", media_margin->values[0].integer);
1477
1478 if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1479 "media-top-margin",
1480 IPP_TAG_INTEGER)) != NULL)
1481 ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1482 "media-top-margin", media_margin->values[0].integer);
aaf19ab0
MS
1483
1484 ippAddCollection(con->response, IPP_TAG_UNSUPPORTED_GROUP, "media-col",
1485 unsup_col);
1486 ippDelete(unsup_col);
54afec33
MS
1487 }
1488 }
1489
ef416fc2 1490 /*
b423cd4c 1491 * Make sure we aren't over our limit...
ef416fc2 1492 */
1493
b423cd4c 1494 if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
1495 cupsdCleanJobs();
ef416fc2 1496
f7deaa1a 1497 if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
b423cd4c 1498 {
84315f46 1499 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Too many active jobs."));
b423cd4c 1500 return (NULL);
1501 }
1502
bc44d920 1503 if ((i = check_quotas(con, printer)) < 0)
b423cd4c 1504 {
1505 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
1506 return (NULL);
1507 }
bc44d920 1508 else if (i == 0)
1509 {
1510 send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print."));
1511 return (NULL);
1512 }
ef416fc2 1513
1514 /*
b423cd4c 1515 * Create the job and set things up...
ef416fc2 1516 */
1517
b423cd4c 1518 if ((attr = ippFindAttribute(con->request, "job-priority",
1519 IPP_TAG_INTEGER)) != NULL)
1520 priority = attr->values[0].integer;
1521 else
ef416fc2 1522 {
b423cd4c 1523 if ((val = cupsGetOption("job-priority", printer->num_options,
1524 printer->options)) != NULL)
1525 priority = atoi(val);
1526 else
1527 priority = 50;
ef416fc2 1528
b423cd4c 1529 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
1530 priority);
1531 }
ef416fc2 1532
cb7f98ee 1533 if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_ZERO)) == NULL)
b423cd4c 1534 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
41681883 1535 "Untitled");
cb7f98ee
MS
1536 else if ((attr->value_tag != IPP_TAG_NAME &&
1537 attr->value_tag != IPP_TAG_NAMELANG) ||
1538 attr->num_values != 1)
1539 {
1540 send_ipp_status(con, IPP_ATTRIBUTES,
1541 _("Bad job-name value: Wrong type or count."));
1542 if ((attr = ippCopyAttribute(con->response, attr, 0)) != NULL)
1543 attr->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
1544 return (NULL);
1545 }
1546 else if (!ippValidateAttribute(attr))
1547 {
1548 send_ipp_status(con, IPP_ATTRIBUTES, _("Bad job-name value: %s"),
1549 cupsLastErrorString());
1550 if ((attr = ippCopyAttribute(con->response, attr, 0)) != NULL)
1551 attr->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
1552 return (NULL);
1553 }
ef416fc2 1554
b423cd4c 1555 if ((job = cupsdAddJob(priority, printer->name)) == NULL)
1556 {
1557 send_ipp_status(con, IPP_INTERNAL_ERROR,
84315f46 1558 _("Unable to add job for destination \"%s\"."),
f7deaa1a 1559 printer->name);
b423cd4c 1560 return (NULL);
1561 }
ef416fc2 1562
a2326b5b 1563 job->dtype = printer->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE);
b423cd4c 1564 job->attrs = con->request;
3dfe78b3 1565 job->dirty = 1;
2abf387c 1566 con->request = ippNewRequest(job->attrs->request.op.operation_id);
ef416fc2 1567
3dfe78b3
MS
1568 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
1569
321d8d57 1570 add_job_uuid(job);
b423cd4c 1571 apply_printer_defaults(printer, job);
ef416fc2 1572
b423cd4c 1573 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
ef416fc2 1574
b423cd4c 1575 if (con->username[0])
1576 {
1577 cupsdSetString(&job->username, con->username);
ef416fc2 1578
1579 if (attr)
b423cd4c 1580 cupsdSetString(&attr->values[0].string.text, con->username);
ef416fc2 1581 }
b423cd4c 1582 else if (attr)
1583 {
1584 cupsdLogMessage(CUPSD_LOG_DEBUG,
1585 "add_job: requesting-user-name=\"%s\"",
1586 attr->values[0].string.text);
ef416fc2 1587
b423cd4c 1588 cupsdSetString(&job->username, attr->values[0].string.text);
1589 }
1590 else
1591 cupsdSetString(&job->username, "anonymous");
ef416fc2 1592
b423cd4c 1593 if (!attr)
1594 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
1595 "job-originating-user-name", NULL, job->username);
1596 else
1597 {
1598 attr->group_tag = IPP_TAG_JOB;
757d2cad 1599 _cupsStrFree(attr->name);
1600 attr->name = _cupsStrAlloc("job-originating-user-name");
b423cd4c 1601 }
ef416fc2 1602
f7deaa1a 1603 if (con->username[0] || auth_info)
1604 {
1605 save_auth_info(con, job, auth_info);
1606
1607 /*
1608 * Remove the auth-info attribute from the attribute data...
1609 */
1610
1611 if (auth_info)
a4924f6c 1612 ippDeleteAttribute(job->attrs, auth_info);
f7deaa1a 1613 }
1614
b423cd4c 1615 if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
1616 IPP_TAG_ZERO)) != NULL)
ef416fc2 1617 {
b423cd4c 1618 /*
1619 * Request contains a job-originating-host-name attribute; validate it...
1620 */
ef416fc2 1621
b423cd4c 1622 if (attr->value_tag != IPP_TAG_NAME ||
1623 attr->num_values != 1 ||
1624 strcmp(con->http.hostname, "localhost"))
ef416fc2 1625 {
1626 /*
b423cd4c 1627 * Can't override the value if we aren't connected via localhost.
1628 * Also, we can only have 1 value and it must be a name value.
ef416fc2 1629 */
1630
b423cd4c 1631 switch (attr->value_tag)
1632 {
1633 case IPP_TAG_STRING :
1634 case IPP_TAG_TEXTLANG :
1635 case IPP_TAG_NAMELANG :
1636 case IPP_TAG_TEXT :
1637 case IPP_TAG_NAME :
1638 case IPP_TAG_KEYWORD :
1639 case IPP_TAG_URI :
1640 case IPP_TAG_URISCHEME :
1641 case IPP_TAG_CHARSET :
1642 case IPP_TAG_LANGUAGE :
1643 case IPP_TAG_MIMETYPE :
1644 /*
1645 * Free old strings...
1646 */
ef416fc2 1647
b423cd4c 1648 for (i = 0; i < attr->num_values; i ++)
1649 {
757d2cad 1650 _cupsStrFree(attr->values[i].string.text);
b423cd4c 1651 attr->values[i].string.text = NULL;
a2326b5b 1652 if (attr->values[i].string.language)
b423cd4c 1653 {
a2326b5b
MS
1654 _cupsStrFree(attr->values[i].string.language);
1655 attr->values[i].string.language = NULL;
b423cd4c 1656 }
1657 }
ef416fc2 1658
b423cd4c 1659 default :
1660 break;
1661 }
ef416fc2 1662
b423cd4c 1663 /*
1664 * Use the default connection hostname instead...
1665 */
bd7854cb 1666
b423cd4c 1667 attr->value_tag = IPP_TAG_NAME;
1668 attr->num_values = 1;
757d2cad 1669 attr->values[0].string.text = _cupsStrAlloc(con->http.hostname);
b423cd4c 1670 }
bd7854cb 1671
b423cd4c 1672 attr->group_tag = IPP_TAG_JOB;
1673 }
1674 else
1675 {
1676 /*
1677 * No job-originating-host-name attribute, so use the hostname from
1678 * the connection...
1679 */
bd7854cb 1680
f7deaa1a 1681 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
b423cd4c 1682 "job-originating-host-name", NULL, con->http.hostname);
1683 }
bd7854cb 1684
b423cd4c 1685 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
1686 time(NULL));
1687 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1688 "time-at-processing", 0);
1689 attr->value_tag = IPP_TAG_NOVALUE;
1690 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1691 "time-at-completed", 0);
1692 attr->value_tag = IPP_TAG_NOVALUE;
bd7854cb 1693
1694 /*
b423cd4c 1695 * Add remaining job attributes...
bd7854cb 1696 */
1697
b423cd4c 1698 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1699 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
1700 "job-state", IPP_JOB_STOPPED);
d09495fa 1701 job->state_value = (ipp_jstate_t)job->state->values[0].integer;
12f89d24
MS
1702 job->reasons = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1703 "job-state-reasons", NULL, "job-incoming");
b423cd4c 1704 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1705 "job-media-sheets-completed", 0);
1706 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
1707 printer->uri);
bd7854cb 1708
b423cd4c 1709 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
1710 IPP_TAG_INTEGER)) != NULL)
1711 attr->values[0].integer = 0;
1712 else
1f0275e3 1713 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-k-octets", 0);
bd7854cb 1714
b423cd4c 1715 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
1716 IPP_TAG_KEYWORD)) == NULL)
1717 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
1718 if (!attr)
ef416fc2 1719 {
b423cd4c 1720 if ((val = cupsGetOption("job-hold-until", printer->num_options,
1721 printer->options)) == NULL)
1722 val = "no-hold";
ef416fc2 1723
b423cd4c 1724 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1725 "job-hold-until", NULL, val);
ef416fc2 1726 }
4a4b4f99 1727 if (attr && strcmp(attr->values[0].string.text, "no-hold"))
ef416fc2 1728 {
1729 /*
b423cd4c 1730 * Hold job until specified time...
ef416fc2 1731 */
1732
b9faaae1 1733 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
e53920b9 1734
1735 job->state->values[0].integer = IPP_JOB_HELD;
1736 job->state_value = IPP_JOB_HELD;
12f89d24
MS
1737
1738 ippSetString(job->attrs, &job->reasons, 0, "job-hold-until-specified");
ef416fc2 1739 }
b423cd4c 1740 else if (job->attrs->request.op.operation_id == IPP_CREATE_JOB)
ef416fc2 1741 {
dfd5680b 1742 job->hold_until = time(NULL) + MultipleOperationTimeout;
b423cd4c 1743 job->state->values[0].integer = IPP_JOB_HELD;
1744 job->state_value = IPP_JOB_HELD;
1745 }
1746 else
1747 {
1748 job->state->values[0].integer = IPP_JOB_PENDING;
1749 job->state_value = IPP_JOB_PENDING;
12f89d24
MS
1750
1751 ippSetString(job->attrs, &job->reasons, 0, "none");
ef416fc2 1752 }
1753
a2326b5b 1754 if (!(printer->type & CUPS_PRINTER_REMOTE) || Classification)
ef416fc2 1755 {
1756 /*
b423cd4c 1757 * Add job sheets options...
ef416fc2 1758 */
1759
b423cd4c 1760 if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1761 IPP_TAG_ZERO)) == NULL)
ef416fc2 1762 {
b423cd4c 1763 cupsdLogMessage(CUPSD_LOG_DEBUG,
1764 "Adding default job-sheets values \"%s,%s\"...",
1765 printer->job_sheets[0], printer->job_sheets[1]);
ef416fc2 1766
b423cd4c 1767 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
1768 2, NULL, NULL);
426c6a59
MS
1769 attr->values[0].string.text = _cupsStrRetain(printer->job_sheets[0]);
1770 attr->values[1].string.text = _cupsStrRetain(printer->job_sheets[1]);
ef416fc2 1771 }
1772
b423cd4c 1773 job->job_sheets = attr;
ef416fc2 1774
ef416fc2 1775 /*
b423cd4c 1776 * Enforce classification level if set...
ef416fc2 1777 */
1778
b423cd4c 1779 if (Classification)
ef416fc2 1780 {
b423cd4c 1781 cupsdLogMessage(CUPSD_LOG_INFO,
1782 "Classification=\"%s\", ClassifyOverride=%d",
1783 Classification ? Classification : "(null)",
1784 ClassifyOverride);
ef416fc2 1785
b423cd4c 1786 if (ClassifyOverride)
1787 {
1788 if (!strcmp(attr->values[0].string.text, "none") &&
1789 (attr->num_values == 1 ||
1790 !strcmp(attr->values[1].string.text, "none")))
1791 {
1792 /*
1793 * Force the leading banner to have the classification on it...
1794 */
ef416fc2 1795
b423cd4c 1796 cupsdSetString(&attr->values[0].string.text, Classification);
e00b005a 1797
75bd9771
MS
1798 cupsdLogJob(job, CUPSD_LOG_NOTICE, "CLASSIFICATION FORCED "
1799 "job-sheets=\"%s,none\", "
1800 "job-originating-user-name=\"%s\"",
1801 Classification, job->username);
b423cd4c 1802 }
1803 else if (attr->num_values == 2 &&
1804 strcmp(attr->values[0].string.text,
1805 attr->values[1].string.text) &&
1806 strcmp(attr->values[0].string.text, "none") &&
1807 strcmp(attr->values[1].string.text, "none"))
1808 {
1809 /*
1810 * Can't put two different security markings on the same document!
1811 */
ef416fc2 1812
b423cd4c 1813 cupsdSetString(&attr->values[1].string.text, attr->values[0].string.text);
ef416fc2 1814
75bd9771
MS
1815 cupsdLogJob(job, CUPSD_LOG_NOTICE, "CLASSIFICATION FORCED "
1816 "job-sheets=\"%s,%s\", "
1817 "job-originating-user-name=\"%s\"",
1818 attr->values[0].string.text,
1819 attr->values[1].string.text, job->username);
b423cd4c 1820 }
1821 else if (strcmp(attr->values[0].string.text, Classification) &&
1822 strcmp(attr->values[0].string.text, "none") &&
1823 (attr->num_values == 1 ||
1824 (strcmp(attr->values[1].string.text, Classification) &&
1825 strcmp(attr->values[1].string.text, "none"))))
1826 {
1827 if (attr->num_values == 1)
75bd9771
MS
1828 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1829 "CLASSIFICATION OVERRIDDEN "
1830 "job-sheets=\"%s\", "
1831 "job-originating-user-name=\"%s\"",
1832 attr->values[0].string.text, job->username);
b423cd4c 1833 else
75bd9771
MS
1834 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1835 "CLASSIFICATION OVERRIDDEN "
1836 "job-sheets=\"%s,%s\",fffff "
1837 "job-originating-user-name=\"%s\"",
1838 attr->values[0].string.text,
1839 attr->values[1].string.text, job->username);
b423cd4c 1840 }
1841 }
1842 else if (strcmp(attr->values[0].string.text, Classification) &&
1843 (attr->num_values == 1 ||
1844 strcmp(attr->values[1].string.text, Classification)))
ef416fc2 1845 {
1846 /*
b423cd4c 1847 * Force the banner to have the classification on it...
ef416fc2 1848 */
1849
b423cd4c 1850 if (attr->num_values > 1 &&
1851 !strcmp(attr->values[0].string.text, attr->values[1].string.text))
1852 {
1853 cupsdSetString(&(attr->values[0].string.text), Classification);
1854 cupsdSetString(&(attr->values[1].string.text), Classification);
1855 }
1856 else
1857 {
1858 if (attr->num_values == 1 ||
1859 strcmp(attr->values[0].string.text, "none"))
1860 cupsdSetString(&(attr->values[0].string.text), Classification);
ef416fc2 1861
b423cd4c 1862 if (attr->num_values > 1 &&
1863 strcmp(attr->values[1].string.text, "none"))
1864 cupsdSetString(&(attr->values[1].string.text), Classification);
1865 }
ef416fc2 1866
b423cd4c 1867 if (attr->num_values > 1)
75bd9771
MS
1868 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1869 "CLASSIFICATION FORCED "
1870 "job-sheets=\"%s,%s\", "
1871 "job-originating-user-name=\"%s\"",
1872 attr->values[0].string.text,
1873 attr->values[1].string.text, job->username);
b423cd4c 1874 else
75bd9771
MS
1875 cupsdLogJob(job, CUPSD_LOG_NOTICE,
1876 "CLASSIFICATION FORCED "
1877 "job-sheets=\"%s\", "
1878 "job-originating-user-name=\"%s\"",
1879 Classification, job->username);
ef416fc2 1880 }
1881 }
1882
b423cd4c 1883 /*
1884 * See if we need to add the starting sheet...
1885 */
ef416fc2 1886
a2326b5b 1887 if (!(printer->type & CUPS_PRINTER_REMOTE))
ef416fc2 1888 {
75bd9771
MS
1889 cupsdLogJob(job, CUPSD_LOG_INFO, "Adding start banner page \"%s\".",
1890 attr->values[0].string.text);
ef416fc2 1891
91c84a35 1892 if ((kbytes = copy_banner(con, job, attr->values[0].string.text)) < 0)
3dfe78b3 1893 {
b9faaae1
MS
1894 cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
1895 "Aborting job because the start banner could not be "
1896 "copied.");
91c84a35 1897 return (NULL);
3dfe78b3 1898 }
ef416fc2 1899
b423cd4c 1900 cupsdUpdateQuota(printer, job->username, 0, kbytes);
1901 }
ef416fc2 1902 }
b423cd4c 1903 else if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1904 IPP_TAG_ZERO)) != NULL)
c8fef167 1905 job->job_sheets = attr;
ef416fc2 1906
b423cd4c 1907 /*
1908 * Fill in the response info...
1909 */
ef416fc2 1910
83e08001 1911 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
f2534050 1912 con->clientname, con->clientport, "/jobs/%d", job->id);
b423cd4c 1913 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
1914 job_uri);
ef416fc2 1915
b423cd4c 1916 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
ef416fc2 1917
b423cd4c 1918 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
1919 job->state_value);
12f89d24
MS
1920 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons",
1921 NULL, job->reasons->values[0].string.text);
ef416fc2 1922
b423cd4c 1923 con->response->request.status.status_code = IPP_OK;
ef416fc2 1924
b423cd4c 1925 /*
1926 * Add any job subscriptions...
1927 */
ef416fc2 1928
b423cd4c 1929 add_job_subscriptions(con, job);
ef416fc2 1930
b423cd4c 1931 /*
1932 * Set all but the first two attributes to the job attributes group...
1933 */
ef416fc2 1934
b423cd4c 1935 for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
1936 attr->group_tag = IPP_TAG_JOB;
ef416fc2 1937
1938 /*
b423cd4c 1939 * Fire the "job created" event...
ef416fc2 1940 */
1941
b423cd4c 1942 cupsdAddEvent(CUPSD_EVENT_JOB_CREATED, printer, job, "Job created.");
ef416fc2 1943
1944 /*
b423cd4c 1945 * Return the new job...
ef416fc2 1946 */
1947
b423cd4c 1948 return (job);
1949}
ef416fc2 1950
ef416fc2 1951
ef416fc2 1952/*
9a4f8274 1953 * 'add_job_subscriptions()' - Add any subscriptions for a job.
ef416fc2 1954 */
1955
1956static void
b423cd4c 1957add_job_subscriptions(
1958 cupsd_client_t *con, /* I - Client connection */
1959 cupsd_job_t *job) /* I - Newly created job */
ef416fc2 1960{
b423cd4c 1961 int i; /* Looping var */
1962 ipp_attribute_t *prev, /* Previous attribute */
1963 *next, /* Next attribute */
1964 *attr; /* Current attribute */
1965 cupsd_subscription_t *sub; /* Subscription object */
1966 const char *recipient, /* notify-recipient-uri */
1967 *pullmethod; /* notify-pull-method */
1968 ipp_attribute_t *user_data; /* notify-user-data */
1969 int interval; /* notify-time-interval */
1970 unsigned mask; /* notify-events */
ef416fc2 1971
ef416fc2 1972
b423cd4c 1973 /*
1974 * Find the first subscription group attribute; return if we have
1975 * none...
1976 */
ef416fc2 1977
1f0275e3 1978 for (attr = job->attrs->attrs; attr; attr = attr->next)
b423cd4c 1979 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
1980 break;
ef416fc2 1981
b423cd4c 1982 if (!attr)
1983 return;
ef416fc2 1984
b423cd4c 1985 /*
1986 * Process the subscription attributes in the request...
1987 */
ef416fc2 1988
b423cd4c 1989 while (attr)
1990 {
1991 recipient = NULL;
1992 pullmethod = NULL;
1993 user_data = NULL;
1994 interval = 0;
1995 mask = CUPSD_EVENT_NONE;
ef416fc2 1996
b423cd4c 1997 while (attr && attr->group_tag != IPP_TAG_ZERO)
1998 {
f899b121 1999 if (!strcmp(attr->name, "notify-recipient-uri") &&
b423cd4c 2000 attr->value_tag == IPP_TAG_URI)
3dfe78b3
MS
2001 {
2002 /*
2003 * Validate the recipient scheme against the ServerBin/notifier
2004 * directory...
2005 */
2006
2007 char notifier[1024], /* Notifier filename */
2008 scheme[HTTP_MAX_URI], /* Scheme portion of URI */
2009 userpass[HTTP_MAX_URI], /* Username portion of URI */
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 */
2013
2014
b423cd4c 2015 recipient = attr->values[0].string.text;
3dfe78b3
MS
2016
2017 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
2018 scheme, sizeof(scheme), userpass, sizeof(userpass),
2019 host, sizeof(host), &port,
2020 resource, sizeof(resource)) < HTTP_URI_OK)
2021 {
2022 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 2023 _("Bad notify-recipient-uri \"%s\"."), recipient);
3dfe78b3
MS
2024 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2025 "notify-status-code", IPP_URI_SCHEME);
2026 return;
2027 }
2028
2029 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
2030 scheme);
2031 if (access(notifier, X_OK))
2032 {
2033 send_ipp_status(con, IPP_NOT_POSSIBLE,
2034 _("notify-recipient-uri URI \"%s\" uses unknown "
84315f46 2035 "scheme."), recipient);
3dfe78b3
MS
2036 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2037 "notify-status-code", IPP_URI_SCHEME);
2038 return;
2039 }
2040
2041 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
2042 {
2043 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 2044 _("notify-recipient-uri URI \"%s\" is already used."),
3dfe78b3
MS
2045 recipient);
2046 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2047 "notify-status-code", IPP_ATTRIBUTES);
2048 return;
2049 }
2050 }
b423cd4c 2051 else if (!strcmp(attr->name, "notify-pull-method") &&
2052 attr->value_tag == IPP_TAG_KEYWORD)
3dfe78b3 2053 {
b423cd4c 2054 pullmethod = attr->values[0].string.text;
3dfe78b3
MS
2055
2056 if (strcmp(pullmethod, "ippget"))
2057 {
2058 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 2059 _("Bad notify-pull-method \"%s\"."), pullmethod);
3dfe78b3
MS
2060 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
2061 "notify-status-code", IPP_ATTRIBUTES);
2062 return;
2063 }
2064 }
b423cd4c 2065 else if (!strcmp(attr->name, "notify-charset") &&
2066 attr->value_tag == IPP_TAG_CHARSET &&
2067 strcmp(attr->values[0].string.text, "us-ascii") &&
2068 strcmp(attr->values[0].string.text, "utf-8"))
2069 {
2070 send_ipp_status(con, IPP_CHARSET,
84315f46 2071 _("Character set \"%s\" not supported."),
b423cd4c 2072 attr->values[0].string.text);
2073 return;
2074 }
2075 else if (!strcmp(attr->name, "notify-natural-language") &&
2076 (attr->value_tag != IPP_TAG_LANGUAGE ||
2077 strcmp(attr->values[0].string.text, DefaultLanguage)))
2078 {
2079 send_ipp_status(con, IPP_CHARSET,
84315f46 2080 _("Language \"%s\" not supported."),
b423cd4c 2081 attr->values[0].string.text);
2082 return;
2083 }
2084 else if (!strcmp(attr->name, "notify-user-data") &&
2085 attr->value_tag == IPP_TAG_STRING)
2086 {
2087 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
2088 {
2089 send_ipp_status(con, IPP_REQUEST_VALUE,
2090 _("The notify-user-data value is too large "
84315f46 2091 "(%d > 63 octets)."),
b423cd4c 2092 attr->values[0].unknown.length);
2093 return;
2094 }
ef416fc2 2095
b423cd4c 2096 user_data = attr;
2097 }
2098 else if (!strcmp(attr->name, "notify-events") &&
2099 attr->value_tag == IPP_TAG_KEYWORD)
2100 {
2101 for (i = 0; i < attr->num_values; i ++)
2102 mask |= cupsdEventValue(attr->values[i].string.text);
2103 }
2104 else if (!strcmp(attr->name, "notify-lease-duration"))
2105 {
2106 send_ipp_status(con, IPP_BAD_REQUEST,
2107 _("The notify-lease-duration attribute cannot be "
2108 "used with job subscriptions."));
2109 return;
2110 }
2111 else if (!strcmp(attr->name, "notify-time-interval") &&
2112 attr->value_tag == IPP_TAG_INTEGER)
2113 interval = attr->values[0].integer;
ef416fc2 2114
b423cd4c 2115 attr = attr->next;
2116 }
ef416fc2 2117
b423cd4c 2118 if (!recipient && !pullmethod)
2119 break;
ef416fc2 2120
b423cd4c 2121 if (mask == CUPSD_EVENT_NONE)
2122 mask = CUPSD_EVENT_JOB_COMPLETED;
ef416fc2 2123
52f6f666
MS
2124 if ((sub = cupsdAddSubscription(mask, cupsdFindDest(job->dest), job,
2125 recipient, 0)) != NULL)
2126 {
2127 sub->interval = interval;
ef416fc2 2128
52f6f666 2129 cupsdSetString(&sub->owner, job->username);
ef416fc2 2130
52f6f666
MS
2131 if (user_data)
2132 {
2133 sub->user_data_len = user_data->values[0].unknown.length;
2134 memcpy(sub->user_data, user_data->values[0].unknown.data,
2135 sub->user_data_len);
2136 }
ef416fc2 2137
52f6f666
MS
2138 ippAddSeparator(con->response);
2139 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
2140 "notify-subscription-id", sub->id);
f8b3a85b
MS
2141
2142 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
2143 sub->id, job->id);
ef416fc2 2144 }
2145
b423cd4c 2146 if (attr)
2147 attr = attr->next;
ef416fc2 2148 }
2149
3dfe78b3 2150 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
b423cd4c 2151
ef416fc2 2152 /*
b423cd4c 2153 * Remove all of the subscription attributes from the job request...
1f0275e3
MS
2154 *
2155 * TODO: Optimize this since subscription groups have to come at the
2156 * end of the request...
ef416fc2 2157 */
2158
b423cd4c 2159 for (attr = job->attrs->attrs, prev = NULL; attr; attr = next)
ef416fc2 2160 {
b423cd4c 2161 next = attr->next;
ef416fc2 2162
b423cd4c 2163 if (attr->group_tag == IPP_TAG_SUBSCRIPTION ||
2164 attr->group_tag == IPP_TAG_ZERO)
2165 {
2166 /*
2167 * Free and remove this attribute...
2168 */
ef416fc2 2169
a2326b5b 2170 ippDeleteAttribute(NULL, attr);
ef416fc2 2171
b423cd4c 2172 if (prev)
2173 prev->next = next;
2174 else
2175 job->attrs->attrs = next;
2176 }
2177 else
2178 prev = attr;
ef416fc2 2179 }
2180
b423cd4c 2181 job->attrs->last = prev;
2182 job->attrs->current = prev;
2183}
ef416fc2 2184
ef416fc2 2185
b423cd4c 2186/*
2187 * 'add_job_uuid()' - Add job-uuid attribute to a job.
2188 *
2189 * See RFC 4122 for the definition of UUIDs and the format.
2190 */
2191
2192static void
321d8d57 2193add_job_uuid(cupsd_job_t *job) /* I - Job */
b423cd4c 2194{
82f97232 2195 char uuid[64]; /* job-uuid string */
ef416fc2 2196
ef416fc2 2197
2198 /*
82f97232 2199 * Add a job-uuid attribute if none exists...
ef416fc2 2200 */
2201
82f97232
MS
2202 if (!ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI))
2203 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL,
07ed0e9a
MS
2204 _httpAssembleUUID(ServerName, RemotePort, job->dest, job->id,
2205 uuid, sizeof(uuid)));
ef416fc2 2206}
2207
2208
2209/*
b423cd4c 2210 * 'add_printer()' - Add a printer to the system.
ef416fc2 2211 */
2212
2213static void
b423cd4c 2214add_printer(cupsd_client_t *con, /* I - Client connection */
2215 ipp_attribute_t *uri) /* I - URI of printer */
ef416fc2 2216{
2217 http_status_t status; /* Policy status */
b423cd4c 2218 int i; /* Looping var */
09a101d6 2219 char scheme[HTTP_MAX_URI], /* Method portion of URI */
b423cd4c 2220 username[HTTP_MAX_URI], /* Username portion of URI */
ef416fc2 2221 host[HTTP_MAX_URI], /* Host portion of URI */
2222 resource[HTTP_MAX_URI]; /* Resource portion of URI */
2223 int port; /* Port portion of URI */
b423cd4c 2224 cupsd_printer_t *printer; /* Printer/class */
2225 ipp_attribute_t *attr; /* Printer attribute */
2226 cups_file_t *fp; /* Script/PPD file */
2227 char line[1024]; /* Line from file... */
2228 char srcfile[1024], /* Source Script/PPD file */
2229 dstfile[1024]; /* Destination Script/PPD file */
2230 int modify; /* Non-zero if we are modifying */
b226ab99
MS
2231 int changed_driver, /* Changed the PPD/interface script? */
2232 need_restart_job, /* Need to restart job? */
2233 set_device_uri, /* Did we set the device URI? */
09a101d6 2234 set_port_monitor; /* Did we set the port monitor? */
ef416fc2 2235
2236
b423cd4c 2237 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer(%p[%d], %s)", con,
ef416fc2 2238 con->http.fd, uri->values[0].string.text);
2239
2240 /*
b423cd4c 2241 * Do we have a valid URI?
ef416fc2 2242 */
2243
09a101d6 2244 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
2245 sizeof(scheme), username, sizeof(username), host,
b423cd4c 2246 sizeof(host), &port, resource, sizeof(resource));
2247
2248 if (strncmp(resource, "/printers/", 10) || strlen(resource) == 10)
ef416fc2 2249 {
b423cd4c 2250 /*
2251 * No, return an error...
2252 */
2253
ef416fc2 2254 send_ipp_status(con, IPP_BAD_REQUEST,
b423cd4c 2255 _("The printer-uri must be of the form "
2256 "\"ipp://HOSTNAME/printers/PRINTERNAME\"."));
ef416fc2 2257 return;
2258 }
2259
2260 /*
b423cd4c 2261 * Do we have a valid printer name?
ef416fc2 2262 */
2263
b423cd4c 2264 if (!validate_name(resource + 10))
ef416fc2 2265 {
b423cd4c 2266 /*
2267 * No, return an error...
2268 */
2269
2270 send_ipp_status(con, IPP_BAD_REQUEST,
2271 _("The printer-uri \"%s\" contains invalid characters."),
2272 uri->values[0].string.text);
2273 return;
ef416fc2 2274 }
ef416fc2 2275
ef416fc2 2276 /*
b423cd4c 2277 * See if the printer already exists; if not, create a new printer...
2278 */
ef416fc2 2279
b423cd4c 2280 if ((printer = cupsdFindPrinter(resource + 10)) == NULL)
ef416fc2 2281 {
2282 /*
b423cd4c 2283 * Printer doesn't exist; see if we have a class of the same name...
ef416fc2 2284 */
2285
a2326b5b 2286 if ((printer = cupsdFindClass(resource + 10)) != NULL)
ef416fc2 2287 {
b423cd4c 2288 /*
2289 * Yes, return an error...
2290 */
ef416fc2 2291
b423cd4c 2292 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 2293 _("A class named \"%s\" already exists."),
b423cd4c 2294 resource + 10);
ef416fc2 2295 return;
2296 }
2297
2298 /*
2e4ff8af 2299 * No, check the default policy then add the printer...
ef416fc2 2300 */
2301
2e4ff8af
MS
2302 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
2303 {
2304 send_http_error(con, status, NULL);
2305 return;
2306 }
2307
b423cd4c 2308 printer = cupsdAddPrinter(resource + 10);
2309 modify = 0;
ef416fc2 2310 }
2e4ff8af
MS
2311 else if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
2312 NULL)) != HTTP_OK)
2313 {
2fb76298 2314 send_http_error(con, status, printer);
2e4ff8af
MS
2315 return;
2316 }
b423cd4c 2317 else
2318 modify = 1;
ef416fc2 2319
b423cd4c 2320 /*
2321 * Look for attributes and copy them over as needed...
2322 */
ef416fc2 2323
b226ab99 2324 changed_driver = 0;
b423cd4c 2325 need_restart_job = 0;
ef416fc2 2326
b423cd4c 2327 if ((attr = ippFindAttribute(con->request, "printer-location",
2328 IPP_TAG_TEXT)) != NULL)
2329 cupsdSetString(&printer->location, attr->values[0].string.text);
ef416fc2 2330
b423cd4c 2331 if ((attr = ippFindAttribute(con->request, "printer-info",
2332 IPP_TAG_TEXT)) != NULL)
2333 cupsdSetString(&printer->info, attr->values[0].string.text);
ef416fc2 2334
09a101d6 2335 set_device_uri = 0;
2336
b423cd4c 2337 if ((attr = ippFindAttribute(con->request, "device-uri",
2338 IPP_TAG_URI)) != NULL)
ef416fc2 2339 {
2340 /*
b423cd4c 2341 * Do we have a valid device URI?
ef416fc2 2342 */
2343
0af14961
MS
2344 http_uri_status_t uri_status; /* URI separation status */
2345 char old_device_uri[1024];
2346 /* Old device URI */
6961465f
MS
2347 static const char * const uri_status_strings[] =
2348 {
2349 "URI too large.",
2350 "Bad arguments to function.",
2351 "Bad resource path.",
2352 "Bad port number.",
2353 "Bad hostname/address.",
2354 "Bad username/password.",
2355 "Bad URI scheme.",
2356 "Bad URI.",
2357 "OK",
2358 "Missing URI scheme.",
2359 "Unknown URI scheme",
2360 "Missing resource path."
2361 };
2fb76298
MS
2362
2363
b423cd4c 2364 need_restart_job = 1;
ef416fc2 2365
2fb76298
MS
2366 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
2367 attr->values[0].string.text,
2368 scheme, sizeof(scheme),
2369 username, sizeof(username),
2370 host, sizeof(host), &port,
2371 resource, sizeof(resource));
2372
6961465f
MS
2373 cupsdLogMessage(CUPSD_LOG_DEBUG,
2374 "%s device-uri: %s", printer->name,
2375 uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
2376
2fb76298
MS
2377 if (uri_status < HTTP_URI_OK)
2378 {
84315f46 2379 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri \"%s\"."),
2fb76298 2380 attr->values[0].string.text);
2fb76298
MS
2381 return;
2382 }
b423cd4c 2383
09a101d6 2384 if (!strcmp(scheme, "file"))
ef416fc2 2385 {
2386 /*
b423cd4c 2387 * See if the administrator has enabled file devices...
ef416fc2 2388 */
2389
b423cd4c 2390 if (!FileDevice && strcmp(resource, "/dev/null"))
ef416fc2 2391 {
2392 /*
b423cd4c 2393 * File devices are disabled and the URL is not file:/dev/null...
ef416fc2 2394 */
2395
b423cd4c 2396 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 2397 _("File device URIs have been disabled. "
b423cd4c 2398 "To enable, see the FileDevice directive in "
cb7f98ee 2399 "\"%s/cups-files.conf\"."),
b423cd4c 2400 ServerRoot);
ef416fc2 2401 return;
2402 }
b423cd4c 2403 }
2404 else
2405 {
ef416fc2 2406 /*
b423cd4c 2407 * See if the backend exists and is executable...
ef416fc2 2408 */
2409
09a101d6 2410 snprintf(srcfile, sizeof(srcfile), "%s/backend/%s", ServerBin, scheme);
b423cd4c 2411 if (access(srcfile, X_OK))
ef416fc2 2412 {
2413 /*
b423cd4c 2414 * Could not find device in list!
ef416fc2 2415 */
ef416fc2 2416
84315f46
MS
2417 send_ipp_status(con, IPP_NOT_POSSIBLE,
2418 _("Bad device-uri scheme \"%s\"."), scheme);
b423cd4c 2419 return;
ef416fc2 2420 }
2421 }
ef416fc2 2422
0af14961
MS
2423 if (printer->sanitized_device_uri)
2424 strlcpy(old_device_uri, printer->sanitized_device_uri,
2425 sizeof(old_device_uri));
2426 else
2427 old_device_uri[0] = '\0';
2428
2429 cupsdSetDeviceURI(printer, attr->values[0].string.text);
2430
b423cd4c 2431 cupsdLogMessage(CUPSD_LOG_INFO,
2432 "Setting %s device-uri to \"%s\" (was \"%s\".)",
0af14961
MS
2433 printer->name, printer->sanitized_device_uri,
2434 old_device_uri);
ef416fc2 2435
09a101d6 2436 set_device_uri = 1;
ef416fc2 2437 }
2438
09a101d6 2439 set_port_monitor = 0;
2440
b423cd4c 2441 if ((attr = ippFindAttribute(con->request, "port-monitor",
09a101d6 2442 IPP_TAG_NAME)) != NULL)
ef416fc2 2443 {
b423cd4c 2444 ipp_attribute_t *supported; /* port-monitor-supported attribute */
ef416fc2 2445
ef416fc2 2446
b423cd4c 2447 need_restart_job = 1;
ef416fc2 2448
e4572d57 2449 supported = ippFindAttribute(printer->ppd_attrs, "port-monitor-supported",
a41f09e2 2450 IPP_TAG_NAME);
080811b1
MS
2451 if (supported)
2452 {
2453 for (i = 0; i < supported->num_values; i ++)
2454 if (!strcmp(supported->values[i].string.text,
2455 attr->values[0].string.text))
2456 break;
2457 }
ef416fc2 2458
080811b1 2459 if (!supported || i >= supported->num_values)
b423cd4c 2460 {
84315f46 2461 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad port-monitor \"%s\"."),
b423cd4c 2462 attr->values[0].string.text);
2463 return;
2464 }
ef416fc2 2465
b423cd4c 2466 cupsdLogMessage(CUPSD_LOG_INFO,
2467 "Setting %s port-monitor to \"%s\" (was \"%s\".)",
2468 printer->name, attr->values[0].string.text,
09a101d6 2469 printer->port_monitor ? printer->port_monitor : "none");
ef416fc2 2470
b423cd4c 2471 if (strcmp(attr->values[0].string.text, "none"))
2472 cupsdSetString(&printer->port_monitor, attr->values[0].string.text);
2473 else
2474 cupsdClearString(&printer->port_monitor);
09a101d6 2475
2476 set_port_monitor = 1;
b423cd4c 2477 }
ef416fc2 2478
b423cd4c 2479 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
f8b3a85b
MS
2480 IPP_TAG_BOOLEAN)) != NULL &&
2481 attr->values[0].boolean != printer->accepting)
b423cd4c 2482 {
2483 cupsdLogMessage(CUPSD_LOG_INFO,
2484 "Setting %s printer-is-accepting-jobs to %d (was %d.)",
2485 printer->name, attr->values[0].boolean, printer->accepting);
ef416fc2 2486
b423cd4c 2487 printer->accepting = attr->values[0].boolean;
f8b3a85b
MS
2488
2489 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
2490 "%s accepting jobs.",
2491 printer->accepting ? "Now" : "No longer");
b423cd4c 2492 }
bd7854cb 2493
b423cd4c 2494 if ((attr = ippFindAttribute(con->request, "printer-is-shared",
2495 IPP_TAG_BOOLEAN)) != NULL)
2496 {
07ed0e9a
MS
2497 if (attr->values[0].boolean &&
2498 printer->num_auth_info_required == 1 &&
2499 !strcmp(printer->auth_info_required[0], "negotiate"))
2500 {
2501 send_ipp_status(con, IPP_BAD_REQUEST,
2502 _("Cannot share a remote Kerberized printer."));
2503 return;
2504 }
2505
b423cd4c 2506 if (printer->shared && !attr->values[0].boolean)
f7deaa1a 2507 cupsdDeregisterPrinter(printer, 1);
bd7854cb 2508
b423cd4c 2509 cupsdLogMessage(CUPSD_LOG_INFO,
2510 "Setting %s printer-is-shared to %d (was %d.)",
2511 printer->name, attr->values[0].boolean, printer->shared);
bd7854cb 2512
b423cd4c 2513 printer->shared = attr->values[0].boolean;
2514 }
bd7854cb 2515
b423cd4c 2516 if ((attr = ippFindAttribute(con->request, "printer-state",
2517 IPP_TAG_ENUM)) != NULL)
bd7854cb 2518 {
b423cd4c 2519 if (attr->values[0].integer != IPP_PRINTER_IDLE &&
2520 attr->values[0].integer != IPP_PRINTER_STOPPED)
2521 {
84315f46 2522 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad printer-state value %d."),
b423cd4c 2523 attr->values[0].integer);
2524 return;
2525 }
bd7854cb 2526
bc44d920 2527 cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)",
2528 printer->name, attr->values[0].integer, printer->state);
b423cd4c 2529
2530 if (attr->values[0].integer == IPP_PRINTER_STOPPED)
2531 cupsdStopPrinter(printer, 0);
2532 else
2533 {
2534 need_restart_job = 1;
2535 cupsdSetPrinterState(printer, (ipp_pstate_t)(attr->values[0].integer), 0);
2536 }
2537 }
745129be 2538
b423cd4c 2539 if ((attr = ippFindAttribute(con->request, "printer-state-message",
2540 IPP_TAG_TEXT)) != NULL)
2541 {
2542 strlcpy(printer->state_message, attr->values[0].string.text,
2543 sizeof(printer->state_message));
f8b3a85b
MS
2544
2545 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL, "%s",
2546 printer->state_message);
bd7854cb 2547 }
2548
1f6f3dbc
MS
2549 if ((attr = ippFindAttribute(con->request, "printer-state-reasons",
2550 IPP_TAG_KEYWORD)) != NULL)
2551 {
2552 if (attr->num_values >
2553 (int)(sizeof(printer->reasons) / sizeof(printer->reasons[0])))
2554 {
2555 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 2556 _("Too many printer-state-reasons values (%d > %d)."),
1f6f3dbc
MS
2557 attr->num_values,
2558 (int)(sizeof(printer->reasons) /
2559 sizeof(printer->reasons[0])));
2560 return;
2561 }
2562
2563 for (i = 0; i < printer->num_reasons; i ++)
2564 _cupsStrFree(printer->reasons[i]);
2565
2566 printer->num_reasons = 0;
2567 for (i = 0; i < attr->num_values; i ++)
2568 {
2569 if (!strcmp(attr->values[i].string.text, "none"))
2570 continue;
2571
2572 printer->reasons[printer->num_reasons] =
426c6a59 2573 _cupsStrRetain(attr->values[i].string.text);
745129be 2574 printer->num_reasons ++;
1f6f3dbc 2575
745129be 2576 if (!strcmp(attr->values[i].string.text, "paused") &&
1f6f3dbc
MS
2577 printer->state != IPP_PRINTER_STOPPED)
2578 {
2579 cupsdLogMessage(CUPSD_LOG_INFO,
2580 "Setting %s printer-state to %d (was %d.)",
2581 printer->name, IPP_PRINTER_STOPPED, printer->state);
2582 cupsdStopPrinter(printer, 0);
2583 }
1f6f3dbc
MS
2584 }
2585
2586 if (PrintcapFormat == PRINTCAP_PLIST)
2587 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
f8b3a85b
MS
2588
2589 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
2590 "Printer \"%s\" state changed.", printer->name);
1f6f3dbc
MS
2591 }
2592
b423cd4c 2593 set_printer_defaults(con, printer);
2594
09a101d6 2595 if ((attr = ippFindAttribute(con->request, "auth-info-required",
2596 IPP_TAG_KEYWORD)) != NULL)
2597 cupsdSetAuthInfoRequired(printer, NULL, attr);
2598
bd7854cb 2599 /*
b423cd4c 2600 * See if we have all required attributes...
bd7854cb 2601 */
2602
b423cd4c 2603 if (!printer->device_uri)
2604 cupsdSetString(&printer->device_uri, "file:///dev/null");
bd7854cb 2605
2606 /*
b423cd4c 2607 * See if we have an interface script or PPD file attached to the request...
bd7854cb 2608 */
2609
b423cd4c 2610 if (con->filename)
2611 {
2612 need_restart_job = 1;
b226ab99 2613 changed_driver = 1;
bd7854cb 2614
b423cd4c 2615 strlcpy(srcfile, con->filename, sizeof(srcfile));
ef416fc2 2616
b423cd4c 2617 if ((fp = cupsFileOpen(srcfile, "rb")))
2618 {
2619 /*
2620 * Yes; get the first line from it...
2621 */
ef416fc2 2622
b423cd4c 2623 line[0] = '\0';
2624 cupsFileGets(fp, line, sizeof(line));
2625 cupsFileClose(fp);
ef416fc2 2626
b423cd4c 2627 /*
2628 * Then see what kind of file it is...
2629 */
ef416fc2 2630
b423cd4c 2631 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
2632 printer->name);
ef416fc2 2633
b423cd4c 2634 if (!strncmp(line, "*PPD-Adobe", 10))
2635 {
2636 /*
2637 * The new file is a PPD file, so remove any old interface script
2638 * that might be lying around...
2639 */
ef416fc2 2640
b423cd4c 2641 unlink(dstfile);
2642 }
2643 else
2644 {
2645 /*
2646 * This must be an interface script, so move the file over to the
2647 * interfaces directory and make it executable...
2648 */
ef416fc2 2649
b423cd4c 2650 if (copy_file(srcfile, dstfile))
2651 {
2652 send_ipp_status(con, IPP_INTERNAL_ERROR,
4d301e69 2653 _("Unable to copy interface script - %s"),
b423cd4c 2654 strerror(errno));
2655 return;
2656 }
568fa3fa
MS
2657
2658 cupsdLogMessage(CUPSD_LOG_DEBUG,
4d301e69 2659 "Copied interface script successfully");
568fa3fa 2660 chmod(dstfile, 0755);
b423cd4c 2661 }
ef416fc2 2662
b423cd4c 2663 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
2664 printer->name);
ef416fc2 2665
b423cd4c 2666 if (!strncmp(line, "*PPD-Adobe", 10))
2667 {
2668 /*
2669 * The new file is a PPD file, so move the file over to the
2670 * ppd directory and make it readable by all...
2671 */
ef416fc2 2672
b423cd4c 2673 if (copy_file(srcfile, dstfile))
2674 {
2675 send_ipp_status(con, IPP_INTERNAL_ERROR,
4d301e69 2676 _("Unable to copy PPD file - %s"),
b423cd4c 2677 strerror(errno));
2678 return;
2679 }
568fa3fa
MS
2680
2681 cupsdLogMessage(CUPSD_LOG_DEBUG,
4d301e69 2682 "Copied PPD file successfully");
568fa3fa 2683 chmod(dstfile, 0644);
b423cd4c 2684 }
2685 else
2686 {
2687 /*
2688 * This must be an interface script, so remove any old PPD file that
2689 * may be lying around...
2690 */
ef416fc2 2691
b423cd4c 2692 unlink(dstfile);
2693 }
2694 }
2695 }
2696 else if ((attr = ippFindAttribute(con->request, "ppd-name",
2697 IPP_TAG_NAME)) != NULL)
ef416fc2 2698 {
b423cd4c 2699 need_restart_job = 1;
b226ab99 2700 changed_driver = 1;
ef416fc2 2701
b423cd4c 2702 if (!strcmp(attr->values[0].string.text, "raw"))
ef416fc2 2703 {
b423cd4c 2704 /*
2705 * Raw driver, remove any existing PPD or interface script files.
2706 */
ef416fc2 2707
b423cd4c 2708 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
2709 printer->name);
2710 unlink(dstfile);
ef416fc2 2711
b423cd4c 2712 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
2713 printer->name);
2714 unlink(dstfile);
2715 }
2716 else
ef416fc2 2717 {
b423cd4c 2718 /*
2719 * PPD model file...
2720 */
2721
2722 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
2723 printer->name);
2724 unlink(dstfile);
2725
2726 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
2727 printer->name);
2728
2729 if (copy_model(con, attr->values[0].string.text, dstfile))
2730 {
84315f46 2731 send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file."));
b423cd4c 2732 return;
2733 }
568fa3fa
MS
2734
2735 cupsdLogMessage(CUPSD_LOG_DEBUG,
4d301e69 2736 "Copied PPD file successfully");
568fa3fa 2737 chmod(dstfile, 0644);
b226ab99
MS
2738 }
2739 }
2740
2741 if (changed_driver)
2742 {
2743 /*
2744 * If we changed the PPD/interface script, then remove the printer's cache
c7017ecc 2745 * file and clear the printer-state-reasons...
b226ab99
MS
2746 */
2747
2748 char cache_name[1024]; /* Cache filename for printer attrs */
2749
f14324a7 2750 snprintf(cache_name, sizeof(cache_name), "%s/%s.data", CacheDir,
54afec33
MS
2751 printer->name);
2752 unlink(cache_name);
2753
c7017ecc
MS
2754 cupsdSetPrinterReasons(printer, "none");
2755
b226ab99
MS
2756 /*
2757 * (Re)register color profiles...
2758 */
568fa3fa 2759
a29fd7dd 2760 cupsdRegisterColor(printer);
ef416fc2 2761 }
2762
09a101d6 2763 /*
2764 * If we set the device URI but not the port monitor, check which port
2765 * monitor to use by default...
2766 */
2767
2768 if (set_device_uri && !set_port_monitor)
2769 {
2770 ppd_file_t *ppd; /* PPD file */
2771 ppd_attr_t *ppdattr; /* cupsPortMonitor attribute */
2772
2773
2774 httpSeparateURI(HTTP_URI_CODING_ALL, printer->device_uri, scheme,
2775 sizeof(scheme), username, sizeof(username), host,
2776 sizeof(host), &port, resource, sizeof(resource));
2777
2778 snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot,
2779 printer->name);
9c80ffa2 2780 if ((ppd = _ppdOpenFile(srcfile, _PPD_LOCALIZATION_NONE)) != NULL)
09a101d6 2781 {
2782 for (ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
2783 ppdattr;
2784 ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL))
2785 if (!strcmp(scheme, ppdattr->spec))
2786 {
2787 cupsdLogMessage(CUPSD_LOG_INFO,
2788 "Setting %s port-monitor to \"%s\" (was \"%s\".)",
2789 printer->name, ppdattr->value,
bc44d920 2790 printer->port_monitor ? printer->port_monitor
2791 : "none");
09a101d6 2792
2793 if (strcmp(ppdattr->value, "none"))
2794 cupsdSetString(&printer->port_monitor, ppdattr->value);
2795 else
2796 cupsdClearString(&printer->port_monitor);
2797
2798 break;
2799 }
2800
2801 ppdClose(ppd);
2802 }
2803 }
2804
ef416fc2 2805 /*
b423cd4c 2806 * Update the printer attributes and return...
ef416fc2 2807 */
2808
b423cd4c 2809 cupsdSetPrinterAttrs(printer);
3dfe78b3 2810 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 2811
b423cd4c 2812 if (need_restart_job && printer->job)
ef416fc2 2813 {
b423cd4c 2814 /*
b9faaae1 2815 * Restart the current job...
b423cd4c 2816 */
ef416fc2 2817
b9faaae1
MS
2818 cupsdSetJobState(printer->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
2819 "Job restarted because the printer was modified.");
ef416fc2 2820 }
2821
3dfe78b3 2822 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
b423cd4c 2823
2824 if (modify)
ef416fc2 2825 {
c7017ecc 2826 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED,
f8b3a85b
MS
2827 printer, NULL, "Printer \"%s\" modified by \"%s\".",
2828 printer->name, get_username(con));
ef416fc2 2829
b423cd4c 2830 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" modified by \"%s\".",
2831 printer->name, get_username(con));
ef416fc2 2832 }
b423cd4c 2833 else
2834 {
c7017ecc 2835 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
f8b3a85b
MS
2836 printer, NULL, "New printer \"%s\" added by \"%s\".",
2837 printer->name, get_username(con));
ef416fc2 2838
b423cd4c 2839 cupsdLogMessage(CUPSD_LOG_INFO, "New printer \"%s\" added by \"%s\".",
2840 printer->name, get_username(con));
2841 }
2842
2843 con->response->request.status.status_code = IPP_OK;
ef416fc2 2844}
2845
2846
2847/*
b423cd4c 2848 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
2849 * based upon the printer state...
ef416fc2 2850 */
2851
b423cd4c 2852static void
2853add_printer_state_reasons(
2854 cupsd_client_t *con, /* I - Client connection */
2855 cupsd_printer_t *p) /* I - Printer info */
ef416fc2 2856{
b423cd4c 2857 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2858 "add_printer_state_reasons(%p[%d], %p[%s])",
2859 con, con->http.fd, p, p->name);
ef416fc2 2860
b423cd4c 2861 if (p->num_reasons == 0)
2862 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
745129be 2863 "printer-state-reasons", NULL, "none");
b423cd4c 2864 else
2865 ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2866 "printer-state-reasons", p->num_reasons, NULL,
2867 (const char * const *)p->reasons);
2868}
ef416fc2 2869
ef416fc2 2870
b423cd4c 2871/*
2872 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
2873 * the specified printer or class.
2874 */
ef416fc2 2875
b423cd4c 2876static void
2877add_queued_job_count(
2878 cupsd_client_t *con, /* I - Client connection */
2879 cupsd_printer_t *p) /* I - Printer or class */
2880{
2881 int count; /* Number of jobs on destination */
ef416fc2 2882
ef416fc2 2883
b423cd4c 2884 cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])",
2885 con, con->http.fd, p, p->name);
ef416fc2 2886
b423cd4c 2887 count = cupsdGetPrinterJobCount(p->name);
ef416fc2 2888
b423cd4c 2889 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2890 "queued-job-count", count);
2891}
ef416fc2 2892
ef416fc2 2893
b423cd4c 2894/*
2895 * 'apply_printer_defaults()' - Apply printer default options to a job.
2896 */
ef416fc2 2897
b423cd4c 2898static void
2899apply_printer_defaults(
2900 cupsd_printer_t *printer, /* I - Printer */
2901 cupsd_job_t *job) /* I - Job */
2902{
2903 int i, /* Looping var */
2904 num_options; /* Number of default options */
2905 cups_option_t *options, /* Default options */
f7deaa1a 2906 *option; /* Current option */
ef416fc2 2907
ef416fc2 2908
b423cd4c 2909 /*
2910 * Collect all of the default options and add the missing ones to the
2911 * job object...
2912 */
ef416fc2 2913
1f0275e3
MS
2914 for (i = printer->num_options, num_options = 0, options = NULL,
2915 option = printer->options;
b423cd4c 2916 i > 0;
2917 i --, option ++)
2918 if (!ippFindAttribute(job->attrs, option->name, IPP_TAG_ZERO))
2919 {
2920 num_options = cupsAddOption(option->name, option->value, num_options,
2921 &options);
2922 }
ef416fc2 2923
b423cd4c 2924 /*
2925 * Encode these options as attributes in the job object...
2926 */
ef416fc2 2927
b423cd4c 2928 cupsEncodeOptions2(job->attrs, num_options, options, IPP_TAG_JOB);
2929 cupsFreeOptions(num_options, options);
2930}
2931
2932
2933/*
2934 * 'authenticate_job()' - Set job authentication info.
2935 */
2936
2937static void
2938authenticate_job(cupsd_client_t *con, /* I - Client connection */
2939 ipp_attribute_t *uri) /* I - Job URI */
2940{
f7deaa1a 2941 ipp_attribute_t *attr, /* job-id attribute */
2942 *auth_info; /* auth-info attribute */
b423cd4c 2943 int jobid; /* Job ID */
2944 cupsd_job_t *job; /* Current job */
61cf44e2 2945 char scheme[HTTP_MAX_URI],
b423cd4c 2946 /* Method portion of URI */
2947 username[HTTP_MAX_URI],
2948 /* Username portion of URI */
2949 host[HTTP_MAX_URI],
2950 /* Host portion of URI */
2951 resource[HTTP_MAX_URI];
2952 /* Resource portion of URI */
2953 int port; /* Port portion of URI */
ef416fc2 2954
ef416fc2 2955
b423cd4c 2956 cupsdLogMessage(CUPSD_LOG_DEBUG2, "authenticate_job(%p[%d], %s)",
2957 con, con->http.fd, uri->values[0].string.text);
ef416fc2 2958
b423cd4c 2959 /*
2960 * Start with "everything is OK" status...
2961 */
ef416fc2 2962
b423cd4c 2963 con->response->request.status.status_code = IPP_OK;
ef416fc2 2964
b423cd4c 2965 /*
2966 * See if we have a job URI or a printer URI...
2967 */
ef416fc2 2968
b423cd4c 2969 if (!strcmp(uri->name, "printer-uri"))
2970 {
2971 /*
2972 * Got a printer URI; see if we also have a job-id attribute...
2973 */
ef416fc2 2974
b423cd4c 2975 if ((attr = ippFindAttribute(con->request, "job-id",
2976 IPP_TAG_INTEGER)) == NULL)
2977 {
2978 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 2979 _("Got a printer-uri attribute but no job-id."));
b423cd4c 2980 return;
2981 }
2982
2983 jobid = attr->values[0].integer;
2984 }
2985 else
2986 {
2987 /*
2988 * Got a job URI; parse it to get the job ID...
2989 */
ef416fc2 2990
61cf44e2
MS
2991 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
2992 sizeof(scheme), username, sizeof(username), host,
b423cd4c 2993 sizeof(host), &port, resource, sizeof(resource));
f7deaa1a 2994
b423cd4c 2995 if (strncmp(resource, "/jobs/", 6))
2996 {
2997 /*
2998 * Not a valid URI!
2999 */
ef416fc2 3000
84315f46 3001 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
b423cd4c 3002 uri->values[0].string.text);
3003 return;
3004 }
ef416fc2 3005
b423cd4c 3006 jobid = atoi(resource + 6);
3007 }
ef416fc2 3008
b423cd4c 3009 /*
3010 * See if the job exists...
3011 */
ef416fc2 3012
b423cd4c 3013 if ((job = cupsdFindJob(jobid)) == NULL)
3014 {
3015 /*
3016 * Nope - return a "not found" error...
3017 */
ef416fc2 3018
84315f46 3019 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
ef416fc2 3020 return;
b423cd4c 3021 }
ef416fc2 3022
b423cd4c 3023 /*
3024 * See if the job has been completed...
3025 */
3026
3027 if (job->state_value != IPP_JOB_HELD)
ef416fc2 3028 {
3029 /*
b423cd4c 3030 * Return a "not-possible" error...
ef416fc2 3031 */
3032
b423cd4c 3033 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 3034 _("Job #%d is not held for authentication."),
b423cd4c 3035 jobid);
3036 return;
3037 }
ef416fc2 3038
b423cd4c 3039 /*
3040 * See if we have already authenticated...
3041 */
3042
f7deaa1a 3043 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
3044
3045 if (!con->username[0] && !auth_info)
b423cd4c 3046 {
db1f069b
MS
3047 cupsd_printer_t *printer; /* Job destination */
3048
db1f069b
MS
3049 /*
3050 * No auth data. If we need to authenticate via Kerberos, send a
3051 * HTTP auth challenge, otherwise just return an IPP error...
3052 */
3053
3054 printer = cupsdFindDest(job->dest);
3055
3056 if (printer && printer->num_auth_info_required > 0 &&
3057 !strcmp(printer->auth_info_required[0], "negotiate"))
3058 send_http_error(con, HTTP_UNAUTHORIZED, printer);
3059 else
3060 send_ipp_status(con, IPP_NOT_AUTHORIZED,
84315f46 3061 _("No authentication information provided."));
b423cd4c 3062 return;
3063 }
3064
3065 /*
3066 * See if the job is owned by the requesting user...
3067 */
3068
3069 if (!validate_user(job, con, job->username, username, sizeof(username)))
3070 {
b0f6947b
MS
3071 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
3072 cupsdFindDest(job->dest));
b423cd4c 3073 return;
3074 }
3075
3076 /*
3077 * Save the authentication information for this job...
3078 */
3079
f7deaa1a 3080 save_auth_info(con, job, auth_info);
b423cd4c 3081
3082 /*
3083 * Reset the job-hold-until value to "no-hold"...
3084 */
3085
3086 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
3087 IPP_TAG_KEYWORD)) == NULL)
3088 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
3089
3090 if (attr)
3091 {
3092 attr->value_tag = IPP_TAG_KEYWORD;
3093 cupsdSetString(&(attr->values[0].string.text), "no-hold");
ef416fc2 3094 }
b423cd4c 3095
3096 /*
3097 * Release the job and return...
3098 */
3099
3100 cupsdReleaseJob(job);
3101
c8fef167
MS
3102 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, NULL, job, "Job authenticated by user");
3103
75bd9771 3104 cupsdLogJob(job, CUPSD_LOG_INFO, "Authenticated by \"%s\".", con->username);
c8fef167
MS
3105
3106 cupsdCheckJobs();
ef416fc2 3107}
3108
3109
3110/*
aaf19ab0 3111 * 'cancel_all_jobs()' - Cancel all or selected print jobs.
ef416fc2 3112 */
3113
b423cd4c 3114static void
3115cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */
3116 ipp_attribute_t *uri) /* I - Job or Printer URI */
ef416fc2 3117{
aaf19ab0 3118 int i; /* Looping var */
b423cd4c 3119 http_status_t status; /* Policy status */
b423cd4c 3120 cups_ptype_t dtype; /* Destination type */
f7deaa1a 3121 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
b423cd4c 3122 userpass[HTTP_MAX_URI], /* Username portion of URI */
f7deaa1a 3123 hostname[HTTP_MAX_URI], /* Host portion of URI */
b423cd4c 3124 resource[HTTP_MAX_URI]; /* Resource portion of URI */
3125 int port; /* Port portion of URI */
3126 ipp_attribute_t *attr; /* Attribute in request */
aaf19ab0
MS
3127 const char *username = NULL; /* Username */
3128 cupsd_jobaction_t purge = CUPSD_JOB_DEFAULT;
3129 /* Purge? */
b423cd4c 3130 cupsd_printer_t *printer; /* Printer */
aaf19ab0
MS
3131 ipp_attribute_t *job_ids; /* job-ids attribute */
3132 cupsd_job_t *job; /* Job */
ef416fc2 3133
3134
b423cd4c 3135 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_all_jobs(%p[%d], %s)", con,
3136 con->http.fd, uri->values[0].string.text);
ef416fc2 3137
3138 /*
aaf19ab0 3139 * Get the jobs to cancel/purge...
ef416fc2 3140 */
3141
aaf19ab0 3142 switch (con->request->request.op.operation_id)
b423cd4c 3143 {
aaf19ab0
MS
3144 case IPP_PURGE_JOBS :
3145 /*
3146 * Get the username (if any) for the jobs we want to cancel (only if
3147 * "my-jobs" is specified...
3148 */
ef416fc2 3149
aaf19ab0
MS
3150 if ((attr = ippFindAttribute(con->request, "my-jobs",
3151 IPP_TAG_BOOLEAN)) != NULL &&
3152 attr->values[0].boolean)
3153 {
3154 if ((attr = ippFindAttribute(con->request, "requesting-user-name",
3155 IPP_TAG_NAME)) != NULL)
3156 username = attr->values[0].string.text;
3157 else
3158 {
3159 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 3160 _("Missing requesting-user-name attribute."));
aaf19ab0
MS
3161 return;
3162 }
3163 }
ef416fc2 3164
aaf19ab0
MS
3165 /*
3166 * Look for the "purge-jobs" attribute...
3167 */
3168
3169 if ((attr = ippFindAttribute(con->request, "purge-jobs",
3170 IPP_TAG_BOOLEAN)) != NULL)
3171 purge = attr->values[0].boolean ? CUPSD_JOB_PURGE : CUPSD_JOB_DEFAULT;
3172 else
3173 purge = CUPSD_JOB_PURGE;
3174 break;
3175
3176 case IPP_CANCEL_MY_JOBS :
3177 if (con->username[0])
3178 username = con->username;
3179 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
3180 IPP_TAG_NAME)) != NULL)
3181 username = attr->values[0].string.text;
3182 else
3183 {
3184 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 3185 _("Missing requesting-user-name attribute."));
aaf19ab0
MS
3186 return;
3187 }
3188
3189 default :
3190 break;
ef416fc2 3191 }
aaf19ab0
MS
3192
3193 job_ids = ippFindAttribute(con->request, "job-ids", IPP_TAG_INTEGER);
ef416fc2 3194
b423cd4c 3195 /*
aaf19ab0 3196 * See if we have a printer URI...
b423cd4c 3197 */
3198
aaf19ab0
MS
3199 if (strcmp(uri->name, "printer-uri"))
3200 {
3201 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 3202 _("The printer-uri attribute is required."));
aaf19ab0
MS
3203 return;
3204 }
ef416fc2 3205
3206 /*
b423cd4c 3207 * And if the destination is valid...
ef416fc2 3208 */
3209
f7deaa1a 3210 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 3211 {
3212 /*
b423cd4c 3213 * Bad URI?
ef416fc2 3214 */
3215
f7deaa1a 3216 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text,
3217 scheme, sizeof(scheme), userpass, sizeof(userpass),
3218 hostname, sizeof(hostname), &port,
3219 resource, sizeof(resource));
3220
b423cd4c 3221 if ((!strncmp(resource, "/printers/", 10) && resource[10]) ||
3222 (!strncmp(resource, "/classes/", 9) && resource[9]))
3223 {
3224 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 3225 _("The printer or class does not exist."));
b423cd4c 3226 return;
3227 }
ef416fc2 3228
b423cd4c 3229 /*
3230 * Check policy...
3231 */
3232
3233 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
3234 {
f899b121 3235 send_http_error(con, status, NULL);
b423cd4c 3236 return;
3237 }
ef416fc2 3238
aaf19ab0
MS
3239 if (job_ids)
3240 {
3241 for (i = 0; i < job_ids->num_values; i ++)
3242 {
db8b865d 3243 if ((job = cupsdFindJob(job_ids->values[i].integer)) == NULL)
aaf19ab0 3244 break;
db8b865d
MS
3245
3246 if (con->request->request.op.operation_id == IPP_CANCEL_MY_JOBS &&
3247 _cups_strcasecmp(job->username, username))
3248 break;
aaf19ab0
MS
3249 }
3250
3251 if (i < job_ids->num_values)
3252 {
84315f46 3253 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
aaf19ab0
MS
3254 job_ids->values[i].integer);
3255 return;
3256 }
ef416fc2 3257
aaf19ab0
MS
3258 for (i = 0; i < job_ids->num_values; i ++)
3259 {
3260 job = cupsdFindJob(job_ids->values[i].integer);
ef416fc2 3261
aaf19ab0
MS
3262 cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
3263 purge == CUPSD_JOB_PURGE ? "Job purged by user." :
3264 "Job canceled by user.");
3265 }
3266
3267 cupsdLogMessage(CUPSD_LOG_INFO, "Selected jobs were %s by \"%s\".",
3268 purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
3269 get_username(con));
3270 }
3271 else
3272 {
3273 /*
3274 * Cancel all jobs on all printers...
3275 */
3276
3277 cupsdCancelJobs(NULL, username, purge);
3278
3279 cupsdLogMessage(CUPSD_LOG_INFO, "All jobs were %s by \"%s\".",
3280 purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
3281 get_username(con));
3282 }
ef416fc2 3283 }
b423cd4c 3284 else
ef416fc2 3285 {
3286 /*
b423cd4c 3287 * Check policy...
ef416fc2 3288 */
3289
f7deaa1a 3290 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
3291 NULL)) != HTTP_OK)
b423cd4c 3292 {
f899b121 3293 send_http_error(con, status, printer);
b423cd4c 3294 return;
3295 }
ef416fc2 3296
aaf19ab0
MS
3297 if (job_ids)
3298 {
3299 for (i = 0; i < job_ids->num_values; i ++)
3300 {
3301 if ((job = cupsdFindJob(job_ids->values[i].integer)) == NULL ||
88f9aafc 3302 _cups_strcasecmp(job->dest, printer->name))
aaf19ab0 3303 break;
db8b865d
MS
3304
3305 if (con->request->request.op.operation_id == IPP_CANCEL_MY_JOBS &&
3306 _cups_strcasecmp(job->username, username))
3307 break;
aaf19ab0 3308 }
b423cd4c 3309
aaf19ab0
MS
3310 if (i < job_ids->num_values)
3311 {
84315f46 3312 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
aaf19ab0
MS
3313 job_ids->values[i].integer);
3314 return;
3315 }
b423cd4c 3316
aaf19ab0
MS
3317 for (i = 0; i < job_ids->num_values; i ++)
3318 {
3319 job = cupsdFindJob(job_ids->values[i].integer);
3320
3321 cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
3322 purge == CUPSD_JOB_PURGE ? "Job purged by user." :
3323 "Job canceled by user.");
3324 }
3325
3326 cupsdLogMessage(CUPSD_LOG_INFO, "Selected jobs were %s by \"%s\".",
3327 purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
3328 get_username(con));
3329 }
3330 else
3331 {
3332 /*
3333 * Cancel all of the jobs on the named printer...
3334 */
3335
3336 cupsdCancelJobs(printer->name, username, purge);
3337
3338 cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".",
3339 printer->name,
3340 purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
3341 get_username(con));
3342 }
ef416fc2 3343 }
3344
b423cd4c 3345 con->response->request.status.status_code = IPP_OK;
3346}
3347
3348
3349/*
3350 * 'cancel_job()' - Cancel a print job.
3351 */
3352
3353static void
3354cancel_job(cupsd_client_t *con, /* I - Client connection */
3355 ipp_attribute_t *uri) /* I - Job or Printer URI */
3356{
3357 ipp_attribute_t *attr; /* Current attribute */
3358 int jobid; /* Job ID */
f7deaa1a 3359 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
b423cd4c 3360 username[HTTP_MAX_URI], /* Username portion of URI */
3361 host[HTTP_MAX_URI], /* Host portion of URI */
3362 resource[HTTP_MAX_URI]; /* Resource portion of URI */
3363 int port; /* Port portion of URI */
3364 cupsd_job_t *job; /* Job information */
bc44d920 3365 cups_ptype_t dtype; /* Destination type (printer/class) */
b423cd4c 3366 cupsd_printer_t *printer; /* Printer data */
68b10830 3367 cupsd_jobaction_t purge; /* Purge the job? */
b423cd4c 3368
3369
3370 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", con,
3371 con->http.fd, uri->values[0].string.text);
3372
ef416fc2 3373 /*
b423cd4c 3374 * See if we have a job URI or a printer URI...
ef416fc2 3375 */
3376
b423cd4c 3377 if (!strcmp(uri->name, "printer-uri"))
3378 {
3379 /*
3380 * Got a printer URI; see if we also have a job-id attribute...
3381 */
3382
3383 if ((attr = ippFindAttribute(con->request, "job-id",
3384 IPP_TAG_INTEGER)) == NULL)
3385 {
3386 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 3387 _("Got a printer-uri attribute but no job-id."));
b423cd4c 3388 return;
3389 }
3390
3391 if ((jobid = attr->values[0].integer) == 0)
ef416fc2 3392 {
3393 /*
b423cd4c 3394 * Find the current job on the specified printer...
ef416fc2 3395 */
3396
f7deaa1a 3397 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 3398 {
3399 /*
b423cd4c 3400 * Bad URI...
ef416fc2 3401 */
3402
b423cd4c 3403 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 3404 _("The printer or class does not exist."));
b423cd4c 3405 return;
ef416fc2 3406 }
3407
3408 /*
b9faaae1 3409 * See if there are any pending jobs...
ef416fc2 3410 */
3411
b9faaae1
MS
3412 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
3413 job;
3414 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
3415 if (job->state_value <= IPP_JOB_PROCESSING &&
88f9aafc 3416 !_cups_strcasecmp(job->dest, printer->name))
b9faaae1
MS
3417 break;
3418
3419 if (job)
3420 jobid = job->id;
ef416fc2 3421 else
ef416fc2 3422 {
3423 /*
b9faaae1 3424 * No, try stopped jobs...
ef416fc2 3425 */
f7deaa1a 3426
b9faaae1 3427 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
b423cd4c 3428 job;
3429 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
b9faaae1 3430 if (job->state_value == IPP_JOB_STOPPED &&
88f9aafc 3431 !_cups_strcasecmp(job->dest, printer->name))
b423cd4c 3432 break;
ef416fc2 3433
b423cd4c 3434 if (job)
3435 jobid = job->id;
3436 else
ef416fc2 3437 {
84315f46 3438 send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s."),
b9faaae1
MS
3439 printer->name);
3440 return;
b423cd4c 3441 }
3442 }
3443 }
3444 }
3445 else
3446 {
3447 /*
3448 * Got a job URI; parse it to get the job ID...
3449 */
ef416fc2 3450
f7deaa1a 3451 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
3452 sizeof(scheme), username, sizeof(username), host,
b423cd4c 3453 sizeof(host), &port, resource, sizeof(resource));
f7deaa1a 3454
b423cd4c 3455 if (strncmp(resource, "/jobs/", 6))
3456 {
3457 /*
3458 * Not a valid URI!
3459 */
ef416fc2 3460
84315f46 3461 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
b423cd4c 3462 uri->values[0].string.text);
3463 return;
3464 }
3465
3466 jobid = atoi(resource + 6);
3467 }
3468
0a682745
MS
3469 /*
3470 * Look for the "purge-job" attribute...
3471 */
3472
3473 if ((attr = ippFindAttribute(con->request, "purge-job",
3474 IPP_TAG_BOOLEAN)) != NULL)
68b10830 3475 purge = attr->values[0].boolean ? CUPSD_JOB_PURGE : CUPSD_JOB_DEFAULT;
0a682745 3476 else
68b10830 3477 purge = CUPSD_JOB_DEFAULT;
0a682745 3478
b423cd4c 3479 /*
3480 * See if the job exists...
3481 */
3482
3483 if ((job = cupsdFindJob(jobid)) == NULL)
3484 {
3485 /*
3486 * Nope - return a "not found" error...
3487 */
3488
84315f46 3489 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
b423cd4c 3490 return;
3491 }
3492
3493 /*
3494 * See if the job is owned by the requesting user...
3495 */
3496
3497 if (!validate_user(job, con, job->username, username, sizeof(username)))
3498 {
b0f6947b
MS
3499 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
3500 cupsdFindDest(job->dest));
b423cd4c 3501 return;
3502 }
3503
3504 /*
d09495fa 3505 * See if the job is already completed, canceled, or aborted; if so,
b423cd4c 3506 * we can't cancel...
3507 */
3508
68b10830 3509 if (job->state_value >= IPP_JOB_CANCELED && purge != CUPSD_JOB_PURGE)
b423cd4c 3510 {
e1d6a774 3511 switch (job->state_value)
3512 {
d09495fa 3513 case IPP_JOB_CANCELED :
e1d6a774 3514 send_ipp_status(con, IPP_NOT_POSSIBLE,
d09495fa 3515 _("Job #%d is already canceled - can\'t cancel."),
e1d6a774 3516 jobid);
3517 break;
3518
3519 case IPP_JOB_ABORTED :
3520 send_ipp_status(con, IPP_NOT_POSSIBLE,
3521 _("Job #%d is already aborted - can\'t cancel."),
3522 jobid);
3523 break;
3524
3525 default :
3526 send_ipp_status(con, IPP_NOT_POSSIBLE,
3527 _("Job #%d is already completed - can\'t cancel."),
3528 jobid);
3529 break;
3530 }
3531
b423cd4c 3532 return;
3533 }
ef416fc2 3534
b423cd4c 3535 /*
3536 * Cancel the job and return...
3537 */
ef416fc2 3538
b9faaae1 3539 cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
68b10830
MS
3540 purge == CUPSD_JOB_PURGE ? "Job purged by \"%s\"" :
3541 "Job canceled by \"%s\"",
b9faaae1 3542 username);
b423cd4c 3543 cupsdCheckJobs();
ef416fc2 3544
68b10830 3545 if (purge == CUPSD_JOB_PURGE)
0a682745 3546 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Purged by \"%s\".", jobid,
75bd9771 3547 username);
0a682745
MS
3548 else
3549 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Canceled by \"%s\".", jobid,
75bd9771 3550 username);
ef416fc2 3551
b423cd4c 3552 con->response->request.status.status_code = IPP_OK;
3553}
ef416fc2 3554
ef416fc2 3555
b423cd4c 3556/*
3557 * 'cancel_subscription()' - Cancel a subscription.
3558 */
ef416fc2 3559
b423cd4c 3560static void
3561cancel_subscription(
3562 cupsd_client_t *con, /* I - Client connection */
3563 int sub_id) /* I - Subscription ID */
3564{
3565 http_status_t status; /* Policy status */
3566 cupsd_subscription_t *sub; /* Subscription */
ef416fc2 3567
ef416fc2 3568
b423cd4c 3569 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3570 "cancel_subscription(con=%p[%d], sub_id=%d)",
3571 con, con->http.fd, sub_id);
ef416fc2 3572
b423cd4c 3573 /*
3574 * Is the subscription ID valid?
3575 */
ef416fc2 3576
b423cd4c 3577 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
3578 {
3579 /*
3580 * Bad subscription ID...
3581 */
ef416fc2 3582
b423cd4c 3583 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 3584 _("Subscription #%d does not exist."), sub_id);
b423cd4c 3585 return;
3586 }
ef416fc2 3587
b423cd4c 3588 /*
3589 * Check policy...
3590 */
ef416fc2 3591
b423cd4c 3592 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
3593 DefaultPolicyPtr,
3594 con, sub->owner)) != HTTP_OK)
3595 {
f899b121 3596 send_http_error(con, status, sub->dest);
b423cd4c 3597 return;
3598 }
ef416fc2 3599
b423cd4c 3600 /*
3601 * Cancel the subscription...
3602 */
ef416fc2 3603
b423cd4c 3604 cupsdDeleteSubscription(sub, 1);
ef416fc2 3605
b423cd4c 3606 con->response->request.status.status_code = IPP_OK;
ef416fc2 3607}
3608
3609
3dfe78b3
MS
3610/*
3611 * 'check_rss_recipient()' - Check that we do not have a duplicate RSS feed URI.
3612 */
3613
3614static int /* O - 1 if OK, 0 if not */
3615check_rss_recipient(
3616 const char *recipient) /* I - Recipient URI */
3617{
3618 cupsd_subscription_t *sub; /* Current subscription */
3619
3620
3621 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
3622 sub;
3623 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
3624 if (sub->recipient)
3625 {
3626 /*
3627 * Compare the URIs up to the first ?...
3628 */
3629
3630 const char *r1, *r2;
3631
3632 for (r1 = recipient, r2 = sub->recipient;
3633 *r1 == *r2 && *r1 && *r1 != '?' && *r2 && *r2 != '?';
3634 r1 ++, r2 ++);
3635
3636 if (*r1 == *r2)
3637 return (0);
3638 }
3639
3640 return (1);
3641}
3642
3643
ef416fc2 3644/*
b423cd4c 3645 * 'check_quotas()' - Check quotas for a printer and user.
ef416fc2 3646 */
3647
6c48a6ca
MS
3648static int /* O - 1 if OK, 0 if forbidden,
3649 -1 if limit reached */
b423cd4c 3650check_quotas(cupsd_client_t *con, /* I - Client connection */
3651 cupsd_printer_t *p) /* I - Printer or class */
ef416fc2 3652{
10d09e33
MS
3653 char username[33], /* Username */
3654 *name; /* Current user name */
b423cd4c 3655 cupsd_quota_t *q; /* Quota data */
7594b224 3656#ifdef HAVE_MBR_UID_TO_UUID
3657 /*
3658 * Use Apple membership APIs which require that all names represent
3659 * valid user account or group records accessible by the server.
3660 */
3661
3662 uuid_t usr_uuid; /* UUID for job requesting user */
3663 uuid_t usr2_uuid; /* UUID for ACL user name entry */
3664 uuid_t grp_uuid; /* UUID for ACL group name entry */
3665 int mbr_err; /* Error from membership function */
3666 int is_member; /* Is this user a member? */
3667#else
3668 /*
3669 * Use standard POSIX APIs for checking users and groups...
3670 */
3671
b423cd4c 3672 struct passwd *pw; /* User password data */
7594b224 3673#endif /* HAVE_MBR_UID_TO_UUID */
ef416fc2 3674
3675
b423cd4c 3676 cupsdLogMessage(CUPSD_LOG_DEBUG2, "check_quotas(%p[%d], %p[%s])",
3677 con, con->http.fd, p, p->name);
ef416fc2 3678
b423cd4c 3679 /*
3680 * Figure out who is printing...
3681 */
3682
3683 strlcpy(username, get_username(con), sizeof(username));
3684
a2326b5b
MS
3685 if ((name = strchr(username, '@')) != NULL)
3686 *name = '\0'; /* Strip @REALM */
3687
b423cd4c 3688 /*
3689 * Check global active job limits for printers and users...
3690 */
3691
3692 if (MaxJobsPerPrinter)
ef416fc2 3693 {
b423cd4c 3694 /*
3695 * Check if there are too many pending jobs on this printer...
3696 */
3697
3698 if (cupsdGetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
3699 {
3700 cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for printer \"%s\"...",
3701 p->name);
6c48a6ca 3702 return (-1);
b423cd4c 3703 }
3704 }
3705
3706 if (MaxJobsPerUser)
3707 {
3708 /*
3709 * Check if there are too many pending jobs for this user...
3710 */
3711
3712 if (cupsdGetUserJobCount(username) >= MaxJobsPerUser)
3713 {
3714 cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for user \"%s\"...",
3715 username);
6c48a6ca 3716 return (-1);
b423cd4c 3717 }
ef416fc2 3718 }
3719
3720 /*
b423cd4c 3721 * Check against users...
ef416fc2 3722 */
3723
10d09e33 3724 if (cupsArrayCount(p->users) == 0 && p->k_limit == 0 && p->page_limit == 0)
b423cd4c 3725 return (1);
3726
10d09e33 3727 if (cupsArrayCount(p->users))
b423cd4c 3728 {
7594b224 3729#ifdef HAVE_MBR_UID_TO_UUID
3730 /*
3731 * Get UUID for job requesting user...
3732 */
3733
3734 if (mbr_user_name_to_uuid((char *)username, usr_uuid))
3735 {
3736 /*
3737 * Unknown user...
3738 */
3739
3740 cupsdLogMessage(CUPSD_LOG_DEBUG,
3741 "check_quotas: UUID lookup failed for user \"%s\"",
3742 username);
3743 cupsdLogMessage(CUPSD_LOG_INFO,
3744 "Denying user \"%s\" access to printer \"%s\" "
3745 "(unknown user)...",
3746 username, p->name);
3747 return (0);
3748 }
3749#else
3750 /*
3751 * Get UID and GID of requesting user...
3752 */
3753
b423cd4c 3754 pw = getpwnam(username);
3755 endpwent();
7594b224 3756#endif /* HAVE_MBR_UID_TO_UUID */
b423cd4c 3757
10d09e33
MS
3758 for (name = (char *)cupsArrayFirst(p->users);
3759 name;
3760 name = (char *)cupsArrayNext(p->users))
3761 if (name[0] == '@')
b423cd4c 3762 {
3763 /*
3764 * Check group membership...
3765 */
3766
7594b224 3767#ifdef HAVE_MBR_UID_TO_UUID
10d09e33 3768 if (name[1] == '#')
c9fc04c6 3769 {
10d09e33 3770 if (uuid_parse(name + 2, grp_uuid))
c9fc04c6
MS
3771 uuid_clear(grp_uuid);
3772 }
10d09e33 3773 else if ((mbr_err = mbr_group_name_to_uuid(name + 1, grp_uuid)) != 0)
7594b224 3774 {
3775 /*
3776 * Invalid ACL entries are ignored for matching; just record a
3777 * warning in the log...
3778 */
3779
3780 cupsdLogMessage(CUPSD_LOG_DEBUG,
3781 "check_quotas: UUID lookup failed for ACL entry "
10d09e33 3782 "\"%s\" (err=%d)", name, mbr_err);
7594b224 3783 cupsdLogMessage(CUPSD_LOG_WARN,
3784 "Access control entry \"%s\" not a valid group name; "
10d09e33 3785 "entry ignored", name);
7594b224 3786 }
7594b224 3787
c9fc04c6
MS
3788 if ((mbr_err = mbr_check_membership(usr_uuid, grp_uuid,
3789 &is_member)) != 0)
3790 {
3791 /*
3792 * At this point, there should be no errors, but check anyways...
7594b224 3793 */
3794
c9fc04c6
MS
3795 cupsdLogMessage(CUPSD_LOG_DEBUG,
3796 "check_quotas: group \"%s\" membership check "
10d09e33 3797 "failed (err=%d)", name + 1, mbr_err);
c9fc04c6 3798 is_member = 0;
7594b224 3799 }
c9fc04c6
MS
3800
3801 /*
3802 * Stop if we found a match...
3803 */
3804
3805 if (is_member)
3806 break;
3807
7594b224 3808#else
10d09e33 3809 if (cupsdCheckGroup(username, pw, name + 1))
b423cd4c 3810 break;
7594b224 3811#endif /* HAVE_MBR_UID_TO_UUID */
3812 }
3813#ifdef HAVE_MBR_UID_TO_UUID
3814 else
3815 {
10d09e33 3816 if (name[0] == '#')
c9fc04c6 3817 {
10d09e33 3818 if (uuid_parse(name + 1, usr2_uuid))
c9fc04c6
MS
3819 uuid_clear(usr2_uuid);
3820 }
10d09e33 3821 else if ((mbr_err = mbr_user_name_to_uuid(name, usr2_uuid)) != 0)
7594b224 3822 {
3823 /*
3824 * Invalid ACL entries are ignored for matching; just record a
3825 * warning in the log...
3826 */
3827
3828 cupsdLogMessage(CUPSD_LOG_DEBUG,
3829 "check_quotas: UUID lookup failed for ACL entry "
10d09e33 3830 "\"%s\" (err=%d)", name, mbr_err);
7594b224 3831 cupsdLogMessage(CUPSD_LOG_WARN,
3832 "Access control entry \"%s\" not a valid user name; "
10d09e33 3833 "entry ignored", name);
7594b224 3834 }
7594b224 3835
d1c13e16 3836 if (!uuid_compare(usr_uuid, usr2_uuid))
c9fc04c6 3837 break;
b423cd4c 3838 }
7594b224 3839#else
88f9aafc 3840 else if (!_cups_strcasecmp(username, name))
b423cd4c 3841 break;
7594b224 3842#endif /* HAVE_MBR_UID_TO_UUID */
b423cd4c 3843
10d09e33 3844 if ((name != NULL) == p->deny_users)
ef416fc2 3845 {
b423cd4c 3846 cupsdLogMessage(CUPSD_LOG_INFO,
3847 "Denying user \"%s\" access to printer \"%s\"...",
3848 username, p->name);
3849 return (0);
ef416fc2 3850 }
b423cd4c 3851 }
ef416fc2 3852
3853 /*
b423cd4c 3854 * Check quotas...
ef416fc2 3855 */
3856
b423cd4c 3857 if (p->k_limit || p->page_limit)
3858 {
3859 if ((q = cupsdUpdateQuota(p, username, 0, 0)) == NULL)
3860 {
3861 cupsdLogMessage(CUPSD_LOG_ERROR,
4d301e69 3862 "Unable to allocate quota data for user \"%s\"",
b423cd4c 3863 username);
bc44d920 3864 return (-1);
b423cd4c 3865 }
ef416fc2 3866
b423cd4c 3867 if ((q->k_count >= p->k_limit && p->k_limit) ||
3868 (q->page_count >= p->page_limit && p->page_limit))
3869 {
3870 cupsdLogMessage(CUPSD_LOG_INFO, "User \"%s\" is over the quota limit...",
3871 username);
bc44d920 3872 return (-1);
b423cd4c 3873 }
3874 }
3875
3876 /*
3877 * If we have gotten this far, we're done!
3878 */
3879
3880 return (1);
ef416fc2 3881}
3882
3883
aaf19ab0
MS
3884/*
3885 * 'close_job()' - Close a multi-file job.
3886 */
3887
3888static void
3889close_job(cupsd_client_t *con, /* I - Client connection */
3890 ipp_attribute_t *uri) /* I - Printer URI */
3891{
3892 cupsd_job_t *job; /* Job */
aaf19ab0
MS
3893 ipp_attribute_t *attr; /* Attribute */
3894 char job_uri[HTTP_MAX_URI],
3895 /* Job URI */
3896 username[256]; /* User name */
3897
3898
3899 cupsdLogMessage(CUPSD_LOG_DEBUG2, "close_job(%p[%d], %s)", con,
3900 con->http.fd, uri->values[0].string.text);
3901
3902 /*
3903 * See if we have a job URI or a printer URI...
3904 */
3905
3906 if (strcmp(uri->name, "printer-uri"))
3907 {
3908 /*
3909 * job-uri is not supported by Close-Job!
3910 */
3911
3912 send_ipp_status(con, IPP_BAD_REQUEST,
3913 _("Close-Job doesn't support the job-uri attribute."));
3914 return;
3915 }
3916
3917 /*
3918 * Got a printer URI; see if we also have a job-id attribute...
3919 */
3920
3921 if ((attr = ippFindAttribute(con->request, "job-id",
3922 IPP_TAG_INTEGER)) == NULL)
3923 {
3924 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 3925 _("Got a printer-uri attribute but no job-id."));
aaf19ab0
MS
3926 return;
3927 }
3928
3929 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
3930 {
3931 /*
3932 * Nope - return a "not found" error...
3933 */
3934
84315f46 3935 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
aaf19ab0
MS
3936 attr->values[0].integer);
3937 return;
3938 }
3939
aaf19ab0
MS
3940 /*
3941 * See if the job is owned by the requesting user...
3942 */
3943
3944 if (!validate_user(job, con, job->username, username, sizeof(username)))
3945 {
3946 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
3947 cupsdFindDest(job->dest));
3948 return;
3949 }
3950
3951 /*
3952 * Add any ending sheet...
3953 */
3954
3955 if (cupsdTimeoutJob(job))
3956 return;
3957
3958 if (job->state_value == IPP_JOB_STOPPED)
3959 {
3960 job->state->values[0].integer = IPP_JOB_PENDING;
3961 job->state_value = IPP_JOB_PENDING;
3962 }
3963 else if (job->state_value == IPP_JOB_HELD)
3964 {
3965 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
3966 IPP_TAG_KEYWORD)) == NULL)
3967 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
3968
3969 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
3970 {
3971 job->state->values[0].integer = IPP_JOB_PENDING;
3972 job->state_value = IPP_JOB_PENDING;
3973 }
3974 }
3975
3976 job->dirty = 1;
3977 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
3978
3979 /*
3980 * Fill in the response info...
3981 */
3982
83e08001 3983 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
f2534050 3984 con->clientname, con->clientport, "/jobs/%d", job->id);
aaf19ab0
MS
3985 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
3986 job_uri);
3987
3988 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
3989
3990 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
3991 job->state_value);
3992
aaf19ab0
MS
3993 con->response->request.status.status_code = IPP_OK;
3994
3995 /*
3996 * Start the job if necessary...
3997 */
3998
3999 cupsdCheckJobs();
4000}
4001
4002
b423cd4c 4003/*
4004 * 'copy_attrs()' - Copy attributes from one request to another.
4005 */
4006
4007static void
4008copy_attrs(ipp_t *to, /* I - Destination request */
4009 ipp_t *from, /* I - Source request */
4010 cups_array_t *ra, /* I - Requested attributes */
4011 ipp_tag_t group, /* I - Group to copy */
10d09e33
MS
4012 int quickcopy, /* I - Do a quick copy? */
4013 cups_array_t *exclude) /* I - Attributes to exclude? */
b423cd4c 4014{
4015 ipp_attribute_t *fromattr; /* Source attribute */
ef416fc2 4016
ef416fc2 4017
b423cd4c 4018 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4019 "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
4020 to, from, ra, group, quickcopy);
ef416fc2 4021
b423cd4c 4022 if (!to || !from)
4023 return;
e00b005a 4024
b423cd4c 4025 for (fromattr = from->attrs; fromattr; fromattr = fromattr->next)
ef416fc2 4026 {
4027 /*
b423cd4c 4028 * Filter attributes as needed...
ef416fc2 4029 */
4030
2abf387c 4031 if ((group != IPP_TAG_ZERO && fromattr->group_tag != group &&
4032 fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
b423cd4c 4033 continue;
4034
db8b865d
MS
4035 if (!strcmp(fromattr->name, "document-password") ||
4036 !strcmp(fromattr->name, "job-authorization-uri") ||
4037 !strcmp(fromattr->name, "job-password") ||
4038 !strcmp(fromattr->name, "job-password-encryption") ||
4039 !strcmp(fromattr->name, "job-printer-uri"))
88f9aafc
MS
4040 continue;
4041
c8fef167 4042 if (exclude &&
10d09e33
MS
4043 (cupsArrayFind(exclude, fromattr->name) ||
4044 cupsArrayFind(exclude, "all")))
4045 {
4046 /*
4047 * We need to exclude this attribute for security reasons; we require the
88f9aafc
MS
4048 * job-id attribute regardless of the security settings for IPP
4049 * conformance.
4050 *
4051 * The job-printer-uri attribute is handled by copy_job_attrs().
10d09e33
MS
4052 *
4053 * Subscription attribute security is handled by copy_subscription_attrs().
4054 */
4055
88f9aafc 4056 if (strcmp(fromattr->name, "job-id"))
10d09e33
MS
4057 continue;
4058 }
4059
b423cd4c 4060 if (!ra || cupsArrayFind(ra, fromattr->name))
d2354e63
MS
4061 {
4062 /*
4063 * Don't send collection attributes by default to IPP/1.x clients
54afec33
MS
4064 * since many do not support collections. Also don't send
4065 * media-col-database unless specifically requested by the client.
d2354e63
MS
4066 */
4067
4068 if (fromattr->value_tag == IPP_TAG_BEGIN_COLLECTION &&
54afec33
MS
4069 !ra &&
4070 (to->request.status.version[0] == 1 ||
4071 !strcmp(fromattr->name, "media-col-database")))
d2354e63
MS
4072 continue;
4073
a2326b5b 4074 ippCopyAttribute(to, fromattr, quickcopy);
d2354e63 4075 }
ef416fc2 4076 }
b423cd4c 4077}
ef416fc2 4078
ef416fc2 4079
b423cd4c 4080/*
4081 * 'copy_banner()' - Copy a banner file to the requests directory for the
4082 * specified job.
4083 */
ef416fc2 4084
b423cd4c 4085static int /* O - Size of banner file in kbytes */
4086copy_banner(cupsd_client_t *con, /* I - Client connection */
4087 cupsd_job_t *job, /* I - Job information */
4088 const char *name) /* I - Name of banner */
4089{
4090 int i; /* Looping var */
4091 int kbytes; /* Size of banner file in kbytes */
4092 char filename[1024]; /* Job filename */
4093 cupsd_banner_t *banner; /* Pointer to banner */
4094 cups_file_t *in; /* Input file */
4095 cups_file_t *out; /* Output file */
4096 int ch; /* Character from file */
4097 char attrname[255], /* Name of attribute */
4098 *s; /* Pointer into name */
4099 ipp_attribute_t *attr; /* Attribute */
ef416fc2 4100
ef416fc2 4101
3dfe78b3
MS
4102 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4103 "copy_banner(con=%p[%d], job=%p[%d], name=\"%s\")",
09a101d6 4104 con, con ? con->http.fd : -1, job, job->id,
4105 name ? name : "(null)");
ef416fc2 4106
b423cd4c 4107 /*
4108 * Find the banner; return if not found or "none"...
4109 */
ef416fc2 4110
b423cd4c 4111 if (!name || !strcmp(name, "none") ||
4112 (banner = cupsdFindBanner(name)) == NULL)
4113 return (0);
ef416fc2 4114
b423cd4c 4115 /*
4116 * Open the banner and job files...
4117 */
ef416fc2 4118
b423cd4c 4119 if (add_file(con, job, banner->filetype, 0))
91c84a35 4120 return (-1);
ef416fc2 4121
b423cd4c 4122 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
4123 job->num_files);
4124 if ((out = cupsFileOpen(filename, "w")) == NULL)
4125 {
4126 cupsdLogMessage(CUPSD_LOG_ERROR,
3dfe78b3 4127 "Unable to create banner job file %s - %s",
b423cd4c 4128 filename, strerror(errno));
4129 job->num_files --;
4130 return (0);
4131 }
4132
4133 fchmod(cupsFileNumber(out), 0640);
4134 fchown(cupsFileNumber(out), RunUser, Group);
ef416fc2 4135
4136 /*
b423cd4c 4137 * Try the localized banner file under the subdirectory...
ef416fc2 4138 */
4139
80ca4592 4140 strlcpy(attrname, job->attrs->attrs->next->values[0].string.text,
b423cd4c 4141 sizeof(attrname));
4142 if (strlen(attrname) > 2 && attrname[2] == '-')
ef416fc2 4143 {
4144 /*
b423cd4c 4145 * Convert ll-cc to ll_CC...
ef416fc2 4146 */
4147
b423cd4c 4148 attrname[2] = '_';
4149 attrname[3] = toupper(attrname[3] & 255);
4150 attrname[4] = toupper(attrname[4] & 255);
4151 }
ef416fc2 4152
b423cd4c 4153 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
4154 attrname, name);
ef416fc2 4155
b423cd4c 4156 if (access(filename, 0) && strlen(attrname) > 2)
ef416fc2 4157 {
4158 /*
b423cd4c 4159 * Wasn't able to find "ll_CC" locale file; try the non-national
4160 * localization banner directory.
ef416fc2 4161 */
4162
b423cd4c 4163 attrname[2] = '\0';
ef416fc2 4164
b423cd4c 4165 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
4166 attrname, name);
ef416fc2 4167 }
b423cd4c 4168
4169 if (access(filename, 0))
ef416fc2 4170 {
4171 /*
b423cd4c 4172 * Use the non-localized banner file.
ef416fc2 4173 */
4174
b423cd4c 4175 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
4176 }
ef416fc2 4177
b423cd4c 4178 if ((in = cupsFileOpen(filename, "r")) == NULL)
4179 {
4180 cupsFileClose(out);
4181 unlink(filename);
4182 cupsdLogMessage(CUPSD_LOG_ERROR,
3dfe78b3 4183 "Unable to open banner template file %s - %s",
b423cd4c 4184 filename, strerror(errno));
4185 job->num_files --;
4186 return (0);
ef416fc2 4187 }
4188
4189 /*
b423cd4c 4190 * Parse the file to the end...
ef416fc2 4191 */
4192
b423cd4c 4193 while ((ch = cupsFileGetChar(in)) != EOF)
4194 if (ch == '{')
4195 {
4196 /*
4197 * Get an attribute name...
4198 */
ef416fc2 4199
b423cd4c 4200 for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
4201 if (!isalpha(ch & 255) && ch != '-' && ch != '?')
4202 break;
4203 else if (s < (attrname + sizeof(attrname) - 1))
4204 *s++ = ch;
4205 else
4206 break;
ef416fc2 4207
b423cd4c 4208 *s = '\0';
4209
4210 if (ch != '}')
4211 {
4212 /*
4213 * Ignore { followed by stuff that is not an attribute name...
4214 */
4215
4216 cupsFilePrintf(out, "{%s%c", attrname, ch);
4217 continue;
4218 }
ef416fc2 4219
ef416fc2 4220 /*
b423cd4c 4221 * See if it is defined...
ef416fc2 4222 */
4223
b423cd4c 4224 if (attrname[0] == '?')
4225 s = attrname + 1;
4226 else
4227 s = attrname;
ef416fc2 4228
b423cd4c 4229 if (!strcmp(s, "printer-name"))
4230 {
4231 cupsFilePuts(out, job->dest);
4232 continue;
ef416fc2 4233 }
b423cd4c 4234 else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
4235 {
4236 /*
4237 * See if we have a leading question mark...
4238 */
ef416fc2 4239
b423cd4c 4240 if (attrname[0] != '?')
4241 {
4242 /*
4243 * Nope, write to file as-is; probably a PostScript procedure...
4244 */
ef416fc2 4245
b423cd4c 4246 cupsFilePrintf(out, "{%s}", attrname);
4247 }
ef416fc2 4248
b423cd4c 4249 continue;
4250 }
ef416fc2 4251
b423cd4c 4252 /*
4253 * Output value(s)...
4254 */
ef416fc2 4255
b423cd4c 4256 for (i = 0; i < attr->num_values; i ++)
4257 {
4258 if (i)
4259 cupsFilePutChar(out, ',');
ef416fc2 4260
b423cd4c 4261 switch (attr->value_tag)
4262 {
4263 case IPP_TAG_INTEGER :
4264 case IPP_TAG_ENUM :
4265 if (!strncmp(s, "time-at-", 8))
dfd5680b 4266 {
0268488e
MS
4267 struct timeval tv; /* Time value */
4268
4269 tv.tv_sec = attr->values[i].integer;
4270 tv.tv_usec = 0;
4271
dfd5680b
MS
4272 cupsFilePuts(out, cupsdGetDateTime(&tv, CUPSD_TIME_STANDARD));
4273 }
b423cd4c 4274 else
4275 cupsFilePrintf(out, "%d", attr->values[i].integer);
4276 break;
ef416fc2 4277
b423cd4c 4278 case IPP_TAG_BOOLEAN :
4279 cupsFilePrintf(out, "%d", attr->values[i].boolean);
4280 break;
ef416fc2 4281
b423cd4c 4282 case IPP_TAG_NOVALUE :
4283 cupsFilePuts(out, "novalue");
4284 break;
ef416fc2 4285
b423cd4c 4286 case IPP_TAG_RANGE :
4287 cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
4288 attr->values[i].range.upper);
4289 break;
fa73b229 4290
b423cd4c 4291 case IPP_TAG_RESOLUTION :
4292 cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
4293 attr->values[i].resolution.yres,
4294 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3e7fe0ca 4295 "dpi" : "dpcm");
b423cd4c 4296 break;
fa73b229 4297
b423cd4c 4298 case IPP_TAG_URI :
4299 case IPP_TAG_STRING :
4300 case IPP_TAG_TEXT :
4301 case IPP_TAG_NAME :
4302 case IPP_TAG_KEYWORD :
4303 case IPP_TAG_CHARSET :
4304 case IPP_TAG_LANGUAGE :
88f9aafc 4305 if (!_cups_strcasecmp(banner->filetype->type, "postscript"))
b423cd4c 4306 {
4307 /*
4308 * Need to quote strings for PS banners...
4309 */
fa73b229 4310
b423cd4c 4311 const char *p;
fa73b229 4312
b423cd4c 4313 for (p = attr->values[i].string.text; *p; p ++)
4314 {
4315 if (*p == '(' || *p == ')' || *p == '\\')
4316 {
4317 cupsFilePutChar(out, '\\');
4318 cupsFilePutChar(out, *p);
4319 }
4320 else if (*p < 32 || *p > 126)
4321 cupsFilePrintf(out, "\\%03o", *p & 255);
4322 else
4323 cupsFilePutChar(out, *p);
4324 }
4325 }
4326 else
4327 cupsFilePuts(out, attr->values[i].string.text);
4328 break;
fa73b229 4329
b423cd4c 4330 default :
4331 break; /* anti-compiler-warning-code */
4332 }
4333 }
4334 }
4335 else if (ch == '\\') /* Quoted char */
4336 {
4337 ch = cupsFileGetChar(in);
fa73b229 4338
b423cd4c 4339 if (ch != '{') /* Only do special handling for \{ */
4340 cupsFilePutChar(out, '\\');
fa73b229 4341
b423cd4c 4342 cupsFilePutChar(out, ch);
4343 }
4344 else
4345 cupsFilePutChar(out, ch);
fa73b229 4346
b423cd4c 4347 cupsFileClose(in);
fa73b229 4348
b423cd4c 4349 kbytes = (cupsFileTell(out) + 1023) / 1024;
fa73b229 4350
b423cd4c 4351 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
4352 IPP_TAG_INTEGER)) != NULL)
4353 attr->values[0].integer += kbytes;
4354
4355 cupsFileClose(out);
4356
4357 return (kbytes);
fa73b229 4358}
4359
4360
4361/*
b423cd4c 4362 * 'copy_file()' - Copy a PPD file or interface script...
fa73b229 4363 */
4364
b423cd4c 4365static int /* O - 0 = success, -1 = error */
4366copy_file(const char *from, /* I - Source file */
4367 const char *to) /* I - Destination file */
fa73b229 4368{
b423cd4c 4369 cups_file_t *src, /* Source file */
4370 *dst; /* Destination file */
4371 int bytes; /* Bytes to read/write */
4372 char buffer[2048]; /* Copy buffer */
fa73b229 4373
4374
b423cd4c 4375 cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_file(\"%s\", \"%s\")", from, to);
4376
fa73b229 4377 /*
b423cd4c 4378 * Open the source and destination file for a copy...
fa73b229 4379 */
4380
b423cd4c 4381 if ((src = cupsFileOpen(from, "rb")) == NULL)
4382 return (-1);
fa73b229 4383
b423cd4c 4384 if ((dst = cupsFileOpen(to, "wb")) == NULL)
4385 {
4386 cupsFileClose(src);
4387 return (-1);
4388 }
fa73b229 4389
b423cd4c 4390 /*
4391 * Copy the source file to the destination...
4392 */
fa73b229 4393
b423cd4c 4394 while ((bytes = cupsFileRead(src, buffer, sizeof(buffer))) > 0)
4395 if (cupsFileWrite(dst, buffer, bytes) < bytes)
4396 {
4397 cupsFileClose(src);
4398 cupsFileClose(dst);
4399 return (-1);
4400 }
fa73b229 4401
b423cd4c 4402 /*
4403 * Close both files and return...
4404 */
fa73b229 4405
b423cd4c 4406 cupsFileClose(src);
fa73b229 4407
b423cd4c 4408 return (cupsFileClose(dst));
4409}
fa73b229 4410
fa73b229 4411
b423cd4c 4412/*
4413 * 'copy_model()' - Copy a PPD model file, substituting default values
4414 * as needed...
4415 */
e00b005a 4416
b423cd4c 4417static int /* O - 0 = success, -1 = error */
4418copy_model(cupsd_client_t *con, /* I - Client connection */
4419 const char *from, /* I - Source file */
4420 const char *to) /* I - Destination file */
4421{
f7deaa1a 4422 fd_set input; /* select() input set */
b423cd4c 4423 struct timeval timeout; /* select() timeout */
bc44d920 4424 int maxfd; /* Max file descriptor for select() */
b423cd4c 4425 char tempfile[1024]; /* Temporary PPD file */
4426 int tempfd; /* Temporary PPD file descriptor */
4427 int temppid; /* Process ID of cups-driverd */
4428 int temppipe[2]; /* Temporary pipes */
4429 char *argv[4], /* Command-line arguments */
4430 *envp[MAX_ENV]; /* Environment */
4431 cups_file_t *src, /* Source file */
4432 *dst; /* Destination file */
2abf387c 4433 ppd_file_t *ppd; /* PPD file */
b423cd4c 4434 int bytes, /* Bytes from pipe */
4435 total; /* Total bytes from pipe */
2abf387c 4436 char buffer[2048]; /* Copy buffer */
b423cd4c 4437 int i; /* Looping var */
4438 char option[PPD_MAX_NAME], /* Option name */
4439 choice[PPD_MAX_NAME]; /* Choice name */
54afec33 4440 ppd_size_t *size; /* Default size */
b423cd4c 4441 int num_defaults; /* Number of default options */
2abf387c 4442 cups_option_t *defaults; /* Default options */
b423cd4c 4443 char cups_protocol[PPD_MAX_LINE];
4444 /* cupsProtocol attribute */
e00b005a 4445
e00b005a 4446
b423cd4c 4447 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4448 "copy_model(con=%p, from=\"%s\", to=\"%s\")",
4449 con, from, to);
e00b005a 4450
b423cd4c 4451 /*
4452 * Run cups-driverd to get the PPD file...
4453 */
e00b005a 4454
b423cd4c 4455 argv[0] = "cups-driverd";
4456 argv[1] = "cat";
4457 argv[2] = (char *)from;
4458 argv[3] = NULL;
e00b005a 4459
b423cd4c 4460 cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
e00b005a 4461
b423cd4c 4462 snprintf(buffer, sizeof(buffer), "%s/daemon/cups-driverd", ServerBin);
4463 snprintf(tempfile, sizeof(tempfile), "%s/%d.ppd", TempDir, con->http.fd);
4464 tempfd = open(tempfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
321d8d57 4465 if (tempfd < 0 || cupsdOpenPipe(temppipe))
b423cd4c 4466 return (-1);
4467
b423cd4c 4468 cupsdLogMessage(CUPSD_LOG_DEBUG,
4469 "copy_model: Running \"cups-driverd cat %s\"...", from);
fa73b229 4470
b423cd4c 4471 if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
38e73f87 4472 -1, -1, 0, DefaultProfile, NULL, &temppid))
fa73b229 4473 {
b423cd4c 4474 close(tempfd);
4475 unlink(tempfile);
a4924f6c 4476
b423cd4c 4477 return (-1);
fa73b229 4478 }
4479
b423cd4c 4480 close(temppipe[1]);
fa73b229 4481
b423cd4c 4482 /*
4483 * Wait up to 30 seconds for the PPD file to be copied...
4484 */
fa73b229 4485
b423cd4c 4486 total = 0;
fa73b229 4487
b423cd4c 4488 if (temppipe[0] > CGIPipes[0])
4489 maxfd = temppipe[0] + 1;
4490 else
4491 maxfd = CGIPipes[0] + 1;
ef416fc2 4492
b423cd4c 4493 for (;;)
4494 {
4495 /*
4496 * See if we have data ready...
4497 */
ef416fc2 4498
f7deaa1a 4499 FD_ZERO(&input);
4500 FD_SET(temppipe[0], &input);
4501 FD_SET(CGIPipes[0], &input);
ef416fc2 4502
b423cd4c 4503 timeout.tv_sec = 30;
4504 timeout.tv_usec = 0;
4505
f7deaa1a 4506 if ((i = select(maxfd, &input, NULL, NULL, &timeout)) < 0)
b423cd4c 4507 {
4508 if (errno == EINTR)
4509 continue;
4510 else
4511 break;
4512 }
4513 else if (i == 0)
ef416fc2 4514 {
4515 /*
b423cd4c 4516 * We have timed out...
ef416fc2 4517 */
4518
b423cd4c 4519 break;
ef416fc2 4520 }
b423cd4c 4521
f7deaa1a 4522 if (FD_ISSET(temppipe[0], &input))
ef416fc2 4523 {
4524 /*
b423cd4c 4525 * Read the PPD file from the pipe, and write it to the PPD file.
ef416fc2 4526 */
4527
b423cd4c 4528 if ((bytes = read(temppipe[0], buffer, sizeof(buffer))) > 0)
4529 {
4530 if (write(tempfd, buffer, bytes) < bytes)
4531 break;
ef416fc2 4532
b423cd4c 4533 total += bytes;
4534 }
4535 else
4536 break;
ef416fc2 4537 }
b423cd4c 4538
f7deaa1a 4539 if (FD_ISSET(CGIPipes[0], &input))
b423cd4c 4540 cupsdUpdateCGI();
ef416fc2 4541 }
4542
b423cd4c 4543 close(temppipe[0]);
4544 close(tempfd);
ef416fc2 4545
b423cd4c 4546 if (!total)
ef416fc2 4547 {
b423cd4c 4548 /*
4549 * No data from cups-deviced...
4550 */
4551
4d301e69 4552 cupsdLogMessage(CUPSD_LOG_ERROR, "copy_model: empty PPD file");
b423cd4c 4553 unlink(tempfile);
4554 return (-1);
ef416fc2 4555 }
4556
9c80ffa2
MS
4557 /*
4558 * Open the source file for a copy...
4559 */
4560
4561 if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
4562 {
4563 unlink(tempfile);
4564 return (-1);
4565 }
4566
b423cd4c 4567 /*
4568 * Read the source file and see what page sizes are supported...
4569 */
fa73b229 4570
9c80ffa2 4571 if ((ppd = _ppdOpen(src, _PPD_LOCALIZATION_NONE)) == NULL)
b423cd4c 4572 {
9c80ffa2 4573 cupsFileClose(src);
b423cd4c 4574 unlink(tempfile);
4575 return (-1);
4576 }
fa73b229 4577
ef416fc2 4578 /*
b423cd4c 4579 * Open the destination (if possible) and set the default options...
ef416fc2 4580 */
4581
b423cd4c 4582 num_defaults = 0;
4583 defaults = NULL;
4584 cups_protocol[0] = '\0';
ef416fc2 4585
b423cd4c 4586 if ((dst = cupsFileOpen(to, "rb")) != NULL)
ef416fc2 4587 {
4588 /*
b423cd4c 4589 * Read all of the default lines from the old PPD...
ef416fc2 4590 */
4591
b423cd4c 4592 while (cupsFileGets(dst, buffer, sizeof(buffer)))
4593 if (!strncmp(buffer, "*Default", 8))
4594 {
4595 /*
4596 * Add the default option...
4597 */
ef416fc2 4598
b423cd4c 4599 if (!ppd_parse_line(buffer, option, sizeof(option),
4600 choice, sizeof(choice)))
2abf387c 4601 {
4602 ppd_option_t *ppdo; /* PPD option */
4603
4604
4605 /*
4606 * Only add the default if the default hasn't already been
4607 * set and the choice exists in the new PPD...
4608 */
4609
4610 if (!cupsGetOption(option, num_defaults, defaults) &&
4611 (ppdo = ppdFindOption(ppd, option)) != NULL &&
4612 ppdFindChoice(ppdo, choice))
4613 num_defaults = cupsAddOption(option, choice, num_defaults,
b423cd4c 4614 &defaults);
2abf387c 4615 }
b423cd4c 4616 }
4617 else if (!strncmp(buffer, "*cupsProtocol:", 14))
4618 strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
ef416fc2 4619
b423cd4c 4620 cupsFileClose(dst);
ef416fc2 4621 }
54afec33 4622 else if ((size = ppdPageSize(ppd, DefaultPaperSize)) != NULL)
ef416fc2 4623 {
b423cd4c 4624 /*
4625 * Add the default media sizes...
b423cd4c 4626 */
4627
54afec33 4628 num_defaults = cupsAddOption("PageSize", size->name,
c5571a1d 4629 num_defaults, &defaults);
54afec33 4630 num_defaults = cupsAddOption("PageRegion", size->name,
c5571a1d 4631 num_defaults, &defaults);
54afec33 4632 num_defaults = cupsAddOption("PaperDimension", size->name,
c5571a1d 4633 num_defaults, &defaults);
54afec33 4634 num_defaults = cupsAddOption("ImageableArea", size->name,
c5571a1d 4635 num_defaults, &defaults);
ef416fc2 4636 }
4637
2abf387c 4638 ppdClose(ppd);
4639
ef416fc2 4640 /*
b423cd4c 4641 * Open the destination file for a copy...
ef416fc2 4642 */
4643
b423cd4c 4644 if ((dst = cupsFileOpen(to, "wb")) == NULL)
ef416fc2 4645 {
2abf387c 4646 cupsFreeOptions(num_defaults, defaults);
b423cd4c 4647 cupsFileClose(src);
4648 unlink(tempfile);
4649 return (-1);
ef416fc2 4650 }
4651
4652 /*
b423cd4c 4653 * Copy the source file to the destination...
ef416fc2 4654 */
4655
9c80ffa2
MS
4656 cupsFileRewind(src);
4657
b423cd4c 4658 while (cupsFileGets(src, buffer, sizeof(buffer)))
ef416fc2 4659 {
b423cd4c 4660 if (!strncmp(buffer, "*Default", 8))
ef416fc2 4661 {
b423cd4c 4662 /*
4663 * Check for an previous default option choice...
4664 */
ef416fc2 4665
b423cd4c 4666 if (!ppd_parse_line(buffer, option, sizeof(option),
4667 choice, sizeof(choice)))
ef416fc2 4668 {
2abf387c 4669 const char *val; /* Default option value */
ef416fc2 4670
2abf387c 4671
4672 if ((val = cupsGetOption(option, num_defaults, defaults)) != NULL)
4673 {
4674 /*
4675 * Substitute the previous choice...
4676 */
4677
4678 snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, val);
4679 }
b423cd4c 4680 }
ef416fc2 4681 }
b423cd4c 4682
4683 cupsFilePrintf(dst, "%s\n", buffer);
ef416fc2 4684 }
4685
b423cd4c 4686 if (cups_protocol[0])
4687 cupsFilePrintf(dst, "%s\n", cups_protocol);
4688
2abf387c 4689 cupsFreeOptions(num_defaults, defaults);
b423cd4c 4690
ef416fc2 4691 /*
b423cd4c 4692 * Close both files and return...
ef416fc2 4693 */
4694
b423cd4c 4695 cupsFileClose(src);
4696
4697 unlink(tempfile);
4698
4699 return (cupsFileClose(dst));
4700}
4701
4702
4703/*
4704 * 'copy_job_attrs()' - Copy job attributes.
4705 */
ef416fc2 4706
b423cd4c 4707static void
4708copy_job_attrs(cupsd_client_t *con, /* I - Client connection */
4709 cupsd_job_t *job, /* I - Job */
10d09e33
MS
4710 cups_array_t *ra, /* I - Requested attributes array */
4711 cups_array_t *exclude) /* I - Private attributes array */
b423cd4c 4712{
4713 char job_uri[HTTP_MAX_URI]; /* Job URI */
ef416fc2 4714
ef416fc2 4715
4716 /*
b423cd4c 4717 * Send the requested attributes for each job...
ef416fc2 4718 */
4719
10d09e33
MS
4720 if (!cupsArrayFind(exclude, "all"))
4721 {
12f89d24
MS
4722 if ((!exclude || !cupsArrayFind(exclude, "number-of-documents")) &&
4723 (!ra || cupsArrayFind(ra, "number-of-documents")))
10d09e33 4724 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
12f89d24 4725 "number-of-documents", job->num_files);
2e4ff8af 4726
10d09e33
MS
4727 if ((!exclude || !cupsArrayFind(exclude, "job-media-progress")) &&
4728 (!ra || cupsArrayFind(ra, "job-media-progress")))
4729 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
4730 "job-media-progress", job->progress);
9a4f8274 4731
10d09e33
MS
4732 if ((!exclude || !cupsArrayFind(exclude, "job-more-info")) &&
4733 (!ra || cupsArrayFind(ra, "job-more-info")))
88f9aafc
MS
4734 {
4735 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "http",
f2534050 4736 NULL, con->clientname, con->clientport, "/jobs/%d",
88f9aafc 4737 job->id);
10d09e33
MS
4738 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4739 "job-more-info", NULL, job_uri);
88f9aafc 4740 }
ef416fc2 4741
10d09e33
MS
4742 if (job->state_value > IPP_JOB_PROCESSING &&
4743 (!exclude || !cupsArrayFind(exclude, "job-preserved")) &&
4744 (!ra || cupsArrayFind(ra, "job-preserved")))
4745 ippAddBoolean(con->response, IPP_TAG_JOB, "job-preserved",
4746 job->num_files > 0);
ef416fc2 4747
10d09e33
MS
4748 if ((!exclude || !cupsArrayFind(exclude, "job-printer-up-time")) &&
4749 (!ra || cupsArrayFind(ra, "job-printer-up-time")))
4750 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
4751 "job-printer-up-time", time(NULL));
4752 }
ef416fc2 4753
88f9aafc
MS
4754 if (!ra || cupsArrayFind(ra, "job-printer-uri"))
4755 {
4756 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
f2534050 4757 con->clientname, con->clientport,
a2326b5b
MS
4758 (job->dtype & CUPS_PRINTER_CLASS) ? "/classes/%s" :
4759 "/printers/%s",
88f9aafc
MS
4760 job->dest);
4761 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4762 "job-printer-uri", NULL, job_uri);
4763 }
4764
b423cd4c 4765 if (!ra || cupsArrayFind(ra, "job-uri"))
88f9aafc
MS
4766 {
4767 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
f2534050 4768 con->clientname, con->clientport, "/jobs/%d",
88f9aafc 4769 job->id);
b423cd4c 4770 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4771 "job-uri", NULL, job_uri);
88f9aafc 4772 }
ef416fc2 4773
10d09e33 4774 copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0, exclude);
b423cd4c 4775}
ef416fc2 4776
ef416fc2 4777
b423cd4c 4778/*
4779 * 'copy_printer_attrs()' - Copy printer attributes.
4780 */
ef416fc2 4781
b423cd4c 4782static void
4783copy_printer_attrs(
4784 cupsd_client_t *con, /* I - Client connection */
4785 cupsd_printer_t *printer, /* I - Printer */
4786 cups_array_t *ra) /* I - Requested attributes array */
4787{
4788 char printer_uri[HTTP_MAX_URI];
4789 /* Printer URI */
7cf5915e
MS
4790 char printer_icons[HTTP_MAX_URI];
4791 /* Printer icons */
b423cd4c 4792 time_t curtime; /* Current time */
4793 int i; /* Looping var */
ef416fc2 4794
ef416fc2 4795
b423cd4c 4796 /*
4797 * Copy the printer attributes to the response using requested-attributes
4798 * and document-format attributes that may be provided by the client.
4799 */
ef416fc2 4800
b423cd4c 4801 curtime = time(NULL);
ef416fc2 4802
5a738aea
MS
4803 if (!ra || cupsArrayFind(ra, "marker-change-time"))
4804 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4805 "marker-change-time", printer->marker_time);
4806
dd1abb6b
MS
4807 if (printer->num_printers > 0 &&
4808 (!ra || cupsArrayFind(ra, "member-uris")))
4809 {
4810 ipp_attribute_t *member_uris; /* member-uris attribute */
4811 cupsd_printer_t *p2; /* Printer in class */
4812 ipp_attribute_t *p2_uri; /* printer-uri-supported for class printer */
4813
4814
4815 if ((member_uris = ippAddStrings(con->response, IPP_TAG_PRINTER,
4816 IPP_TAG_URI, "member-uris",
4817 printer->num_printers, NULL,
4818 NULL)) != NULL)
4819 {
4820 for (i = 0; i < printer->num_printers; i ++)
4821 {
4822 p2 = printer->printers[i];
4823
4824 if ((p2_uri = ippFindAttribute(p2->attrs, "printer-uri-supported",
4825 IPP_TAG_URI)) != NULL)
4826 member_uris->values[i].string.text =
426c6a59 4827 _cupsStrRetain(p2_uri->values[0].string.text);
dd1abb6b
MS
4828 else
4829 {
4830 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri,
f2534050
MS
4831 sizeof(printer_uri), "ipp", NULL, con->clientname,
4832 con->clientport,
dd1abb6b
MS
4833 (p2->type & CUPS_PRINTER_CLASS) ?
4834 "/classes/%s" : "/printers/%s", p2->name);
4835 member_uris->values[i].string.text = _cupsStrAlloc(printer_uri);
4836 }
4837 }
4838 }
4839 }
4840
323c5de1 4841 if (printer->alert && (!ra || cupsArrayFind(ra, "printer-alert")))
4842 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_STRING,
4843 "printer-alert", NULL, printer->alert);
4844
4845 if (printer->alert_description &&
4846 (!ra || cupsArrayFind(ra, "printer-alert-description")))
4847 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
4848 "printer-alert-description", NULL,
4849 printer->alert_description);
4850
b423cd4c 4851 if (!ra || cupsArrayFind(ra, "printer-current-time"))
4852 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
4853 ippTimeToDate(curtime));
ef416fc2 4854
37e7e6e0 4855#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
d9bca400
MS
4856 if (!ra || cupsArrayFind(ra, "printer-dns-sd-name"))
4857 {
4858 if (printer->reg_name)
4859 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4860 "printer-dns-sd-name", NULL, printer->reg_name);
4861 else
4862 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
4863 "printer-dns-sd-name", 0);
4864 }
37e7e6e0 4865#endif /* HAVE_DNSSD || HAVE_AVAHI */
d9bca400 4866
b423cd4c 4867 if (!ra || cupsArrayFind(ra, "printer-error-policy"))
4868 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4869 "printer-error-policy", NULL, printer->error_policy);
ef416fc2 4870
f11a948a
MS
4871 if (!ra || cupsArrayFind(ra, "printer-error-policy-supported"))
4872 {
4873 static const char * const errors[] =/* printer-error-policy-supported values */
4874 {
4875 "abort-job",
4876 "retry-current-job",
4877 "retry-job",
4878 "stop-printer"
4879 };
4880
a2326b5b 4881 if (printer->type & CUPS_PRINTER_CLASS)
f11a948a
MS
4882 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
4883 "printer-error-policy-supported", NULL, "retry-current-job");
4884 else
4885 ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
4886 "printer-error-policy-supported",
4887 sizeof(errors) / sizeof(errors[0]), NULL, errors);
4888 }
4889
7cf5915e
MS
4890 if (!ra || cupsArrayFind(ra, "printer-icons"))
4891 {
4892 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_icons, sizeof(printer_icons),
f2534050 4893 "http", NULL, con->clientname, con->clientport,
7cf5915e
MS
4894 "/icons/%s.png", printer->name);
4895 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons",
4896 NULL, printer_icons);
4897 cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-icons=\"%s\"", printer_icons);
4898 }
4899
b423cd4c 4900 if (!ra || cupsArrayFind(ra, "printer-is-accepting-jobs"))
4901 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
4902 printer->accepting);
ef416fc2 4903
b423cd4c 4904 if (!ra || cupsArrayFind(ra, "printer-is-shared"))
4905 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared",
4906 printer->shared);
ef416fc2 4907
a2326b5b 4908 if (!ra || cupsArrayFind(ra, "printer-more-info"))
58dc1933
MS
4909 {
4910 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
f2534050 4911 "http", NULL, con->clientname, con->clientport,
58dc1933
MS
4912 (printer->type & CUPS_PRINTER_CLASS) ?
4913 "/classes/%s" : "/printers/%s", printer->name);
4914 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
4915 "printer-more-info", NULL, printer_uri);
4916 }
4917
b423cd4c 4918 if (!ra || cupsArrayFind(ra, "printer-op-policy"))
4919 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4920 "printer-op-policy", NULL, printer->op_policy);
4921
4922 if (!ra || cupsArrayFind(ra, "printer-state"))
4923 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
4924 printer->state);
4925
4926 if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
4927 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4928 "printer-state-change-time", printer->state_time);
f7deaa1a 4929
b423cd4c 4930 if (!ra || cupsArrayFind(ra, "printer-state-message"))
4931 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
4932 "printer-state-message", NULL, printer->state_message);
ef416fc2 4933
b423cd4c 4934 if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
4935 add_printer_state_reasons(con, printer);
ef416fc2 4936
b423cd4c 4937 if (!ra || cupsArrayFind(ra, "printer-type"))
4938 {
4939 int type; /* printer-type value */
ef416fc2 4940
ef416fc2 4941 /*
b423cd4c 4942 * Add the CUPS-specific printer-type attribute...
ef416fc2 4943 */
4944
09a101d6 4945 type = printer->type;
b423cd4c 4946
4947 if (printer == DefaultPrinter)
4948 type |= CUPS_PRINTER_DEFAULT;
4949
4950 if (!printer->accepting)
4951 type |= CUPS_PRINTER_REJECTING;
4952
4953 if (!printer->shared)
4954 type |= CUPS_PRINTER_NOT_SHARED;
4955
4956 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-type",
4957 type);
ef416fc2 4958 }
ef416fc2 4959
b423cd4c 4960 if (!ra || cupsArrayFind(ra, "printer-up-time"))
4961 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4962 "printer-up-time", curtime);
ef416fc2 4963
a2326b5b 4964 if (!ra || cupsArrayFind(ra, "printer-uri-supported"))
ef416fc2 4965 {
b423cd4c 4966 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
f2534050 4967 "ipp", NULL, con->clientname, con->clientport,
8ca02f3c 4968 (printer->type & CUPS_PRINTER_CLASS) ?
4969 "/classes/%s" : "/printers/%s", printer->name);
b423cd4c 4970 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
4971 "printer-uri-supported", NULL, printer_uri);
4972 cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-uri-supported=\"%s\"",
4973 printer_uri);
4974 }
ef416fc2 4975
b423cd4c 4976 if (!ra || cupsArrayFind(ra, "queued-job-count"))
4977 add_queued_job_count(con, printer);
ef416fc2 4978
10d09e33 4979 copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0, NULL);
61cf44e2 4980 if (printer->ppd_attrs)
10d09e33
MS
4981 copy_attrs(con->response, printer->ppd_attrs, ra, IPP_TAG_ZERO, 0, NULL);
4982 copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY, NULL);
b423cd4c 4983}
ef416fc2 4984
ef416fc2 4985
b423cd4c 4986/*
4987 * 'copy_subscription_attrs()' - Copy subscription attributes.
4988 */
ef416fc2 4989
b423cd4c 4990static void
4991copy_subscription_attrs(
4992 cupsd_client_t *con, /* I - Client connection */
4993 cupsd_subscription_t *sub, /* I - Subscription */
10d09e33
MS
4994 cups_array_t *ra, /* I - Requested attributes array */
4995 cups_array_t *exclude) /* I - Private attributes array */
b423cd4c 4996{
4997 ipp_attribute_t *attr; /* Current attribute */
4998 char printer_uri[HTTP_MAX_URI];
4999 /* Printer URI */
5000 int count; /* Number of events */
5001 unsigned mask; /* Current event mask */
5002 const char *name; /* Current event name */
ef416fc2 5003
ef416fc2 5004
10d09e33
MS
5005 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5006 "copy_subscription_attrs(con=%p, sub=%p, ra=%p, exclude=%p)",
5007 con, sub, ra, exclude);
5008
b423cd4c 5009 /*
5010 * Copy the subscription attributes to the response using the
5011 * requested-attributes attribute that may be provided by the client.
5012 */
ef416fc2 5013
10d09e33 5014 if (!exclude || !cupsArrayFind(exclude, "all"))
b423cd4c 5015 {
10d09e33
MS
5016 if ((!exclude || !cupsArrayFind(exclude, "notify-events")) &&
5017 (!ra || cupsArrayFind(ra, "notify-events")))
b423cd4c 5018 {
10d09e33 5019 cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_subscription_attrs: notify-events");
ef416fc2 5020
10d09e33
MS
5021 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
5022 {
5023 /*
5024 * Simple event list...
5025 */
5026
5027 ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
5028 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
5029 "notify-events", NULL, name);
5030 }
5031 else
5032 {
5033 /*
5034 * Complex event list...
5035 */
ef416fc2 5036
10d09e33
MS
5037 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
5038 if (sub->mask & mask)
5039 count ++;
ef416fc2 5040
10d09e33
MS
5041 attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
5042 (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
5043 "notify-events", count, NULL, NULL);
ef416fc2 5044
10d09e33
MS
5045 for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
5046 if (sub->mask & mask)
5047 {
5048 attr->values[count].string.text =
5049 (char *)cupsdEventName((cupsd_eventmask_t)mask);
ef416fc2 5050
10d09e33
MS
5051 count ++;
5052 }
5053 }
ef416fc2 5054 }
10d09e33
MS
5055
5056 if ((!exclude || !cupsArrayFind(exclude, "notify-lease-duration")) &&
5057 (!sub->job && (!ra || cupsArrayFind(ra, "notify-lease-duration"))))
5058 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5059 "notify-lease-duration", sub->lease);
5060
5061 if ((!exclude || !cupsArrayFind(exclude, "notify-recipient-uri")) &&
5062 (sub->recipient && (!ra || cupsArrayFind(ra, "notify-recipient-uri"))))
5063 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5064 "notify-recipient-uri", NULL, sub->recipient);
5065 else if ((!exclude || !cupsArrayFind(exclude, "notify-pull-method")) &&
5066 (!ra || cupsArrayFind(ra, "notify-pull-method")))
5067 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
5068 "notify-pull-method", NULL, "ippget");
5069
5070 if ((!exclude || !cupsArrayFind(exclude, "notify-subscriber-user-name")) &&
5071 (!ra || cupsArrayFind(ra, "notify-subscriber-user-name")))
5072 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
5073 "notify-subscriber-user-name", NULL, sub->owner);
5074
5075 if ((!exclude || !cupsArrayFind(exclude, "notify-time-interval")) &&
5076 (!ra || cupsArrayFind(ra, "notify-time-interval")))
5077 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5078 "notify-time-interval", sub->interval);
5079
5080 if (sub->user_data_len > 0 &&
5081 (!exclude || !cupsArrayFind(exclude, "notify-user-data")) &&
5082 (!ra || cupsArrayFind(ra, "notify-user-data")))
5083 ippAddOctetString(con->response, IPP_TAG_SUBSCRIPTION, "notify-user-data",
5084 sub->user_data, sub->user_data_len);
b423cd4c 5085 }
ef416fc2 5086
b423cd4c 5087 if (sub->job && (!ra || cupsArrayFind(ra, "notify-job-id")))
5088 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5089 "notify-job-id", sub->job->id);
ef416fc2 5090
b423cd4c 5091 if (sub->dest && (!ra || cupsArrayFind(ra, "notify-printer-uri")))
5092 {
5093 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
f2534050 5094 "ipp", NULL, con->clientname, con->clientport,
b423cd4c 5095 "/printers/%s", sub->dest->name);
5096 ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5097 "notify-printer-uri", NULL, printer_uri);
ef416fc2 5098 }
ef416fc2 5099
b423cd4c 5100 if (!ra || cupsArrayFind(ra, "notify-subscription-id"))
5101 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5102 "notify-subscription-id", sub->id);
b423cd4c 5103}
ef416fc2 5104
ef416fc2 5105
b423cd4c 5106/*
5107 * 'create_job()' - Print a file to a printer or class.
5108 */
5109
5110static void
5111create_job(cupsd_client_t *con, /* I - Client connection */
5112 ipp_attribute_t *uri) /* I - Printer URI */
5113{
a29fd7dd 5114 int i; /* Looping var */
f7deaa1a 5115 cupsd_printer_t *printer; /* Printer */
5116 cupsd_job_t *job; /* New job */
a29fd7dd
MS
5117 static const char * const forbidden_attrs[] =
5118 { /* List of forbidden attributes */
5119 "compression",
5120 "document-format",
5121 "document-name",
5122 "document-natural-language"
5123 };
b423cd4c 5124
ef416fc2 5125
b423cd4c 5126 cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
5127 con->http.fd, uri->values[0].string.text);
ef416fc2 5128
f7deaa1a 5129 /*
5130 * Is the destination valid?
5131 */
5132
5133 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
5134 {
5135 /*
5136 * Bad URI...
5137 */
5138
5139 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 5140 _("The printer or class does not exist."));
f7deaa1a 5141 return;
5142 }
5143
a29fd7dd
MS
5144 /*
5145 * Check for invalid Create-Job attributes and log a warning or error depending
5146 * on whether cupsd is running in "strict conformance" mode...
5147 */
5148
5149 for (i = 0;
5150 i < (int)(sizeof(forbidden_attrs) / sizeof(forbidden_attrs[0]));
5151 i ++)
5152 if (ippFindAttribute(con->request, forbidden_attrs[i], IPP_TAG_ZERO))
5153 {
5154 if (StrictConformance)
5155 {
5156 send_ipp_status(con, IPP_BAD_REQUEST,
5157 _("The '%s' operation attribute cannot be supplied in a "
5158 "Create-Job request."), forbidden_attrs[i]);
5159 return;
5160 }
5161
5162 cupsdLogMessage(CUPSD_LOG_WARN,
5163 "Unexpected '%s' operation attribute in a Create-Job "
5164 "request.", forbidden_attrs[i]);
5165 }
5166
ef416fc2 5167 /*
b423cd4c 5168 * Create the job object...
ef416fc2 5169 */
5170
f7deaa1a 5171 if ((job = add_job(con, printer, NULL)) == NULL)
b423cd4c 5172 return;
ef416fc2 5173
09a101d6 5174 job->pending_timeout = 1;
5175
ef416fc2 5176 /*
5177 * Save and log the job...
5178 */
f7deaa1a 5179
75bd9771
MS
5180 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
5181 job->dest, job->username);
ef416fc2 5182}
5183
5184
5185/*
5186 * 'create_requested_array()' - Create an array for the requested-attributes.
5187 */
5188
5189static cups_array_t * /* O - Array of attributes or NULL */
5190create_requested_array(ipp_t *request) /* I - IPP request */
5191{
ef416fc2 5192 cups_array_t *ra; /* Requested attributes array */
ef416fc2 5193
5194
5195 /*
db8b865d 5196 * Create the array for standard attributes...
ef416fc2 5197 */
5198
db8b865d 5199 ra = ippCreateRequestedArray(request);
ef416fc2 5200
5201 /*
db8b865d 5202 * Add CUPS defaults as needed...
ef416fc2 5203 */
5204
db8b865d 5205 if (cupsArrayFind(ra, "printer-defaults"))
ef416fc2 5206 {
db8b865d
MS
5207 /*
5208 * Include user-set defaults...
5209 */
ef416fc2 5210
db8b865d 5211 char *name; /* Option name */
f7deaa1a 5212
db8b865d 5213 cupsArrayRemove(ra, "printer-defaults");
f7deaa1a 5214
db8b865d
MS
5215 for (name = (char *)cupsArrayFirst(CommonDefaults);
5216 name;
5217 name = (char *)cupsArrayNext(CommonDefaults))
5218 if (!cupsArrayFind(ra, name))
f7deaa1a 5219 cupsArrayAdd(ra, name);
ef416fc2 5220 }
5221
5222 return (ra);
5223}
5224
5225
5226/*
71f63681 5227 * 'create_subscriptions()' - Create one or more notification subscriptions.
ef416fc2 5228 */
5229
5230static void
71f63681 5231create_subscriptions(
ef416fc2 5232 cupsd_client_t *con, /* I - Client connection */
5233 ipp_attribute_t *uri) /* I - Printer URI */
5234{
5235 http_status_t status; /* Policy status */
5236 int i; /* Looping var */
5237 ipp_attribute_t *attr; /* Current attribute */
bc44d920 5238 cups_ptype_t dtype; /* Destination type (printer/class) */
ed486911 5239 char scheme[HTTP_MAX_URI],
5240 /* Scheme portion of URI */
ef416fc2 5241 userpass[HTTP_MAX_URI],
5242 /* Username portion of URI */
5243 host[HTTP_MAX_URI],
5244 /* Host portion of URI */
5245 resource[HTTP_MAX_URI];
5246 /* Resource portion of URI */
5247 int port; /* Port portion of URI */
5248 cupsd_printer_t *printer; /* Printer/class */
5249 cupsd_job_t *job; /* Job */
5250 int jobid; /* Job ID */
5251 cupsd_subscription_t *sub; /* Subscription object */
bc44d920 5252 const char *username, /* requesting-user-name or
5253 authenticated username */
ef416fc2 5254 *recipient, /* notify-recipient-uri */
5255 *pullmethod; /* notify-pull-method */
5256 ipp_attribute_t *user_data; /* notify-user-data */
5257 int interval, /* notify-time-interval */
5258 lease; /* notify-lease-duration */
5259 unsigned mask; /* notify-events */
f7deaa1a 5260 ipp_attribute_t *notify_events,/* notify-events(-default) */
5261 *notify_lease; /* notify-lease-duration(-default) */
ef416fc2 5262
5263
bd7854cb 5264#ifdef DEBUG
5265 for (attr = con->request->attrs; attr; attr = attr->next)
5266 {
5267 if (attr->group_tag != IPP_TAG_ZERO)
3dfe78b3 5268 cupsdLogMessage(CUPSD_LOG_DEBUG2, "g%04x v%04x %s", attr->group_tag,
bd7854cb 5269 attr->value_tag, attr->name);
5270 else
3dfe78b3 5271 cupsdLogMessage(CUPSD_LOG_DEBUG2, "----SEP----");
bd7854cb 5272 }
5273#endif /* DEBUG */
5274
ef416fc2 5275 /*
5276 * Is the destination valid?
5277 */
5278
71f63681 5279 cupsdLogMessage(CUPSD_LOG_DEBUG, "create_subscriptions(con=%p(%d), uri=\"%s\")", con, con->http.fd, uri->values[0].string.text);
fa73b229 5280
ed486911 5281 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
5282 sizeof(scheme), userpass, sizeof(userpass), host,
a4d04587 5283 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 5284
5285 if (!strcmp(resource, "/"))
5286 {
ef416fc2 5287 dtype = (cups_ptype_t)0;
5288 printer = NULL;
5289 }
5290 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
5291 {
ef416fc2 5292 dtype = (cups_ptype_t)0;
5293 printer = NULL;
5294 }
5295 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
5296 {
ef416fc2 5297 dtype = CUPS_PRINTER_CLASS;
5298 printer = NULL;
5299 }
f7deaa1a 5300 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 5301 {
5302 /*
5303 * Bad URI...
5304 */
5305
5306 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 5307 _("The printer or class does not exist."));
ef416fc2 5308 return;
5309 }
5310
5311 /*
5312 * Check policy...
5313 */
5314
5315 if (printer)
5316 {
f7deaa1a 5317 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
5318 NULL)) != HTTP_OK)
ef416fc2 5319 {
f899b121 5320 send_http_error(con, status, printer);
ef416fc2 5321 return;
5322 }
5323 }
5324 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5325 {
f899b121 5326 send_http_error(con, status, NULL);
ef416fc2 5327 return;
5328 }
5329
5330 /*
5331 * Get the user that is requesting the subscription...
5332 */
5333
e00b005a 5334 username = get_username(con);
ef416fc2 5335
5336 /*
5337 * Find the first subscription group attribute; return if we have
5338 * none...
5339 */
5340
5341 for (attr = con->request->attrs; attr; attr = attr->next)
5342 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
5343 break;
5344
5345 if (!attr)
5346 {
5347 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 5348 _("No subscription attributes in request."));
ef416fc2 5349 return;
5350 }
5351
5352 /*
5353 * Process the subscription attributes in the request...
5354 */
5355
fa73b229 5356 con->response->request.status.status_code = IPP_BAD_REQUEST;
5357
ef416fc2 5358 while (attr)
5359 {
5360 recipient = NULL;
5361 pullmethod = NULL;
5362 user_data = NULL;
5363 interval = 0;
5364 lease = DefaultLeaseDuration;
5365 jobid = 0;
5366 mask = CUPSD_EVENT_NONE;
5367
f7deaa1a 5368 if (printer)
5369 {
5370 notify_events = ippFindAttribute(printer->attrs, "notify-events-default",
5371 IPP_TAG_KEYWORD);
5372 notify_lease = ippFindAttribute(printer->attrs,
5373 "notify-lease-duration-default",
5374 IPP_TAG_INTEGER);
5375
5376 if (notify_lease)
5377 lease = notify_lease->values[0].integer;
5378 }
5379 else
5380 {
5381 notify_events = NULL;
5382 notify_lease = NULL;
5383 }
5384
ef416fc2 5385 while (attr && attr->group_tag != IPP_TAG_ZERO)
5386 {
7594b224 5387 if (!strcmp(attr->name, "notify-recipient-uri") &&
ef416fc2 5388 attr->value_tag == IPP_TAG_URI)
ed486911 5389 {
5390 /*
5391 * Validate the recipient scheme against the ServerBin/notifier
5392 * directory...
5393 */
5394
5395 char notifier[1024]; /* Notifier filename */
5396
5397
ef416fc2 5398 recipient = attr->values[0].string.text;
ed486911 5399
8ca02f3c 5400 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
5401 scheme, sizeof(scheme), userpass, sizeof(userpass),
5402 host, sizeof(host), &port,
5403 resource, sizeof(resource)) < HTTP_URI_OK)
ed486911 5404 {
5405 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 5406 _("Bad notify-recipient-uri \"%s\"."), recipient);
ed486911 5407 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5408 "notify-status-code", IPP_URI_SCHEME);
5409 return;
5410 }
5411
5412 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
5413 scheme);
5414 if (access(notifier, X_OK))
5415 {
5416 send_ipp_status(con, IPP_NOT_POSSIBLE,
bc44d920 5417 _("notify-recipient-uri URI \"%s\" uses unknown "
84315f46 5418 "scheme."), recipient);
ed486911 5419 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5420 "notify-status-code", IPP_URI_SCHEME);
5421 return;
5422 }
3dfe78b3
MS
5423
5424 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
5425 {
5426 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 5427 _("notify-recipient-uri URI \"%s\" is already used."),
3dfe78b3
MS
5428 recipient);
5429 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5430 "notify-status-code", IPP_ATTRIBUTES);
5431 return;
5432 }
ed486911 5433 }
ef416fc2 5434 else if (!strcmp(attr->name, "notify-pull-method") &&
5435 attr->value_tag == IPP_TAG_KEYWORD)
ed486911 5436 {
ef416fc2 5437 pullmethod = attr->values[0].string.text;
ed486911 5438
5439 if (strcmp(pullmethod, "ippget"))
5440 {
5441 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 5442 _("Bad notify-pull-method \"%s\"."), pullmethod);
ed486911 5443 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5444 "notify-status-code", IPP_ATTRIBUTES);
5445 return;
5446 }
5447 }
ef416fc2 5448 else if (!strcmp(attr->name, "notify-charset") &&
5449 attr->value_tag == IPP_TAG_CHARSET &&
5450 strcmp(attr->values[0].string.text, "us-ascii") &&
5451 strcmp(attr->values[0].string.text, "utf-8"))
5452 {
5453 send_ipp_status(con, IPP_CHARSET,
84315f46 5454 _("Character set \"%s\" not supported."),
ef416fc2 5455 attr->values[0].string.text);
5456 return;
5457 }
5458 else if (!strcmp(attr->name, "notify-natural-language") &&
5459 (attr->value_tag != IPP_TAG_LANGUAGE ||
5460 strcmp(attr->values[0].string.text, DefaultLanguage)))
5461 {
5462 send_ipp_status(con, IPP_CHARSET,
84315f46 5463 _("Language \"%s\" not supported."),
ef416fc2 5464 attr->values[0].string.text);
5465 return;
5466 }
5467 else if (!strcmp(attr->name, "notify-user-data") &&
5468 attr->value_tag == IPP_TAG_STRING)
5469 {
5470 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
5471 {
5472 send_ipp_status(con, IPP_REQUEST_VALUE,
5473 _("The notify-user-data value is too large "
84315f46 5474 "(%d > 63 octets)."),
ef416fc2 5475 attr->values[0].unknown.length);
5476 return;
5477 }
5478
5479 user_data = attr;
5480 }
5481 else if (!strcmp(attr->name, "notify-events") &&
5482 attr->value_tag == IPP_TAG_KEYWORD)
f7deaa1a 5483 notify_events = attr;
ef416fc2 5484 else if (!strcmp(attr->name, "notify-lease-duration") &&
5485 attr->value_tag == IPP_TAG_INTEGER)
5486 lease = attr->values[0].integer;
5487 else if (!strcmp(attr->name, "notify-time-interval") &&
5488 attr->value_tag == IPP_TAG_INTEGER)
5489 interval = attr->values[0].integer;
5490 else if (!strcmp(attr->name, "notify-job-id") &&
5491 attr->value_tag == IPP_TAG_INTEGER)
5492 jobid = attr->values[0].integer;
5493
5494 attr = attr->next;
5495 }
5496
f7deaa1a 5497 if (notify_events)
5498 {
5499 for (i = 0; i < notify_events->num_values; i ++)
5500 mask |= cupsdEventValue(notify_events->values[i].string.text);
5501 }
5502
bd7854cb 5503 if (recipient)
5504 cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
5505 if (pullmethod)
5506 cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
5507 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
5508 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-time-interval=%d", interval);
fa73b229 5509
ef416fc2 5510 if (!recipient && !pullmethod)
5511 break;
5512
5513 if (mask == CUPSD_EVENT_NONE)
5514 {
5515 if (jobid)
5516 mask = CUPSD_EVENT_JOB_COMPLETED;
5517 else if (printer)
5518 mask = CUPSD_EVENT_PRINTER_STATE_CHANGED;
5519 else
5520 {
5521 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 5522 _("notify-events not specified."));
ef416fc2 5523 return;
5524 }
5525 }
5526
bd7854cb 5527 if (MaxLeaseDuration && (lease == 0 || lease > MaxLeaseDuration))
ef416fc2 5528 {
5529 cupsdLogMessage(CUPSD_LOG_INFO,
71f63681 5530 "create_subscriptions: Limiting notify-lease-duration to "
ef416fc2 5531 "%d seconds.",
5532 MaxLeaseDuration);
5533 lease = MaxLeaseDuration;
5534 }
5535
5536 if (jobid)
5537 {
5538 if ((job = cupsdFindJob(jobid)) == NULL)
5539 {
84315f46
MS
5540 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
5541 jobid);
ef416fc2 5542 return;
5543 }
5544 }
5545 else
5546 job = NULL;
5547
52f6f666
MS
5548 if ((sub = cupsdAddSubscription(mask, printer, job, recipient, 0)) == NULL)
5549 {
5550 send_ipp_status(con, IPP_TOO_MANY_SUBSCRIPTIONS,
5551 _("There are too many subscriptions."));
5552 return;
5553 }
ef416fc2 5554
fa73b229 5555 if (job)
84315f46 5556 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for job %d.",
fa73b229 5557 sub->id, job->id);
5558 else if (printer)
5559 cupsdLogMessage(CUPSD_LOG_DEBUG,
84315f46 5560 "Added subscription #%d for printer \"%s\".",
fa73b229 5561 sub->id, printer->name);
5562 else
84315f46 5563 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for server.",
fa73b229 5564 sub->id);
5565
ef416fc2 5566 sub->interval = interval;
5567 sub->lease = lease;
bd7854cb 5568 sub->expire = lease ? time(NULL) + lease : 0;
ef416fc2 5569
5570 cupsdSetString(&sub->owner, username);
5571
5572 if (user_data)
5573 {
5574 sub->user_data_len = user_data->values[0].unknown.length;
5575 memcpy(sub->user_data, user_data->values[0].unknown.data,
5576 sub->user_data_len);
5577 }
5578
5579 ippAddSeparator(con->response);
5580 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5581 "notify-subscription-id", sub->id);
5582
fa73b229 5583 con->response->request.status.status_code = IPP_OK;
5584
ef416fc2 5585 if (attr)
5586 attr = attr->next;
5587 }
5588
3dfe78b3 5589 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
ef416fc2 5590}
5591
5592
5593/*
5594 * 'delete_printer()' - Remove a printer or class from the system.
5595 */
5596
5597static void
5598delete_printer(cupsd_client_t *con, /* I - Client connection */
5599 ipp_attribute_t *uri) /* I - URI of printer or class */
5600{
5601 http_status_t status; /* Policy status */
bc44d920 5602 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 5603 cupsd_printer_t *printer; /* Printer/class */
5604 char filename[1024]; /* Script/PPD filename */
5605
5606
5607 cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", con,
5608 con->http.fd, uri->values[0].string.text);
5609
5610 /*
5611 * Do we have a valid URI?
5612 */
5613
f7deaa1a 5614 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 5615 {
5616 /*
5617 * Bad URI...
5618 */
5619
5620 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 5621 _("The printer or class does not exist."));
ef416fc2 5622 return;
5623 }
5624
5625 /*
5626 * Check policy...
5627 */
5628
5629 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5630 {
f899b121 5631 send_http_error(con, status, NULL);
ef416fc2 5632 return;
5633 }
5634
5635 /*
5636 * Remove old jobs...
5637 */
5638
f7deaa1a 5639 cupsdCancelJobs(printer->name, NULL, 1);
ef416fc2 5640
5641 /*
5642 * Remove old subscriptions and send a "deleted printer" event...
5643 */
5644
5645 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
5646 "%s \"%s\" deleted by \"%s\".",
5647 (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
f7deaa1a 5648 printer->name, get_username(con));
ef416fc2 5649
5650 cupsdExpireSubscriptions(printer, NULL);
f7deaa1a 5651
ef416fc2 5652 /*
5653 * Remove any old PPD or script files...
5654 */
5655
f7deaa1a 5656 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot,
5657 printer->name);
ef416fc2 5658 unlink(filename);
4ecc3203
MS
5659 snprintf(filename, sizeof(filename), "%s/interfaces/%s.O", ServerRoot,
5660 printer->name);
5661 unlink(filename);
ef416fc2 5662
f7deaa1a 5663 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
5664 printer->name);
ef416fc2 5665 unlink(filename);
4ecc3203
MS
5666 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd.O", ServerRoot,
5667 printer->name);
5668 unlink(filename);
ef416fc2 5669
7cf5915e 5670 snprintf(filename, sizeof(filename), "%s/%s.png", CacheDir, printer->name);
61cf44e2
MS
5671 unlink(filename);
5672
f14324a7 5673 snprintf(filename, sizeof(filename), "%s/%s.data", CacheDir, printer->name);
54afec33
MS
5674 unlink(filename);
5675
568fa3fa
MS
5676 /*
5677 * Unregister color profiles...
5678 */
5679
a29fd7dd 5680 cupsdUnregisterColor(printer);
568fa3fa 5681
ef416fc2 5682 if (dtype & CUPS_PRINTER_CLASS)
5683 {
f7deaa1a 5684 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".",
5685 printer->name, get_username(con));
ef416fc2 5686
5687 cupsdDeletePrinter(printer, 0);
3dfe78b3 5688 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
ef416fc2 5689 }
5690 else
5691 {
f7deaa1a 5692 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".",
5693 printer->name, get_username(con));
ef416fc2 5694
f8b3a85b
MS
5695 if (cupsdDeletePrinter(printer, 0))
5696 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
5697
3dfe78b3 5698 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 5699 }
5700
3dfe78b3 5701 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
ef416fc2 5702
5703 /*
5704 * Return with no errors...
5705 */
5706
5707 con->response->request.status.status_code = IPP_OK;
5708}
5709
5710
5711/*
5712 * 'get_default()' - Get the default destination.
5713 */
5714
5715static void
5716get_default(cupsd_client_t *con) /* I - Client connection */
5717{
fa73b229 5718 http_status_t status; /* Policy status */
5719 cups_array_t *ra; /* Requested attributes array */
ef416fc2 5720
5721
5722 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->http.fd);
5723
5724 /*
5725 * Check policy...
5726 */
5727
5728 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5729 {
f899b121 5730 send_http_error(con, status, NULL);
ef416fc2 5731 return;
5732 }
5733
fa73b229 5734 if (DefaultPrinter)
ef416fc2 5735 {
fa73b229 5736 ra = create_requested_array(con->request);
ef416fc2 5737
fa73b229 5738 copy_printer_attrs(con, DefaultPrinter, ra);
ef416fc2 5739
fa73b229 5740 cupsArrayDelete(ra);
ef416fc2 5741
fa73b229 5742 con->response->request.status.status_code = IPP_OK;
ef416fc2 5743 }
5744 else
84315f46 5745 send_ipp_status(con, IPP_NOT_FOUND, _("No default printer."));
ef416fc2 5746}
5747
5748
5749/*
5750 * 'get_devices()' - Get the list of available devices on the local system.
5751 */
5752
5753static void
5754get_devices(cupsd_client_t *con) /* I - Client connection */
5755{
5756 http_status_t status; /* Policy status */
ae71f5de
MS
5757 ipp_attribute_t *limit, /* limit attribute */
5758 *timeout, /* timeout attribute */
5759 *requested, /* requested-attributes attribute */
ed6e7faf
MS
5760 *exclude, /* exclude-schemes attribute */
5761 *include; /* include-schemes attribute */
ef416fc2 5762 char command[1024], /* cups-deviced command */
ed6e7faf 5763 options[2048], /* Options to pass to command */
ae71f5de 5764 requested_str[256],
89d46774 5765 /* String for requested attributes */
ed6e7faf
MS
5766 exclude_str[512],
5767 /* String for excluded schemes */
5768 include_str[512];
5769 /* String for included schemes */
ef416fc2 5770
5771
5772 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->http.fd);
5773
5774 /*
5775 * Check policy...
5776 */
5777
5778 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5779 {
f899b121 5780 send_http_error(con, status, NULL);
ef416fc2 5781 return;
5782 }
5783
5784 /*
5785 * Run cups-deviced command with the given options...
5786 */
5787
ae71f5de
MS
5788 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
5789 timeout = ippFindAttribute(con->request, "timeout", IPP_TAG_INTEGER);
ef416fc2 5790 requested = ippFindAttribute(con->request, "requested-attributes",
5791 IPP_TAG_KEYWORD);
ae71f5de 5792 exclude = ippFindAttribute(con->request, "exclude-schemes", IPP_TAG_NAME);
ed6e7faf 5793 include = ippFindAttribute(con->request, "include-schemes", IPP_TAG_NAME);
ef416fc2 5794
5795 if (requested)
89d46774 5796 url_encode_attr(requested, requested_str, sizeof(requested_str));
ef416fc2 5797 else
89d46774 5798 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
ef416fc2 5799
ae71f5de
MS
5800 if (exclude)
5801 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
5802 else
5803 exclude_str[0] = '\0';
5804
ed6e7faf
MS
5805 if (include)
5806 url_encode_attr(include, include_str, sizeof(include_str));
5807 else
5808 include_str[0] = '\0';
5809
ef416fc2 5810 snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
5811 snprintf(options, sizeof(options),
ed6e7faf 5812 "%d+%d+%d+%d+%s%s%s%s%s",
ef416fc2 5813 con->request->request.op.request_id,
ae71f5de 5814 limit ? limit->values[0].integer : 0,
7a0cbd5e 5815 timeout ? timeout->values[0].integer : 15,
ae71f5de
MS
5816 (int)User,
5817 requested_str,
ed6e7faf
MS
5818 exclude_str[0] ? "%20" : "", exclude_str,
5819 include_str[0] ? "%20" : "", include_str);
ef416fc2 5820
5821 if (cupsdSendCommand(con, command, options, 1))
5822 {
5823 /*
5824 * Command started successfully, don't send an IPP response here...
5825 */
5826
5827 ippDelete(con->response);
5828 con->response = NULL;
5829 }
5830 else
5831 {
5832 /*
5833 * Command failed, return "internal error" so the user knows something
5834 * went wrong...
5835 */
5836
5837 send_ipp_status(con, IPP_INTERNAL_ERROR,
5838 _("cups-deviced failed to execute."));
5839 }
5840}
5841
5842
2e4ff8af
MS
5843/*
5844 * 'get_document()' - Get a copy of a job file.
5845 */
5846
5847static void
5848get_document(cupsd_client_t *con, /* I - Client connection */
5849 ipp_attribute_t *uri) /* I - Job URI */
5850{
5851 http_status_t status; /* Policy status */
5852 ipp_attribute_t *attr; /* Current attribute */
5853 int jobid; /* Job ID */
5854 int docnum; /* Document number */
5855 cupsd_job_t *job; /* Current job */
61cf44e2 5856 char scheme[HTTP_MAX_URI], /* Method portion of URI */
2e4ff8af
MS
5857 username[HTTP_MAX_URI], /* Username portion of URI */
5858 host[HTTP_MAX_URI], /* Host portion of URI */
5859 resource[HTTP_MAX_URI]; /* Resource portion of URI */
5860 int port; /* Port portion of URI */
5861 char filename[1024], /* Filename for document */
5862 format[1024]; /* Format for document */
5863
5864
5865 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con,
5866 con->http.fd, uri->values[0].string.text);
5867
5868 /*
5869 * See if we have a job URI or a printer URI...
5870 */
5871
5872 if (!strcmp(uri->name, "printer-uri"))
5873 {
5874 /*
5875 * Got a printer URI; see if we also have a job-id attribute...
5876 */
5877
5878 if ((attr = ippFindAttribute(con->request, "job-id",
5879 IPP_TAG_INTEGER)) == NULL)
5880 {
5881 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 5882 _("Got a printer-uri attribute but no job-id."));
2e4ff8af
MS
5883 return;
5884 }
5885
5886 jobid = attr->values[0].integer;
5887 }
5888 else
5889 {
5890 /*
5891 * Got a job URI; parse it to get the job ID...
5892 */
5893
61cf44e2
MS
5894 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
5895 sizeof(scheme), username, sizeof(username), host,
2e4ff8af
MS
5896 sizeof(host), &port, resource, sizeof(resource));
5897
5898 if (strncmp(resource, "/jobs/", 6))
5899 {
5900 /*
5901 * Not a valid URI!
5902 */
5903
84315f46 5904 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
2e4ff8af
MS
5905 uri->values[0].string.text);
5906 return;
5907 }
5908
5909 jobid = atoi(resource + 6);
5910 }
5911
5912 /*
5913 * See if the job exists...
5914 */
5915
5916 if ((job = cupsdFindJob(jobid)) == NULL)
5917 {
5918 /*
5919 * Nope - return a "not found" error...
5920 */
5921
84315f46 5922 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
2e4ff8af
MS
5923 return;
5924 }
5925
5926 /*
5927 * Check policy...
5928 */
5929
e60ec91f
MS
5930 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con,
5931 job->username)) != HTTP_OK)
2e4ff8af
MS
5932 {
5933 send_http_error(con, status, NULL);
5934 return;
5935 }
5936
5937 /*
5938 * Get the document number...
5939 */
5940
5941 if ((attr = ippFindAttribute(con->request, "document-number",
5942 IPP_TAG_INTEGER)) == NULL)
5943 {
5944 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 5945 _("Missing document-number attribute."));
2e4ff8af
MS
5946 return;
5947 }
5948
5949 if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files ||
5950 attr->num_values > 1)
5951 {
84315f46
MS
5952 send_ipp_status(con, IPP_NOT_FOUND,
5953 _("Document #%d does not exist in job #%d."), docnum,
5954 jobid);
2e4ff8af
MS
5955 return;
5956 }
5957
5958 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, jobid,
5959 docnum);
5960 if ((con->file = open(filename, O_RDONLY)) == -1)
5961 {
5962 cupsdLogMessage(CUPSD_LOG_ERROR,
5963 "Unable to open document %d in job %d - %s", docnum, jobid,
5964 strerror(errno));
5965 send_ipp_status(con, IPP_NOT_FOUND,
84315f46
MS
5966 _("Unable to open document #%d in job #%d."), docnum,
5967 jobid);
2e4ff8af
MS
5968 return;
5969 }
5970
5971 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
5972
5973 cupsdLoadJob(job);
5974
5975 snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super,
5976 job->filetypes[docnum - 1]->type);
5977
5978 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
5979 NULL, format);
5980 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "document-number",
5981 docnum);
5982 if ((attr = ippFindAttribute(job->attrs, "document-name",
5983 IPP_TAG_NAME)) != NULL)
5984 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "document-name",
5985 NULL, attr->values[0].string.text);
5986}
5987
5988
ef416fc2 5989/*
fa73b229 5990 * 'get_job_attrs()' - Get job attributes.
ef416fc2 5991 */
5992
5993static void
fa73b229 5994get_job_attrs(cupsd_client_t *con, /* I - Client connection */
5995 ipp_attribute_t *uri) /* I - Job URI */
ef416fc2 5996{
5997 http_status_t status; /* Policy status */
fa73b229 5998 ipp_attribute_t *attr; /* Current attribute */
5999 int jobid; /* Job ID */
6000 cupsd_job_t *job; /* Current job */
ef55b745 6001 cupsd_printer_t *printer; /* Current printer */
10d09e33
MS
6002 cupsd_policy_t *policy; /* Current security policy */
6003 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
ef416fc2 6004 username[HTTP_MAX_URI], /* Username portion of URI */
6005 host[HTTP_MAX_URI], /* Host portion of URI */
6006 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6007 int port; /* Port portion of URI */
10d09e33
MS
6008 cups_array_t *ra, /* Requested attributes array */
6009 *exclude; /* Private attributes array */
ef416fc2 6010
6011
fa73b229 6012 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
6013 con->http.fd, uri->values[0].string.text);
ef416fc2 6014
6015 /*
fa73b229 6016 * See if we have a job URI or a printer URI...
6017 */
6018
6019 if (!strcmp(uri->name, "printer-uri"))
6020 {
6021 /*
6022 * Got a printer URI; see if we also have a job-id attribute...
6023 */
6024
6025 if ((attr = ippFindAttribute(con->request, "job-id",
6026 IPP_TAG_INTEGER)) == NULL)
6027 {
6028 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 6029 _("Got a printer-uri attribute but no job-id."));
fa73b229 6030 return;
6031 }
6032
6033 jobid = attr->values[0].integer;
6034 }
6035 else
6036 {
6037 /*
6038 * Got a job URI; parse it to get the job ID...
6039 */
6040
61cf44e2
MS
6041 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6042 sizeof(scheme), username, sizeof(username), host,
a4d04587 6043 sizeof(host), &port, resource, sizeof(resource));
fa73b229 6044
6045 if (strncmp(resource, "/jobs/", 6))
6046 {
6047 /*
6048 * Not a valid URI!
6049 */
6050
84315f46 6051 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
fa73b229 6052 uri->values[0].string.text);
6053 return;
6054 }
6055
6056 jobid = atoi(resource + 6);
6057 }
6058
6059 /*
6060 * See if the job exists...
6061 */
6062
6063 if ((job = cupsdFindJob(jobid)) == NULL)
6064 {
6065 /*
6066 * Nope - return a "not found" error...
6067 */
6068
84315f46 6069 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
fa73b229 6070 return;
6071 }
6072
6073 /*
6074 * Check policy...
6075 */
6076
ef55b745
MS
6077 if ((printer = job->printer) == NULL)
6078 printer = cupsdFindDest(job->dest);
6079
6080 if (printer)
10d09e33
MS
6081 policy = printer->op_policy_ptr;
6082 else
6083 policy = DefaultPolicyPtr;
6084
6085 if ((status = cupsdCheckPolicy(policy, con, job->username)) != HTTP_OK)
fa73b229 6086 {
f899b121 6087 send_http_error(con, status, NULL);
fa73b229 6088 return;
6089 }
6090
10d09e33
MS
6091 exclude = cupsdGetPrivateAttrs(policy, con, printer, job->username);
6092
fa73b229 6093 /*
6094 * Copy attributes...
6095 */
6096
bd7854cb 6097 cupsdLoadJob(job);
6098
fa73b229 6099 ra = create_requested_array(con->request);
10d09e33 6100 copy_job_attrs(con, job, ra, exclude);
fa73b229 6101 cupsArrayDelete(ra);
6102
6103 con->response->request.status.status_code = IPP_OK;
6104}
6105
6106
6107/*
6108 * 'get_jobs()' - Get a list of jobs for the specified printer.
6109 */
6110
6111static void
6112get_jobs(cupsd_client_t *con, /* I - Client connection */
6113 ipp_attribute_t *uri) /* I - Printer URI */
6114{
6115 http_status_t status; /* Policy status */
6116 ipp_attribute_t *attr; /* Current attribute */
6117 const char *dest; /* Destination */
bc44d920 6118 cups_ptype_t dtype; /* Destination type (printer/class) */
fa73b229 6119 cups_ptype_t dmask; /* Destination type mask */
f7deaa1a 6120 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
fa73b229 6121 username[HTTP_MAX_URI], /* Username portion of URI */
6122 host[HTTP_MAX_URI], /* Host portion of URI */
6123 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6124 int port; /* Port portion of URI */
aaf19ab0
MS
6125 int job_comparison; /* Job comparison */
6126 ipp_jstate_t job_state; /* job-state value */
fa73b229 6127 int first_job_id; /* First job ID */
6128 int limit; /* Maximum number of jobs to return */
6129 int count; /* Number of jobs that match */
aaf19ab0 6130 ipp_attribute_t *job_ids; /* job-ids attribute */
fa73b229 6131 cupsd_job_t *job; /* Current job pointer */
6132 cupsd_printer_t *printer; /* Printer */
6133 cups_array_t *list; /* Which job list... */
10d09e33
MS
6134 cups_array_t *ra, /* Requested attributes array */
6135 *exclude; /* Private attributes array */
6136 cupsd_policy_t *policy; /* Current policy */
fa73b229 6137
6138
6139 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->http.fd,
6140 uri->values[0].string.text);
6141
6142 /*
6143 * Is the destination valid?
ef416fc2 6144 */
6145
4b3f67ff
MS
6146 if (strcmp(uri->name, "printer-uri"))
6147 {
84315f46 6148 send_ipp_status(con, IPP_BAD_REQUEST, _("No printer-uri in request."));
4b3f67ff
MS
6149 return;
6150 }
6151
f7deaa1a 6152 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6153 sizeof(scheme), username, sizeof(username), host,
a4d04587 6154 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 6155
b9faaae1 6156 if (!strcmp(resource, "/") || !strcmp(resource, "/jobs"))
ef416fc2 6157 {
6158 dest = NULL;
6159 dtype = (cups_ptype_t)0;
6160 dmask = (cups_ptype_t)0;
6161 printer = NULL;
6162 }
fa73b229 6163 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
ef416fc2 6164 {
6165 dest = NULL;
6166 dtype = (cups_ptype_t)0;
6167 dmask = CUPS_PRINTER_CLASS;
6168 printer = NULL;
6169 }
fa73b229 6170 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
ef416fc2 6171 {
6172 dest = NULL;
6173 dtype = CUPS_PRINTER_CLASS;
6174 dmask = CUPS_PRINTER_CLASS;
6175 printer = NULL;
6176 }
f7deaa1a 6177 else if ((dest = cupsdValidateDest(uri->values[0].string.text, &dtype,
6178 &printer)) == NULL)
ef416fc2 6179 {
6180 /*
6181 * Bad URI...
6182 */
6183
6184 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 6185 _("The printer or class does not exist."));
ef416fc2 6186 return;
6187 }
6188 else
b86bc4cf 6189 {
6190 dtype &= CUPS_PRINTER_CLASS;
ef416fc2 6191 dmask = CUPS_PRINTER_CLASS;
b86bc4cf 6192 }
ef416fc2 6193
6194 /*
6195 * Check policy...
6196 */
6197
6198 if (printer)
10d09e33
MS
6199 policy = printer->op_policy_ptr;
6200 else
6201 policy = DefaultPolicyPtr;
6202
6203 if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
ef416fc2 6204 {
f899b121 6205 send_http_error(con, status, NULL);
ef416fc2 6206 return;
6207 }
6208
aaf19ab0
MS
6209 job_ids = ippFindAttribute(con->request, "job-ids", IPP_TAG_INTEGER);
6210
ef416fc2 6211 /*
6212 * See if the "which-jobs" attribute have been specified...
6213 */
6214
fa73b229 6215 if ((attr = ippFindAttribute(con->request, "which-jobs",
aaf19ab0
MS
6216 IPP_TAG_KEYWORD)) != NULL && job_ids)
6217 {
6218 send_ipp_status(con, IPP_CONFLICT,
6219 _("The %s attribute cannot be provided with job-ids."),
6220 "which-jobs");
6221 return;
6222 }
6223 else if (!attr || !strcmp(attr->values[0].string.text, "not-completed"))
6224 {
6225 job_comparison = -1;
6226 job_state = IPP_JOB_STOPPED;
61515785 6227 list = ActiveJobs;
aaf19ab0
MS
6228 }
6229 else if (!strcmp(attr->values[0].string.text, "completed"))
6230 {
6231 job_comparison = 1;
6232 job_state = IPP_JOB_CANCELED;
6233 list = Jobs;
6234 }
6235 else if (!strcmp(attr->values[0].string.text, "aborted"))
6236 {
6237 job_comparison = 0;
6238 job_state = IPP_JOB_ABORTED;
6239 list = Jobs;
6240 }
6241 else if (!strcmp(attr->values[0].string.text, "all"))
6242 {
6243 job_comparison = 1;
6244 job_state = IPP_JOB_PENDING;
6245 list = Jobs;
6246 }
6247 else if (!strcmp(attr->values[0].string.text, "canceled"))
6248 {
6249 job_comparison = 0;
6250 job_state = IPP_JOB_CANCELED;
6251 list = Jobs;
6252 }
6253 else if (!strcmp(attr->values[0].string.text, "pending"))
ef416fc2 6254 {
aaf19ab0
MS
6255 job_comparison = 0;
6256 job_state = IPP_JOB_PENDING;
6257 list = ActiveJobs;
ef416fc2 6258 }
aaf19ab0 6259 else if (!strcmp(attr->values[0].string.text, "pending-held"))
ef416fc2 6260 {
aaf19ab0
MS
6261 job_comparison = 0;
6262 job_state = IPP_JOB_HELD;
6263 list = ActiveJobs;
ef416fc2 6264 }
aaf19ab0 6265 else if (!strcmp(attr->values[0].string.text, "processing"))
3dfe78b3 6266 {
aaf19ab0
MS
6267 job_comparison = 0;
6268 job_state = IPP_JOB_PROCESSING;
6269 list = PrintingJobs;
6270 }
6271 else if (!strcmp(attr->values[0].string.text, "processing-stopped"))
6272 {
6273 job_comparison = 0;
6274 job_state = IPP_JOB_STOPPED;
6275 list = ActiveJobs;
3dfe78b3 6276 }
ef416fc2 6277 else
6278 {
aaf19ab0
MS
6279 send_ipp_status(con, IPP_ATTRIBUTES,
6280 _("The which-jobs value \"%s\" is not supported."),
6281 attr->values[0].string.text);
6282 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
6283 "which-jobs", NULL, attr->values[0].string.text);
6284 return;
ef416fc2 6285 }
6286
6287 /*
6288 * See if they want to limit the number of jobs reported...
6289 */
6290
fa73b229 6291 if ((attr = ippFindAttribute(con->request, "limit",
6292 IPP_TAG_INTEGER)) != NULL)
aaf19ab0
MS
6293 {
6294 if (job_ids)
6295 {
6296 send_ipp_status(con, IPP_CONFLICT,
6297 _("The %s attribute cannot be provided with job-ids."),
6298 "limit");
6299 return;
6300 }
6301
ef416fc2 6302 limit = attr->values[0].integer;
aaf19ab0 6303 }
ef416fc2 6304 else
3dfe78b3 6305 limit = 0;
ef416fc2 6306
6307 if ((attr = ippFindAttribute(con->request, "first-job-id",
6308 IPP_TAG_INTEGER)) != NULL)
aaf19ab0
MS
6309 {
6310 if (job_ids)
6311 {
6312 send_ipp_status(con, IPP_CONFLICT,
6313 _("The %s attribute cannot be provided with job-ids."),
6314 "first-job-id");
6315 return;
6316 }
6317
ef416fc2 6318 first_job_id = attr->values[0].integer;
aaf19ab0 6319 }
ef416fc2 6320 else
6321 first_job_id = 1;
6322
6323 /*
6324 * See if we only want to see jobs for a specific user...
6325 */
6326
fa73b229 6327 if ((attr = ippFindAttribute(con->request, "my-jobs",
aaf19ab0
MS
6328 IPP_TAG_BOOLEAN)) != NULL && job_ids)
6329 {
6330 send_ipp_status(con, IPP_CONFLICT,
6331 _("The %s attribute cannot be provided with job-ids."),
6332 "my-jobs");
6333 return;
6334 }
6335 else if (attr && attr->values[0].boolean)
e00b005a 6336 strlcpy(username, get_username(con), sizeof(username));
ef416fc2 6337 else
6338 username[0] = '\0';
6339
db8b865d 6340 ra = create_requested_array(con->request);
ef416fc2 6341
6342 /*
6343 * OK, build a list of jobs for this printer...
6344 */
6345
aaf19ab0 6346 if (job_ids)
ef416fc2 6347 {
aaf19ab0 6348 int i; /* Looping var */
ef416fc2 6349
aaf19ab0
MS
6350 for (i = 0; i < job_ids->num_values; i ++)
6351 {
4220952d 6352 if (!cupsdFindJob(job_ids->values[i].integer))
aaf19ab0
MS
6353 break;
6354 }
ef416fc2 6355
aaf19ab0
MS
6356 if (i < job_ids->num_values)
6357 {
84315f46 6358 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
aaf19ab0
MS
6359 job_ids->values[i].integer);
6360 return;
6361 }
0a682745 6362
aaf19ab0
MS
6363 for (i = 0; i < job_ids->num_values; i ++)
6364 {
6365 job = cupsdFindJob(job_ids->values[i].integer);
0a682745 6366
aaf19ab0 6367 cupsdLoadJob(job);
ef416fc2 6368
aaf19ab0
MS
6369 if (!job->attrs)
6370 {
6371 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d",
6372 job->id);
6373 continue;
6374 }
ef416fc2 6375
aaf19ab0
MS
6376 if (i > 0)
6377 ippAddSeparator(con->response);
bd7854cb 6378
10d09e33
MS
6379 exclude = cupsdGetPrivateAttrs(job->printer ?
6380 job->printer->op_policy_ptr :
6381 policy, con, job->printer,
6382 job->username);
6383
6384 copy_job_attrs(con, job, ra, exclude);
d1c13e16 6385 }
aaf19ab0
MS
6386 }
6387 else
6388 {
6389 for (count = 0, job = (cupsd_job_t *)cupsArrayFirst(list);
6390 (limit <= 0 || count < limit) && job;
6391 job = (cupsd_job_t *)cupsArrayNext(list))
6392 {
6393 /*
6394 * Filter out jobs that don't match...
6395 */
bd7854cb 6396
aaf19ab0
MS
6397 cupsdLogMessage(CUPSD_LOG_DEBUG2,
6398 "get_jobs: job->id=%d, dest=\"%s\", username=\"%s\", "
6399 "state_value=%d, attrs=%p", job->id, job->dest,
6400 job->username, job->state_value, job->attrs);
7594b224 6401
aaf19ab0
MS
6402 if (!job->dest || !job->username)
6403 cupsdLoadJob(job);
fa73b229 6404
aaf19ab0
MS
6405 if (!job->dest || !job->username)
6406 continue;
ef416fc2 6407
aaf19ab0
MS
6408 if ((dest && strcmp(job->dest, dest)) &&
6409 (!job->printer || !dest || strcmp(job->printer->name, dest)))
6410 continue;
6411 if ((job->dtype & dmask) != dtype &&
6412 (!job->printer || (job->printer->type & dmask) != dtype))
6413 continue;
ef416fc2 6414
aaf19ab0
MS
6415 if ((job_comparison < 0 && job->state_value > job_state) ||
6416 (job_comparison == 0 && job->state_value != job_state) ||
6417 (job_comparison > 0 && job->state_value < job_state))
6418 continue;
6419
6420 if (job->id < first_job_id)
6421 continue;
6422
6423 cupsdLoadJob(job);
6424
6425 if (!job->attrs)
6426 {
6427 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d",
6428 job->id);
6429 continue;
6430 }
6431
88f9aafc 6432 if (username[0] && _cups_strcasecmp(username, job->username))
aaf19ab0
MS
6433 continue;
6434
6435 if (count > 0)
6436 ippAddSeparator(con->response);
6437
6438 count ++;
6439
10d09e33
MS
6440 exclude = cupsdGetPrivateAttrs(job->printer ?
6441 job->printer->op_policy_ptr :
6442 policy, con, job->printer,
6443 job->username);
6444
6445 copy_job_attrs(con, job, ra, exclude);
aaf19ab0
MS
6446 }
6447
6448 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count=%d", count);
6449 }
d1c13e16 6450
fa73b229 6451 cupsArrayDelete(ra);
ef416fc2 6452
fa73b229 6453 con->response->request.status.status_code = IPP_OK;
ef416fc2 6454}
6455
6456
6457/*
6458 * 'get_notifications()' - Get events for a subscription.
6459 */
6460
6461static void
bd7854cb 6462get_notifications(cupsd_client_t *con) /* I - Client connection */
ef416fc2 6463{
bd7854cb 6464 int i, j; /* Looping vars */
6465 http_status_t status; /* Policy status */
6466 cupsd_subscription_t *sub; /* Subscription */
6467 ipp_attribute_t *ids, /* notify-subscription-ids */
6468 *sequences; /* notify-sequence-numbers */
6469 int min_seq; /* Minimum sequence number */
6470 int interval; /* Poll interval */
6471
6472
f7deaa1a 6473 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])",
bd7854cb 6474 con, con->http.fd);
6475
6476 /*
6477 * Get subscription attributes...
6478 */
6479
6480 ids = ippFindAttribute(con->request, "notify-subscription-ids",
6481 IPP_TAG_INTEGER);
6482 sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
6483 IPP_TAG_INTEGER);
6484
6485 if (!ids)
6486 {
6487 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 6488 _("Missing notify-subscription-ids attribute."));
bd7854cb 6489 return;
6490 }
6491
6492 /*
6493 * Are the subscription IDs valid?
6494 */
6495
6496 for (i = 0, interval = 60; i < ids->num_values; i ++)
6497 {
6498 if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
6499 {
6500 /*
6501 * Bad subscription ID...
6502 */
6503
84315f46 6504 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
bd7854cb 6505 ids->values[i].integer);
6506 return;
6507 }
6508
6509 /*
6510 * Check policy...
6511 */
6512
6513 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
6514 DefaultPolicyPtr,
6515 con, sub->owner)) != HTTP_OK)
6516 {
f899b121 6517 send_http_error(con, status, sub->dest);
bd7854cb 6518 return;
6519 }
6520
6521 /*
6522 * Check the subscription type and update the interval accordingly.
6523 */
6524
6525 if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
6526 interval > 10)
6527 interval = 10;
6528 else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
6529 interval = 0;
6530 else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
6531 interval > 30)
6532 interval = 30;
6533 }
6534
6535 /*
6536 * Tell the client to poll again in N seconds...
6537 */
6538
6539 if (interval > 0)
6540 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
6541 "notify-get-interval", interval);
6542
6543 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
6544 "printer-up-time", time(NULL));
6545
6546 /*
6547 * Copy the subscription event attributes to the response.
6548 */
6549
6550 con->response->request.status.status_code =
6551 interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
6552
6553 for (i = 0; i < ids->num_values; i ++)
6554 {
6555 /*
6556 * Get the subscription and sequence number...
6557 */
6558
6559 sub = cupsdFindSubscription(ids->values[i].integer);
6560
6561 if (sequences && i < sequences->num_values)
6562 min_seq = sequences->values[i].integer;
6563 else
6564 min_seq = 1;
6565
6566 /*
6567 * If we don't have any new events, nothing to do here...
6568 */
6569
10d09e33 6570 if (min_seq > (sub->first_event_id + cupsArrayCount(sub->events)))
bd7854cb 6571 continue;
6572
6573 /*
6574 * Otherwise copy all of the new events...
6575 */
6576
6577 if (sub->first_event_id > min_seq)
6578 j = 0;
6579 else
6580 j = min_seq - sub->first_event_id;
6581
10d09e33 6582 for (; j < cupsArrayCount(sub->events); j ++)
b423cd4c 6583 {
6584 ippAddSeparator(con->response);
ef416fc2 6585
10d09e33
MS
6586 copy_attrs(con->response,
6587 ((cupsd_event_t *)cupsArrayIndex(sub->events, j))->attrs, NULL,
6588 IPP_TAG_EVENT_NOTIFICATION, 0, NULL);
b423cd4c 6589 }
6590 }
6591}
ef416fc2 6592
ef416fc2 6593
b94498cf 6594/*
6595 * 'get_ppd()' - Get a named PPD from the local system.
6596 */
6597
6598static void
6599get_ppd(cupsd_client_t *con, /* I - Client connection */
6600 ipp_attribute_t *uri) /* I - Printer URI or PPD name */
6601{
6602 http_status_t status; /* Policy status */
6603 cupsd_printer_t *dest; /* Destination */
6604 cups_ptype_t dtype; /* Destination type */
6605
6606
6607 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", con,
6608 con->http.fd, uri, uri->name, uri->values[0].string.text);
6609
6610 if (!strcmp(uri->name, "ppd-name"))
6611 {
6612 /*
6613 * Return a PPD file from cups-driverd...
6614 */
6615
6616 char command[1024], /* cups-driverd command */
6617 options[1024], /* Options to pass to command */
6618 ppd_name[1024]; /* ppd-name */
6619
6620
6621 /*
6622 * Check policy...
6623 */
6624
6625 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6626 {
6627 send_http_error(con, status, NULL);
6628 return;
6629 }
6630
6631 /*
6632 * Run cups-driverd command with the given options...
6633 */
6634
6635 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
6636 url_encode_string(uri->values[0].string.text, ppd_name, sizeof(ppd_name));
6637 snprintf(options, sizeof(options), "get+%d+%s",
6638 con->request->request.op.request_id, ppd_name);
6639
6640 if (cupsdSendCommand(con, command, options, 0))
6641 {
6642 /*
6643 * Command started successfully, don't send an IPP response here...
6644 */
6645
6646 ippDelete(con->response);
6647 con->response = NULL;
6648 }
6649 else
6650 {
6651 /*
6652 * Command failed, return "internal error" so the user knows something
6653 * went wrong...
6654 */
6655
6656 send_ipp_status(con, IPP_INTERNAL_ERROR,
6657 _("cups-driverd failed to execute."));
6658 }
6659 }
6660 else if (!strcmp(uri->name, "printer-uri") &&
6661 cupsdValidateDest(uri->values[0].string.text, &dtype, &dest))
6662 {
6663 int i; /* Looping var */
6664 char filename[1024]; /* PPD filename */
6665
6666
6667 /*
6668 * Check policy...
6669 */
6670
6671 if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK)
6672 {
2fb76298 6673 send_http_error(con, status, dest);
b94498cf 6674 return;
6675 }
6676
6677 /*
6678 * See if we need the PPD for a class or remote printer...
6679 */
6680
bc44d920 6681 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
6682 dest->name);
6683
6684 if ((dtype & CUPS_PRINTER_REMOTE) && access(filename, 0))
b94498cf 6685 {
3d8365b8 6686 con->response->request.status.status_code = CUPS_SEE_OTHER;
b94498cf 6687 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
6688 "printer-uri", NULL, dest->uri);
6689 return;
6690 }
a2326b5b 6691 else if (dtype & CUPS_PRINTER_CLASS)
b94498cf 6692 {
6693 for (i = 0; i < dest->num_printers; i ++)
a2326b5b 6694 if (!(dest->printers[i]->type & CUPS_PRINTER_CLASS))
bc44d920 6695 {
6696 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
6697 dest->printers[i]->name);
6698
6699 if (!access(filename, 0))
6700 break;
6701 }
b94498cf 6702
6703 if (i < dest->num_printers)
6704 dest = dest->printers[i];
6705 else
6706 {
3d8365b8 6707 con->response->request.status.status_code = CUPS_SEE_OTHER;
b94498cf 6708 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI,
6709 "printer-uri", NULL, dest->printers[0]->uri);
6710 return;
6711 }
6712 }
6713
6714 /*
6715 * Found the printer with the PPD file, now see if there is one...
6716 */
6717
b94498cf 6718 if ((con->file = open(filename, O_RDONLY)) < 0)
6719 {
6720 send_ipp_status(con, IPP_NOT_FOUND,
6721 _("The PPD file \"%s\" could not be opened: %s"),
3d8365b8 6722 uri->values[0].string.text, strerror(errno));
b94498cf 6723 return;
6724 }
6725
6726 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
6727
6728 con->pipe_pid = 0;
6729
3d8365b8 6730 con->response->request.status.status_code = IPP_OK;
b94498cf 6731 }
6732 else
6733 send_ipp_status(con, IPP_NOT_FOUND,
6734 _("The PPD file \"%s\" could not be found."),
6735 uri->values[0].string.text);
6736}
6737
6738
b423cd4c 6739/*
6740 * 'get_ppds()' - Get the list of PPD files on the local system.
6741 */
ef416fc2 6742
b423cd4c 6743static void
6744get_ppds(cupsd_client_t *con) /* I - Client connection */
6745{
6746 http_status_t status; /* Policy status */
b423cd4c 6747 ipp_attribute_t *limit, /* Limit attribute */
b94498cf 6748 *device, /* ppd-device-id attribute */
6749 *language, /* ppd-natural-language attribute */
b423cd4c 6750 *make, /* ppd-make attribute */
b94498cf 6751 *model, /* ppd-make-and-model attribute */
3d8365b8 6752 *model_number, /* ppd-model-number attribute */
b94498cf 6753 *product, /* ppd-product attribute */
6754 *psversion, /* ppd-psverion attribute */
3d8365b8 6755 *type, /* ppd-type attribute */
ed6e7faf
MS
6756 *requested, /* requested-attributes attribute */
6757 *exclude, /* exclude-schemes attribute */
6758 *include; /* include-schemes attribute */
b94498cf 6759 char command[1024], /* cups-driverd command */
ed6e7faf 6760 options[4096], /* Options to pass to command */
b94498cf 6761 device_str[256],/* Escaped ppd-device-id string */
6762 language_str[256],
bc44d920 6763 /* Escaped ppd-natural-language */
b94498cf 6764 make_str[256], /* Escaped ppd-make string */
6765 model_str[256], /* Escaped ppd-make-and-model string */
3d8365b8 6766 model_number_str[256],
6767 /* ppd-model-number string */
b94498cf 6768 product_str[256],
6769 /* Escaped ppd-product string */
6770 psversion_str[256],
6771 /* Escaped ppd-psversion string */
3d8365b8 6772 type_str[256], /* Escaped ppd-type string */
ed6e7faf 6773 requested_str[256],
89d46774 6774 /* String for requested attributes */
ed6e7faf
MS
6775 exclude_str[512],
6776 /* String for excluded schemes */
6777 include_str[512];
6778 /* String for included schemes */
ef416fc2 6779
ef416fc2 6780
b423cd4c 6781 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->http.fd);
ef416fc2 6782
6783 /*
b423cd4c 6784 * Check policy...
ef416fc2 6785 */
6786
b423cd4c 6787 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
ef416fc2 6788 {
f899b121 6789 send_http_error(con, status, NULL);
b423cd4c 6790 return;
ef416fc2 6791 }
ef416fc2 6792
b423cd4c 6793 /*
6794 * Run cups-driverd command with the given options...
6795 */
6796
3d8365b8 6797 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
6798 device = ippFindAttribute(con->request, "ppd-device-id", IPP_TAG_TEXT);
6799 language = ippFindAttribute(con->request, "ppd-natural-language",
6800 IPP_TAG_LANGUAGE);
6801 make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
6802 model = ippFindAttribute(con->request, "ppd-make-and-model",
6803 IPP_TAG_TEXT);
6804 model_number = ippFindAttribute(con->request, "ppd-model-number",
6805 IPP_TAG_INTEGER);
6806 product = ippFindAttribute(con->request, "ppd-product", IPP_TAG_TEXT);
6807 psversion = ippFindAttribute(con->request, "ppd-psversion", IPP_TAG_TEXT);
6808 type = ippFindAttribute(con->request, "ppd-type", IPP_TAG_KEYWORD);
6809 requested = ippFindAttribute(con->request, "requested-attributes",
6810 IPP_TAG_KEYWORD);
8b450588
MS
6811 exclude = ippFindAttribute(con->request, "exclude-schemes",
6812 IPP_TAG_NAME);
6813 include = ippFindAttribute(con->request, "include-schemes",
6814 IPP_TAG_NAME);
b423cd4c 6815
6816 if (requested)
89d46774 6817 url_encode_attr(requested, requested_str, sizeof(requested_str));
6818 else
6819 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
ef416fc2 6820
b94498cf 6821 if (device)
6822 url_encode_attr(device, device_str, sizeof(device_str));
6823 else
6824 device_str[0] = '\0';
6825
6826 if (language)
6827 url_encode_attr(language, language_str, sizeof(language_str));
6828 else
6829 language_str[0] = '\0';
6830
89d46774 6831 if (make)
6832 url_encode_attr(make, make_str, sizeof(make_str));
b423cd4c 6833 else
89d46774 6834 make_str[0] = '\0';
fa73b229 6835
b94498cf 6836 if (model)
6837 url_encode_attr(model, model_str, sizeof(model_str));
6838 else
6839 model_str[0] = '\0';
6840
3d8365b8 6841 if (model_number)
6842 snprintf(model_number_str, sizeof(model_number_str), "ppd-model-number=%d",
6843 model_number->values[0].integer);
6844 else
6845 model_number_str[0] = '\0';
6846
b94498cf 6847 if (product)
6848 url_encode_attr(product, product_str, sizeof(product_str));
6849 else
6850 product_str[0] = '\0';
6851
6852 if (psversion)
6853 url_encode_attr(psversion, psversion_str, sizeof(psversion_str));
6854 else
6855 psversion_str[0] = '\0';
6856
3d8365b8 6857 if (type)
6858 url_encode_attr(type, type_str, sizeof(type_str));
6859 else
6860 type_str[0] = '\0';
6861
ed6e7faf
MS
6862 if (exclude)
6863 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
6864 else
6865 exclude_str[0] = '\0';
6866
6867 if (include)
6868 url_encode_attr(include, include_str, sizeof(include_str));
6869 else
6870 include_str[0] = '\0';
6871
b423cd4c 6872 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
3d8365b8 6873 snprintf(options, sizeof(options),
ed6e7faf 6874 "list+%d+%d+%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
b423cd4c 6875 con->request->request.op.request_id,
6876 limit ? limit->values[0].integer : 0,
b94498cf 6877 requested_str,
6878 device ? "%20" : "", device_str,
6879 language ? "%20" : "", language_str,
6880 make ? "%20" : "", make_str,
6881 model ? "%20" : "", model_str,
3d8365b8 6882 model_number ? "%20" : "", model_number_str,
b94498cf 6883 product ? "%20" : "", product_str,
3d8365b8 6884 psversion ? "%20" : "", psversion_str,
ed6e7faf
MS
6885 type ? "%20" : "", type_str,
6886 exclude_str[0] ? "%20" : "", exclude_str,
6887 include_str[0] ? "%20" : "", include_str);
ef416fc2 6888
b423cd4c 6889 if (cupsdSendCommand(con, command, options, 0))
6890 {
6891 /*
6892 * Command started successfully, don't send an IPP response here...
6893 */
6894
6895 ippDelete(con->response);
6896 con->response = NULL;
6897 }
6898 else
6899 {
6900 /*
6901 * Command failed, return "internal error" so the user knows something
6902 * went wrong...
6903 */
6904
6905 send_ipp_status(con, IPP_INTERNAL_ERROR,
6906 _("cups-driverd failed to execute."));
6907 }
ef416fc2 6908}
6909
6910
6911/*
b423cd4c 6912 * 'get_printer_attrs()' - Get printer attributes.
ef416fc2 6913 */
6914
6915static void
b423cd4c 6916get_printer_attrs(cupsd_client_t *con, /* I - Client connection */
6917 ipp_attribute_t *uri) /* I - Printer URI */
ef416fc2 6918{
6919 http_status_t status; /* Policy status */
bc44d920 6920 cups_ptype_t dtype; /* Destination type (printer/class) */
b423cd4c 6921 cupsd_printer_t *printer; /* Printer/class */
ef416fc2 6922 cups_array_t *ra; /* Requested attributes array */
6923
6924
b423cd4c 6925 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
6926 con->http.fd, uri->values[0].string.text);
ef416fc2 6927
6928 /*
b423cd4c 6929 * Is the destination valid?
ef416fc2 6930 */
6931
f7deaa1a 6932 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 6933 {
6934 /*
b423cd4c 6935 * Bad URI...
ef416fc2 6936 */
6937
6938 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 6939 _("The printer or class does not exist."));
ef416fc2 6940 return;
6941 }
6942
6943 /*
6944 * Check policy...
6945 */
6946
b423cd4c 6947 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
ef416fc2 6948 {
f899b121 6949 send_http_error(con, status, printer);
ef416fc2 6950 return;
6951 }
6952
6953 /*
b423cd4c 6954 * Send the attributes...
ef416fc2 6955 */
6956
6957 ra = create_requested_array(con->request);
6958
b423cd4c 6959 copy_printer_attrs(con, printer, ra);
ef416fc2 6960
6961 cupsArrayDelete(ra);
6962
6963 con->response->request.status.status_code = IPP_OK;
6964}
6965
6966
c168a833
MS
6967/*
6968 * 'get_printer_supported()' - Get printer supported values.
6969 */
6970
6971static void
6972get_printer_supported(
6973 cupsd_client_t *con, /* I - Client connection */
6974 ipp_attribute_t *uri) /* I - Printer URI */
6975{
6976 http_status_t status; /* Policy status */
6977 cups_ptype_t dtype; /* Destination type (printer/class) */
6978 cupsd_printer_t *printer; /* Printer/class */
6979
6980
6981 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_supported(%p[%d], %s)", con,
6982 con->http.fd, uri->values[0].string.text);
6983
6984 /*
6985 * Is the destination valid?
6986 */
6987
6988 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
6989 {
6990 /*
6991 * Bad URI...
6992 */
6993
6994 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 6995 _("The printer or class does not exist."));
c168a833
MS
6996 return;
6997 }
6998
6999 /*
7000 * Check policy...
7001 */
7002
7003 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7004 {
7005 send_http_error(con, status, printer);
7006 return;
7007 }
7008
7009 /*
7010 * Return a list of attributes that can be set via Set-Printer-Attributes.
7011 */
7012
7013 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7014 "printer-info", 0);
7015 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7016 "printer-location", 0);
7017
7018 con->response->request.status.status_code = IPP_OK;
7019}
7020
7021
ef416fc2 7022/*
b423cd4c 7023 * 'get_printers()' - Get a list of printers or classes.
ef416fc2 7024 */
7025
7026static void
b423cd4c 7027get_printers(cupsd_client_t *con, /* I - Client connection */
7028 int type) /* I - 0 or CUPS_PRINTER_CLASS */
ef416fc2 7029{
b423cd4c 7030 http_status_t status; /* Policy status */
7031 ipp_attribute_t *attr; /* Current attribute */
bc44d920 7032 int limit; /* Max number of printers to return */
b423cd4c 7033 int count; /* Number of printers that match */
7034 cupsd_printer_t *printer; /* Current printer pointer */
7035 int printer_type, /* printer-type attribute */
7036 printer_mask; /* printer-type-mask attribute */
7037 char *location; /* Location string */
7038 const char *username; /* Current user */
7039 char *first_printer_name; /* first-printer-name attribute */
7040 cups_array_t *ra; /* Requested attributes array */
e07d4801 7041 int local; /* Local connection? */
ef416fc2 7042
ef416fc2 7043
b423cd4c 7044 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
7045 con->http.fd, type);
ef416fc2 7046
7047 /*
7048 * Check policy...
7049 */
7050
b423cd4c 7051 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
ef416fc2 7052 {
f899b121 7053 send_http_error(con, status, NULL);
ef416fc2 7054 return;
7055 }
7056
7057 /*
b423cd4c 7058 * Check for printers...
ef416fc2 7059 */
7060
b423cd4c 7061 if (!Printers || !cupsArrayCount(Printers))
7062 {
7063 send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
7064 return;
7065 }
7066
7067 /*
7068 * See if they want to limit the number of printers reported...
7069 */
ef416fc2 7070
fa73b229 7071 if ((attr = ippFindAttribute(con->request, "limit",
7072 IPP_TAG_INTEGER)) != NULL)
ef416fc2 7073 limit = attr->values[0].integer;
7074 else
b423cd4c 7075 limit = 10000000;
7076
7077 if ((attr = ippFindAttribute(con->request, "first-printer-name",
7078 IPP_TAG_NAME)) != NULL)
7079 first_printer_name = attr->values[0].string.text;
7080 else
7081 first_printer_name = NULL;
ef416fc2 7082
7083 /*
b423cd4c 7084 * Support filtering...
ef416fc2 7085 */
7086
b423cd4c 7087 if ((attr = ippFindAttribute(con->request, "printer-type",
7088 IPP_TAG_ENUM)) != NULL)
7089 printer_type = attr->values[0].integer;
ef416fc2 7090 else
b423cd4c 7091 printer_type = 0;
ef416fc2 7092
b423cd4c 7093 if ((attr = ippFindAttribute(con->request, "printer-type-mask",
7094 IPP_TAG_ENUM)) != NULL)
7095 printer_mask = attr->values[0].integer;
ef416fc2 7096 else
b423cd4c 7097 printer_mask = 0;
e00b005a 7098
e07d4801
MS
7099 local = httpAddrLocalhost(&(con->clientaddr));
7100
b423cd4c 7101 if ((attr = ippFindAttribute(con->request, "printer-location",
7102 IPP_TAG_TEXT)) != NULL)
7103 location = attr->values[0].string.text;
7104 else
7105 location = NULL;
e00b005a 7106
7107 if (con->username[0])
b423cd4c 7108 username = con->username;
e00b005a 7109 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7110 IPP_TAG_NAME)) != NULL)
b423cd4c 7111 username = attr->values[0].string.text;
e00b005a 7112 else
b423cd4c 7113 username = NULL;
ef416fc2 7114
b423cd4c 7115 ra = create_requested_array(con->request);
ef416fc2 7116
7117 /*
b423cd4c 7118 * OK, build a list of printers for this printer...
ef416fc2 7119 */
7120
b423cd4c 7121 if (first_printer_name)
ef416fc2 7122 {
b423cd4c 7123 if ((printer = cupsdFindDest(first_printer_name)) == NULL)
7124 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
ef416fc2 7125 }
7126 else
b423cd4c 7127 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
ef416fc2 7128
b423cd4c 7129 for (count = 0;
7130 count < limit && printer;
7131 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
7132 {
e07d4801
MS
7133 if (!local && !printer->shared)
7134 continue;
7135
b423cd4c 7136 if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
7137 (printer->type & printer_mask) == printer_type &&
b19ccc9e 7138 (!location ||
88f9aafc 7139 (printer->location && !_cups_strcasecmp(printer->location, location))))
ef416fc2 7140 {
b423cd4c 7141 /*
7142 * If a username is specified, see if it is allowed or denied
7143 * access...
7144 */
ef416fc2 7145
10d09e33
MS
7146 if (cupsArrayCount(printer->users) && username &&
7147 !user_allowed(printer, username))
b423cd4c 7148 continue;
ef416fc2 7149
b423cd4c 7150 /*
7151 * Add the group separator as needed...
7152 */
ef416fc2 7153
b423cd4c 7154 if (count > 0)
7155 ippAddSeparator(con->response);
ef416fc2 7156
b423cd4c 7157 count ++;
ef416fc2 7158
b423cd4c 7159 /*
7160 * Send the attributes...
7161 */
ef416fc2 7162
b423cd4c 7163 copy_printer_attrs(con, printer, ra);
7164 }
ef416fc2 7165 }
7166
b423cd4c 7167 cupsArrayDelete(ra);
ef416fc2 7168
7169 con->response->request.status.status_code = IPP_OK;
7170}
7171
7172
7173/*
b423cd4c 7174 * 'get_subscription_attrs()' - Get subscription attributes.
ef416fc2 7175 */
7176
7177static void
b423cd4c 7178get_subscription_attrs(
7179 cupsd_client_t *con, /* I - Client connection */
7180 int sub_id) /* I - Subscription ID */
ef416fc2 7181{
b423cd4c 7182 http_status_t status; /* Policy status */
7183 cupsd_subscription_t *sub; /* Subscription */
10d09e33
MS
7184 cupsd_policy_t *policy; /* Current security policy */
7185 cups_array_t *ra, /* Requested attributes array */
7186 *exclude; /* Private attributes array */
ef416fc2 7187
7188
b423cd4c 7189 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7190 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
7191 con, con->http.fd, sub_id);
ef416fc2 7192
fa73b229 7193 /*
b423cd4c 7194 * Is the subscription ID valid?
fa73b229 7195 */
7196
b423cd4c 7197 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
fa73b229 7198 {
7199 /*
b423cd4c 7200 * Bad subscription ID...
fa73b229 7201 */
7202
84315f46
MS
7203 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
7204 sub_id);
fa73b229 7205 return;
7206 }
7207
7208 /*
7209 * Check policy...
7210 */
7211
10d09e33
MS
7212 if (sub->dest)
7213 policy = sub->dest->op_policy_ptr;
7214 else
7215 policy = DefaultPolicyPtr;
7216
7217 if ((status = cupsdCheckPolicy(policy, con, sub->owner)) != HTTP_OK)
fa73b229 7218 {
f899b121 7219 send_http_error(con, status, sub->dest);
fa73b229 7220 return;
7221 }
7222
10d09e33
MS
7223 exclude = cupsdGetPrivateAttrs(policy, con, sub->dest, sub->owner);
7224
ef416fc2 7225 /*
b423cd4c 7226 * Copy the subscription attributes to the response using the
7227 * requested-attributes attribute that may be provided by the client.
ef416fc2 7228 */
7229
b423cd4c 7230 ra = create_requested_array(con->request);
fa73b229 7231
10d09e33 7232 copy_subscription_attrs(con, sub, ra, exclude);
ef416fc2 7233
b423cd4c 7234 cupsArrayDelete(ra);
fa73b229 7235
b423cd4c 7236 con->response->request.status.status_code = IPP_OK;
7237}
fa73b229 7238
fa73b229 7239
b423cd4c 7240/*
7241 * 'get_subscriptions()' - Get subscriptions.
7242 */
ef416fc2 7243
b423cd4c 7244static void
7245get_subscriptions(cupsd_client_t *con, /* I - Client connection */
7246 ipp_attribute_t *uri) /* I - Printer/job URI */
7247{
7248 http_status_t status; /* Policy status */
7249 int count; /* Number of subscriptions */
7250 int limit; /* Limit */
7251 cupsd_subscription_t *sub; /* Subscription */
7252 cups_array_t *ra; /* Requested attributes array */
7253 ipp_attribute_t *attr; /* Attribute */
bc44d920 7254 cups_ptype_t dtype; /* Destination type (printer/class) */
f7deaa1a 7255 char scheme[HTTP_MAX_URI],
7256 /* Scheme portion of URI */
b423cd4c 7257 username[HTTP_MAX_URI],
7258 /* Username portion of URI */
7259 host[HTTP_MAX_URI],
7260 /* Host portion of URI */
7261 resource[HTTP_MAX_URI];
7262 /* Resource portion of URI */
7263 int port; /* Port portion of URI */
7264 cupsd_job_t *job; /* Job pointer */
7265 cupsd_printer_t *printer; /* Printer */
10d09e33
MS
7266 cupsd_policy_t *policy; /* Policy */
7267 cups_array_t *exclude; /* Private attributes array */
fa73b229 7268
fa73b229 7269
b423cd4c 7270 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7271 "get_subscriptions(con=%p[%d], uri=%s)",
7272 con, con->http.fd, uri->values[0].string.text);
7273
7274 /*
7275 * Is the destination valid?
7276 */
7277
f7deaa1a 7278 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7279 sizeof(scheme), username, sizeof(username), host,
b423cd4c 7280 sizeof(host), &port, resource, sizeof(resource));
7281
7282 if (!strcmp(resource, "/") ||
7283 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
7284 (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
7285 (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
7286 {
7287 printer = NULL;
7288 job = NULL;
ef416fc2 7289 }
b423cd4c 7290 else if (!strncmp(resource, "/jobs/", 6) && resource[6])
ef416fc2 7291 {
b423cd4c 7292 printer = NULL;
7293 job = cupsdFindJob(atoi(resource + 6));
ef416fc2 7294
b423cd4c 7295 if (!job)
ef416fc2 7296 {
84315f46
MS
7297 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
7298 atoi(resource + 6));
ef416fc2 7299 return;
7300 }
b423cd4c 7301 }
f7deaa1a 7302 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
b423cd4c 7303 {
fa73b229 7304 /*
b423cd4c 7305 * Bad URI...
fa73b229 7306 */
7307
b423cd4c 7308 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 7309 _("The printer or class does not exist."));
b423cd4c 7310 return;
7311 }
7312 else if ((attr = ippFindAttribute(con->request, "notify-job-id",
7313 IPP_TAG_INTEGER)) != NULL)
7314 {
7315 job = cupsdFindJob(attr->values[0].integer);
ef416fc2 7316
b423cd4c 7317 if (!job)
fa73b229 7318 {
84315f46 7319 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
b423cd4c 7320 attr->values[0].integer);
fa73b229 7321 return;
7322 }
ef416fc2 7323 }
b423cd4c 7324 else
7325 job = NULL;
ef416fc2 7326
7327 /*
b423cd4c 7328 * Check policy...
ef416fc2 7329 */
7330
10d09e33
MS
7331 if (printer)
7332 policy = printer->op_policy_ptr;
7333 else
7334 policy = DefaultPolicyPtr;
7335
7336 if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
ef416fc2 7337 {
f899b121 7338 send_http_error(con, status, printer);
b423cd4c 7339 return;
7340 }
ef416fc2 7341
b423cd4c 7342 /*
7343 * Copy the subscription attributes to the response using the
7344 * requested-attributes attribute that may be provided by the client.
7345 */
ef416fc2 7346
b423cd4c 7347 ra = create_requested_array(con->request);
ef416fc2 7348
b423cd4c 7349 if ((attr = ippFindAttribute(con->request, "limit",
7350 IPP_TAG_INTEGER)) != NULL)
7351 limit = attr->values[0].integer;
7352 else
7353 limit = 0;
ef416fc2 7354
b423cd4c 7355 /*
7356 * See if we only want to see subscriptions for a specific user...
7357 */
ef416fc2 7358
b423cd4c 7359 if ((attr = ippFindAttribute(con->request, "my-subscriptions",
7360 IPP_TAG_BOOLEAN)) != NULL &&
7361 attr->values[0].boolean)
7362 strlcpy(username, get_username(con), sizeof(username));
fa73b229 7363 else
b423cd4c 7364 username[0] = '\0';
ef416fc2 7365
b423cd4c 7366 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
7367 sub;
7368 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
7369 if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
88f9aafc 7370 (!username[0] || !_cups_strcasecmp(username, sub->owner)))
fa73b229 7371 {
b423cd4c 7372 ippAddSeparator(con->response);
10d09e33
MS
7373
7374 exclude = cupsdGetPrivateAttrs(sub->dest ? sub->dest->op_policy_ptr :
7375 policy, con, sub->dest,
7376 sub->owner);
7377
7378 copy_subscription_attrs(con, sub, ra, exclude);
ef416fc2 7379
b423cd4c 7380 count ++;
7381 if (limit && count >= limit)
7382 break;
7383 }
ef416fc2 7384
b423cd4c 7385 cupsArrayDelete(ra);
fa73b229 7386
b423cd4c 7387 if (count)
7388 con->response->request.status.status_code = IPP_OK;
7389 else
7390 send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
7391}
ef416fc2 7392
ef416fc2 7393
b423cd4c 7394/*
7395 * 'get_username()' - Get the username associated with a request.
7396 */
ef416fc2 7397
b423cd4c 7398static const char * /* O - Username */
7399get_username(cupsd_client_t *con) /* I - Connection */
7400{
7401 ipp_attribute_t *attr; /* Attribute */
ef416fc2 7402
ef416fc2 7403
b423cd4c 7404 if (con->username[0])
7405 return (con->username);
7406 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7407 IPP_TAG_NAME)) != NULL)
7408 return (attr->values[0].string.text);
7409 else
7410 return ("anonymous");
ef416fc2 7411}
7412
7413
7414/*
b423cd4c 7415 * 'hold_job()' - Hold a print job.
ef416fc2 7416 */
7417
b423cd4c 7418static void
7419hold_job(cupsd_client_t *con, /* I - Client connection */
7420 ipp_attribute_t *uri) /* I - Job or Printer URI */
ef416fc2 7421{
b9faaae1
MS
7422 ipp_attribute_t *attr; /* Current job-hold-until */
7423 const char *when; /* New value */
b423cd4c 7424 int jobid; /* Job ID */
61cf44e2 7425 char scheme[HTTP_MAX_URI], /* Method portion of URI */
b423cd4c 7426 username[HTTP_MAX_URI], /* Username portion of URI */
7427 host[HTTP_MAX_URI], /* Host portion of URI */
7428 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7429 int port; /* Port portion of URI */
7430 cupsd_job_t *job; /* Job information */
7431
ef416fc2 7432
b423cd4c 7433 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->http.fd,
7434 uri->values[0].string.text);
ef416fc2 7435
7436 /*
b423cd4c 7437 * See if we have a job URI or a printer URI...
ef416fc2 7438 */
7439
b423cd4c 7440 if (!strcmp(uri->name, "printer-uri"))
7441 {
7442 /*
7443 * Got a printer URI; see if we also have a job-id attribute...
7444 */
ef416fc2 7445
b423cd4c 7446 if ((attr = ippFindAttribute(con->request, "job-id",
7447 IPP_TAG_INTEGER)) == NULL)
7448 {
7449 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 7450 _("Got a printer-uri attribute but no job-id."));
b423cd4c 7451 return;
7452 }
ef416fc2 7453
b423cd4c 7454 jobid = attr->values[0].integer;
7455 }
ef416fc2 7456 else
ef416fc2 7457 {
b423cd4c 7458 /*
7459 * Got a job URI; parse it to get the job ID...
7460 */
ef416fc2 7461
61cf44e2
MS
7462 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7463 sizeof(scheme), username, sizeof(username), host,
b423cd4c 7464 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 7465
b423cd4c 7466 if (strncmp(resource, "/jobs/", 6))
7467 {
7468 /*
7469 * Not a valid URI!
7470 */
ef416fc2 7471
b423cd4c 7472 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 7473 _("Bad job-uri \"%s\"."),
b423cd4c 7474 uri->values[0].string.text);
7475 return;
7476 }
ef416fc2 7477
b423cd4c 7478 jobid = atoi(resource + 6);
7479 }
ef416fc2 7480
ef416fc2 7481 /*
b423cd4c 7482 * See if the job exists...
ef416fc2 7483 */
7484
b423cd4c 7485 if ((job = cupsdFindJob(jobid)) == NULL)
7486 {
7487 /*
7488 * Nope - return a "not found" error...
7489 */
7490
84315f46 7491 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
b423cd4c 7492 return;
7493 }
ef416fc2 7494
7495 /*
b423cd4c 7496 * See if the job is owned by the requesting user...
ef416fc2 7497 */
7498
b423cd4c 7499 if (!validate_user(job, con, job->username, username, sizeof(username)))
7500 {
b0f6947b
MS
7501 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
7502 cupsdFindDest(job->dest));
b423cd4c 7503 return;
7504 }
ef416fc2 7505
f99f3698
MS
7506 /*
7507 * See if the job is in a state that allows holding...
7508 */
7509
7510 if (job->state_value > IPP_JOB_STOPPED)
7511 {
7512 /*
7513 * Return a "not-possible" error...
7514 */
7515
7516 send_ipp_status(con, IPP_NOT_POSSIBLE,
7517 _("Job #%d is finished and cannot be altered."),
7518 job->id);
7519 return;
7520 }
7521
ef416fc2 7522 /*
b423cd4c 7523 * Hold the job and return...
ef416fc2 7524 */
7525
b9faaae1
MS
7526 if ((attr = ippFindAttribute(con->request, "job-hold-until",
7527 IPP_TAG_KEYWORD)) == NULL)
7528 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
ef416fc2 7529
b423cd4c 7530 if (attr)
7531 {
b9faaae1 7532 when = attr->values[0].string.text;
d09495fa 7533
01ce6322 7534 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
b9faaae1 7535 "Job job-hold-until value changed by user.");
b423cd4c 7536 }
b9faaae1
MS
7537 else
7538 when = "indefinite";
ef416fc2 7539
b9faaae1
MS
7540 cupsdSetJobHoldUntil(job, when, 1);
7541 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, "Job held by \"%s\".",
7542 username);
b423cd4c 7543
7544 con->response->request.status.status_code = IPP_OK;
ef416fc2 7545}
7546
7547
61cf44e2
MS
7548/*
7549 * 'hold_new_jobs()' - Hold pending/new jobs on a printer or class.
7550 */
7551
7552static void
7553hold_new_jobs(cupsd_client_t *con, /* I - Connection */
7554 ipp_attribute_t *uri) /* I - Printer URI */
7555{
7556 http_status_t status; /* Policy status */
7557 cups_ptype_t dtype; /* Destination type (printer/class) */
7558 cupsd_printer_t *printer; /* Printer data */
7559
7560
7561 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_new_jobs(%p[%d], %s)", con,
7562 con->http.fd, uri->values[0].string.text);
7563
7564 /*
7565 * Is the destination valid?
7566 */
7567
7568 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7569 {
7570 /*
7571 * Bad URI...
7572 */
7573
7574 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 7575 _("The printer or class does not exist."));
61cf44e2
MS
7576 return;
7577 }
7578
7579 /*
7580 * Check policy...
7581 */
7582
7583 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7584 {
7585 send_http_error(con, status, printer);
7586 return;
7587 }
7588
7589 /*
7590 * Hold pending/new jobs sent to the printer...
7591 */
7592
7593 printer->holding_new_jobs = 1;
7594
7595 cupsdSetPrinterReasons(printer, "+hold-new-jobs");
61cf44e2
MS
7596
7597 if (dtype & CUPS_PRINTER_CLASS)
7598 cupsdLogMessage(CUPSD_LOG_INFO,
7599 "Class \"%s\" now holding pending/new jobs (\"%s\").",
7600 printer->name, get_username(con));
7601 else
7602 cupsdLogMessage(CUPSD_LOG_INFO,
7603 "Printer \"%s\" now holding pending/new jobs (\"%s\").",
7604 printer->name, get_username(con));
7605
7606 /*
7607 * Everything was ok, so return OK status...
7608 */
7609
7610 con->response->request.status.status_code = IPP_OK;
7611}
7612
7613
ef416fc2 7614/*
b423cd4c 7615 * 'move_job()' - Move a job to a new destination.
ef416fc2 7616 */
7617
7618static void
b423cd4c 7619move_job(cupsd_client_t *con, /* I - Client connection */
7620 ipp_attribute_t *uri) /* I - Job URI */
ef416fc2 7621{
7622 http_status_t status; /* Policy status */
7623 ipp_attribute_t *attr; /* Current attribute */
b423cd4c 7624 int jobid; /* Job ID */
ef416fc2 7625 cupsd_job_t *job; /* Current job */
d09495fa 7626 const char *src; /* Source printer/class */
b423cd4c 7627 cups_ptype_t stype, /* Source type (printer or class) */
bc44d920 7628 dtype; /* Destination type (printer/class) */
f7deaa1a 7629 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
ef416fc2 7630 username[HTTP_MAX_URI], /* Username portion of URI */
7631 host[HTTP_MAX_URI], /* Host portion of URI */
b423cd4c 7632 resource[HTTP_MAX_URI]; /* Resource portion of URI */
ef416fc2 7633 int port; /* Port portion of URI */
b423cd4c 7634 cupsd_printer_t *sprinter, /* Source printer */
7635 *dprinter; /* Destination printer */
ef416fc2 7636
7637
b423cd4c 7638 cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->http.fd,
ef416fc2 7639 uri->values[0].string.text);
7640
7641 /*
b423cd4c 7642 * Get the new printer or class...
ef416fc2 7643 */
7644
b423cd4c 7645 if ((attr = ippFindAttribute(con->request, "job-printer-uri",
7646 IPP_TAG_URI)) == NULL)
ef416fc2 7647 {
b423cd4c 7648 /*
7649 * Need job-printer-uri...
7650 */
ef416fc2 7651
b423cd4c 7652 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 7653 _("job-printer-uri attribute missing."));
b423cd4c 7654 return;
ef416fc2 7655 }
f7faf1f5 7656
f7deaa1a 7657 if (!cupsdValidateDest(attr->values[0].string.text, &dtype, &dprinter))
ef416fc2 7658 {
b423cd4c 7659 /*
7660 * Bad URI...
7661 */
ef416fc2 7662
b423cd4c 7663 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 7664 _("The printer or class does not exist."));
b423cd4c 7665 return;
ef416fc2 7666 }
7667
ef416fc2 7668 /*
b423cd4c 7669 * See if we have a job URI or a printer URI...
ef416fc2 7670 */
7671
f7deaa1a 7672 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7673 sizeof(scheme), username, sizeof(username), host,
b423cd4c 7674 sizeof(host), &port, resource, sizeof(resource));
7675
7676 if (!strcmp(uri->name, "printer-uri"))
ef416fc2 7677 {
7678 /*
b423cd4c 7679 * Got a printer URI; see if we also have a job-id attribute...
ef416fc2 7680 */
7681
b423cd4c 7682 if ((attr = ippFindAttribute(con->request, "job-id",
7683 IPP_TAG_INTEGER)) == NULL)
ef416fc2 7684 {
b423cd4c 7685 /*
7686 * Move all jobs...
7687 */
7688
f7deaa1a 7689 if ((src = cupsdValidateDest(uri->values[0].string.text, &stype,
7690 &sprinter)) == NULL)
b423cd4c 7691 {
7692 /*
7693 * Bad URI...
7694 */
7695
7696 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 7697 _("The printer or class does not exist."));
b423cd4c 7698 return;
7699 }
7700
7701 job = NULL;
7702 }
7703 else
7704 {
7705 /*
7706 * Otherwise, just move a single job...
7707 */
7708
7709 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
7710 {
7711 /*
7712 * Nope - return a "not found" error...
7713 */
7714
7715 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 7716 _("Job #%d does not exist."), attr->values[0].integer);
b423cd4c 7717 return;
7718 }
7719 else
7720 {
7721 /*
7722 * Job found, initialize source pointers...
7723 */
7724
7725 src = NULL;
7726 sprinter = NULL;
7727 }
ef416fc2 7728 }
7729 }
7730 else
7731 {
7732 /*
b423cd4c 7733 * Got a job URI; parse it to get the job ID...
ef416fc2 7734 */
7735
b423cd4c 7736 if (strncmp(resource, "/jobs/", 6))
7737 {
7738 /*
7739 * Not a valid URI!
7740 */
7741
84315f46 7742 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
b423cd4c 7743 uri->values[0].string.text);
7744 return;
7745 }
ef416fc2 7746
ef416fc2 7747 /*
b423cd4c 7748 * See if the job exists...
ef416fc2 7749 */
7750
b423cd4c 7751 jobid = atoi(resource + 6);
ef416fc2 7752
b423cd4c 7753 if ((job = cupsdFindJob(jobid)) == NULL)
ef416fc2 7754 {
7755 /*
b423cd4c 7756 * Nope - return a "not found" error...
ef416fc2 7757 */
7758
84315f46 7759 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
b423cd4c 7760 return;
ef416fc2 7761 }
7762 else
b423cd4c 7763 {
7764 /*
7765 * Job found, initialize source pointers...
7766 */
ef416fc2 7767
b423cd4c 7768 src = NULL;
7769 sprinter = NULL;
7770 }
ef416fc2 7771 }
7772
ac884b6a
MS
7773 /*
7774 * Check the policy of the destination printer...
7775 */
7776
7777 if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con,
7778 job ? job->username : NULL)) != HTTP_OK)
7779 {
7780 send_http_error(con, status, dprinter);
7781 return;
7782 }
7783
ef416fc2 7784 /*
b423cd4c 7785 * Now move the job or jobs...
ef416fc2 7786 */
7787
b423cd4c 7788 if (job)
7789 {
7790 /*
7791 * See if the job has been completed...
7792 */
7793
7794 if (job->state_value > IPP_JOB_STOPPED)
7795 {
7796 /*
7797 * Return a "not-possible" error...
7798 */
7799
7800 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 7801 _("Job #%d is finished and cannot be altered."),
b423cd4c 7802 job->id);
7803 return;
7804 }
ef416fc2 7805
b423cd4c 7806 /*
7807 * See if the job is owned by the requesting user...
7808 */
ef416fc2 7809
b423cd4c 7810 if (!validate_user(job, con, job->username, username, sizeof(username)))
7811 {
b0f6947b
MS
7812 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
7813 cupsdFindDest(job->dest));
b423cd4c 7814 return;
7815 }
ef416fc2 7816
ef416fc2 7817 /*
b423cd4c 7818 * Move the job to a different printer or class...
ef416fc2 7819 */
7820
e53920b9 7821 cupsdMoveJob(job, dprinter);
ef416fc2 7822 }
b423cd4c 7823 else
7824 {
7825 /*
7826 * Got the source printer, now look through the jobs...
7827 */
ef416fc2 7828
b423cd4c 7829 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
7830 job;
7831 job = (cupsd_job_t *)cupsArrayNext(Jobs))
7832 {
7833 /*
7834 * See if the job is pointing at the source printer or has not been
7835 * completed...
7836 */
ef416fc2 7837
88f9aafc 7838 if (_cups_strcasecmp(job->dest, src) ||
b423cd4c 7839 job->state_value > IPP_JOB_STOPPED)
7840 continue;
ef416fc2 7841
b423cd4c 7842 /*
7843 * See if the job can be moved by the requesting user...
7844 */
ef416fc2 7845
b423cd4c 7846 if (!validate_user(job, con, job->username, username, sizeof(username)))
7847 continue;
ef416fc2 7848
b423cd4c 7849 /*
7850 * Move the job to a different printer or class...
7851 */
ef416fc2 7852
e53920b9 7853 cupsdMoveJob(job, dprinter);
b423cd4c 7854 }
ef416fc2 7855 }
7856
7857 /*
b423cd4c 7858 * Start jobs if possible...
ef416fc2 7859 */
7860
b423cd4c 7861 cupsdCheckJobs();
ef416fc2 7862
7863 /*
b423cd4c 7864 * Return with "everything is OK" status...
ef416fc2 7865 */
7866
b423cd4c 7867 con->response->request.status.status_code = IPP_OK;
7868}
ef416fc2 7869
ef416fc2 7870
b423cd4c 7871/*
7872 * 'ppd_parse_line()' - Parse a PPD default line.
7873 */
ef416fc2 7874
b423cd4c 7875static int /* O - 0 on success, -1 on failure */
7876ppd_parse_line(const char *line, /* I - Line */
7877 char *option, /* O - Option name */
7878 int olen, /* I - Size of option name */
7879 char *choice, /* O - Choice name */
7880 int clen) /* I - Size of choice name */
7881{
7882 /*
7883 * Verify this is a default option line...
7884 */
ef416fc2 7885
b423cd4c 7886 if (strncmp(line, "*Default", 8))
7887 return (-1);
ef416fc2 7888
b423cd4c 7889 /*
7890 * Read the option name...
7891 */
7892
18ecb428
MS
7893 for (line += 8, olen --;
7894 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
7895 line ++)
b423cd4c 7896 if (olen > 0)
7897 {
7898 *option++ = *line;
7899 olen --;
ef416fc2 7900 }
7901
b423cd4c 7902 *option = '\0';
ef416fc2 7903
b423cd4c 7904 /*
7905 * Skip everything else up to the colon (:)...
7906 */
ef416fc2 7907
b423cd4c 7908 while (*line && *line != ':')
7909 line ++;
ef416fc2 7910
b423cd4c 7911 if (!*line)
7912 return (-1);
ef416fc2 7913
b423cd4c 7914 line ++;
ef416fc2 7915
b423cd4c 7916 /*
7917 * Now grab the option choice, skipping leading whitespace...
7918 */
ef416fc2 7919
b423cd4c 7920 while (isspace(*line & 255))
7921 line ++;
ef416fc2 7922
18ecb428
MS
7923 for (clen --;
7924 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
7925 line ++)
b423cd4c 7926 if (clen > 0)
7927 {
7928 *choice++ = *line;
7929 clen --;
7930 }
ef416fc2 7931
b423cd4c 7932 *choice = '\0';
ef416fc2 7933
b423cd4c 7934 /*
7935 * Return with no errors...
7936 */
ef416fc2 7937
b423cd4c 7938 return (0);
7939}
ef416fc2 7940
ef416fc2 7941
b423cd4c 7942/*
7943 * 'print_job()' - Print a file to a printer or class.
7944 */
ef416fc2 7945
b423cd4c 7946static void
7947print_job(cupsd_client_t *con, /* I - Client connection */
7948 ipp_attribute_t *uri) /* I - Printer URI */
7949{
7950 ipp_attribute_t *attr; /* Current attribute */
7951 ipp_attribute_t *format; /* Document-format attribute */
f7deaa1a 7952 const char *default_format; /* document-format-default value */
b423cd4c 7953 cupsd_job_t *job; /* New job */
7954 char filename[1024]; /* Job filename */
7955 mime_type_t *filetype; /* Type of file */
7956 char super[MIME_MAX_SUPER], /* Supertype of file */
7957 type[MIME_MAX_TYPE], /* Subtype of file */
7958 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
7959 /* Textual name of mime type */
7960 cupsd_printer_t *printer; /* Printer data */
7961 struct stat fileinfo; /* File information */
7962 int kbytes; /* Size of file */
7963 int compression; /* Document compression */
ef416fc2 7964
ef416fc2 7965
b423cd4c 7966 cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->http.fd,
7967 uri->values[0].string.text);
ef416fc2 7968
b423cd4c 7969 /*
7970 * Validate print file attributes, for now just document-format and
7971 * compression (CUPS only supports "none" and "gzip")...
7972 */
ef416fc2 7973
b423cd4c 7974 compression = CUPS_FILE_NONE;
ef416fc2 7975
b423cd4c 7976 if ((attr = ippFindAttribute(con->request, "compression",
7977 IPP_TAG_KEYWORD)) != NULL)
7978 {
7979 if (strcmp(attr->values[0].string.text, "none")
7980#ifdef HAVE_LIBZ
7981 && strcmp(attr->values[0].string.text, "gzip")
7982#endif /* HAVE_LIBZ */
7983 )
7984 {
7985 send_ipp_status(con, IPP_ATTRIBUTES,
84315f46 7986 _("Unsupported compression \"%s\"."),
b423cd4c 7987 attr->values[0].string.text);
7988 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
7989 "compression", NULL, attr->values[0].string.text);
7990 return;
7991 }
ef416fc2 7992
b423cd4c 7993#ifdef HAVE_LIBZ
7994 if (!strcmp(attr->values[0].string.text, "gzip"))
7995 compression = CUPS_FILE_GZIP;
7996#endif /* HAVE_LIBZ */
7997 }
ef416fc2 7998
b423cd4c 7999 /*
8000 * Do we have a file to print?
8001 */
ef416fc2 8002
b423cd4c 8003 if (!con->filename)
8004 {
84315f46 8005 send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
b423cd4c 8006 return;
8007 }
ef416fc2 8008
f7deaa1a 8009 /*
8010 * Is the destination valid?
8011 */
8012
8013 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
8014 {
8015 /*
8016 * Bad URI...
8017 */
8018
8019 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 8020 _("The printer or class does not exist."));
f7deaa1a 8021 return;
8022 }
8023
b423cd4c 8024 /*
8025 * Is it a format we support?
8026 */
ef416fc2 8027
b423cd4c 8028 if ((format = ippFindAttribute(con->request, "document-format",
8029 IPP_TAG_MIMETYPE)) != NULL)
8030 {
8031 /*
8032 * Grab format from client...
8033 */
8034
c5b24bfa 8035 if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]", super,
f7deaa1a 8036 type) != 2)
b423cd4c 8037 {
8038 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 8039 _("Bad document-format \"%s\"."),
b423cd4c 8040 format->values[0].string.text);
8041 return;
ef416fc2 8042 }
b423cd4c 8043 }
f7deaa1a 8044 else if ((default_format = cupsGetOption("document-format",
8045 printer->num_options,
8046 printer->options)) != NULL)
8047 {
8048 /*
8049 * Use default document format...
8050 */
8051
c5b24bfa 8052 if (sscanf(default_format, "%15[^/]/%255[^;]", super, type) != 2)
f7deaa1a 8053 {
8054 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 8055 _("Bad document-format \"%s\"."),
f7deaa1a 8056 default_format);
8057 return;
8058 }
8059 }
b423cd4c 8060 else
8061 {
8062 /*
f7deaa1a 8063 * Auto-type it!
b423cd4c 8064 */
8065
5a9febac
MS
8066 strlcpy(super, "application", sizeof(super));
8067 strlcpy(type, "octet-stream", sizeof(type));
b423cd4c 8068 }
ef416fc2 8069
b423cd4c 8070 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
8071 {
ef416fc2 8072 /*
b423cd4c 8073 * Auto-type the file...
ef416fc2 8074 */
8075
b423cd4c 8076 ipp_attribute_t *doc_name; /* document-name attribute */
8077
8078
a0f6818e 8079 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job ???] Auto-typing file...");
b423cd4c 8080
8081 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
8082 filetype = mimeFileType(MimeDatabase, con->filename,
8083 doc_name ? doc_name->values[0].string.text : NULL,
8084 &compression);
8085
f7deaa1a 8086 if (!filetype)
8087 filetype = mimeType(MimeDatabase, super, type);
a0f6818e
MS
8088
8089 cupsdLogMessage(CUPSD_LOG_INFO, "[Job ???] Request file type is %s/%s.",
8090 filetype->super, filetype->type);
f7deaa1a 8091 }
8092 else
8093 filetype = mimeType(MimeDatabase, super, type);
ef416fc2 8094
f7deaa1a 8095 if (filetype &&
8096 (!format ||
8097 (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
8098 {
8099 /*
8100 * Replace the document-format attribute value with the auto-typed or
8101 * default one.
8102 */
b423cd4c 8103
f7deaa1a 8104 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
8105 filetype->type);
ed486911 8106
f7deaa1a 8107 if (format)
8108 {
8109 _cupsStrFree(format->values[0].string.text);
8110
8111 format->values[0].string.text = _cupsStrAlloc(mimetype);
ef416fc2 8112 }
b423cd4c 8113 else
f7deaa1a 8114 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
8115 "document-format", NULL, mimetype);
ef416fc2 8116 }
f7deaa1a 8117 else if (!filetype)
b423cd4c 8118 {
8119 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
84315f46 8120 _("Unsupported document-format \"%s\"."),
c7017ecc
MS
8121 format ? format->values[0].string.text :
8122 "application/octet-stream");
b423cd4c 8123 cupsdLogMessage(CUPSD_LOG_INFO,
8124 "Hint: Do you have the raw file printing rules enabled?");
8125
8126 if (format)
8127 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
8128 "document-format", NULL, format->values[0].string.text);
8129
8130 return;
8131 }
8132
b423cd4c 8133 /*
8134 * Read any embedded job ticket info from PS files...
8135 */
8136
88f9aafc
MS
8137 if (!_cups_strcasecmp(filetype->super, "application") &&
8138 (!_cups_strcasecmp(filetype->type, "postscript") ||
8139 !_cups_strcasecmp(filetype->type, "pdf")))
c5571a1d 8140 read_job_ticket(con);
b423cd4c 8141
8142 /*
8143 * Create the job object...
8144 */
8145
f7deaa1a 8146 if ((job = add_job(con, printer, filetype)) == NULL)
b423cd4c 8147 return;
8148
8149 /*
8150 * Update quota data...
8151 */
8152
8153 if (stat(con->filename, &fileinfo))
8154 kbytes = 0;
8155 else
8156 kbytes = (fileinfo.st_size + 1023) / 1024;
8157
8158 cupsdUpdateQuota(printer, job->username, 0, kbytes);
8159
8160 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
8161 IPP_TAG_INTEGER)) != NULL)
8162 attr->values[0].integer += kbytes;
8163
ef416fc2 8164 /*
8165 * Add the job file...
8166 */
8167
8168 if (add_file(con, job, filetype, compression))
8169 return;
8170
8171 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
8172 job->num_files);
8173 rename(con->filename, filename);
8174 cupsdClearString(&con->filename);
8175
8176 /*
8177 * See if we need to add the ending sheet...
8178 */
8179
91c84a35
MS
8180 if (cupsdTimeoutJob(job))
8181 return;
ef416fc2 8182
ef416fc2 8183 /*
8184 * Log and save the job...
8185 */
8186
75bd9771
MS
8187 cupsdLogJob(job, CUPSD_LOG_INFO,
8188 "File of type %s/%s queued by \"%s\".",
8189 filetype->super, filetype->type, job->username);
8190 cupsdLogJob(job, CUPSD_LOG_DEBUG, "hold_until=%d", (int)job->hold_until);
b19ccc9e
MS
8191 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
8192 job->dest, job->username);
ef416fc2 8193
ef416fc2 8194 /*
8195 * Start the job if possible...
8196 */
8197
8198 cupsdCheckJobs();
8199}
8200
8201
8202/*
c5571a1d 8203 * 'read_job_ticket()' - Read a job ticket embedded in a print file.
ef416fc2 8204 *
c5571a1d 8205 * This function only gets called when printing a single PDF or PostScript
ef416fc2 8206 * file using the Print-Job operation. It doesn't work for Create-Job +
8207 * Send-File, since the job attributes need to be set at job creation
c5571a1d
MS
8208 * time for banners to work. The embedded job ticket stuff is here
8209 * primarily to allow the Windows printer driver for CUPS to pass in JCL
ef416fc2 8210 * options and IPP attributes which otherwise would be lost.
8211 *
c5571a1d 8212 * The format of a job ticket is simple:
ef416fc2 8213 *
8214 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
8215 *
8216 * %cupsJobTicket: attr1=value1
8217 * %cupsJobTicket: attr2=value2
8218 * ...
8219 * %cupsJobTicket: attrN=valueN
8220 *
8221 * Job ticket lines must appear immediately after the first line that
c5571a1d
MS
8222 * specifies PostScript (%!PS-Adobe-3.0) or PDF (%PDF) format, and CUPS
8223 * stops looking for job ticket info when it finds a line that does not begin
ef416fc2 8224 * with "%cupsJobTicket:".
8225 *
8226 * The maximum length of a job ticket line, including the prefix, is
8227 * 255 characters to conform with the Adobe DSC.
8228 *
8229 * Read-only attributes are rejected with a notice to the error log in
8230 * case a malicious user tries anything. Since the job ticket is read
8231 * prior to attribute validation in print_job(), job ticket attributes
8232 * will go through the same validation as IPP attributes...
8233 */
8234
8235static void
c5571a1d 8236read_job_ticket(cupsd_client_t *con) /* I - Client connection */
ef416fc2 8237{
8238 cups_file_t *fp; /* File to read from */
8239 char line[256]; /* Line data */
8240 int num_options; /* Number of options */
8241 cups_option_t *options; /* Options */
8242 ipp_t *ticket; /* New attributes */
8243 ipp_attribute_t *attr, /* Current attribute */
8244 *attr2, /* Job attribute */
8245 *prev2; /* Previous job attribute */
8246
8247
8248 /*
8249 * First open the print file...
8250 */
8251
8252 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
8253 {
8254 cupsdLogMessage(CUPSD_LOG_ERROR,
c5571a1d 8255 "Unable to open print file for job ticket - %s",
ef416fc2 8256 strerror(errno));
8257 return;
8258 }
8259
8260 /*
8261 * Skip the first line...
8262 */
8263
8264 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
8265 {
8266 cupsdLogMessage(CUPSD_LOG_ERROR,
c5571a1d 8267 "Unable to read from print file for job ticket - %s",
ef416fc2 8268 strerror(errno));
8269 cupsFileClose(fp);
8270 return;
8271 }
8272
c5571a1d 8273 if (strncmp(line, "%!PS-Adobe-", 11) && strncmp(line, "%PDF-", 5))
ef416fc2 8274 {
8275 /*
8276 * Not a DSC-compliant file, so no job ticket info will be available...
8277 */
8278
8279 cupsFileClose(fp);
8280 return;
8281 }
8282
8283 /*
8284 * Read job ticket info from the file...
8285 */
8286
8287 num_options = 0;
8288 options = NULL;
8289
fa73b229 8290 while (cupsFileGets(fp, line, sizeof(line)))
ef416fc2 8291 {
8292 /*
8293 * Stop at the first non-ticket line...
8294 */
8295
fa73b229 8296 if (strncmp(line, "%cupsJobTicket:", 15))
ef416fc2 8297 break;
8298
8299 /*
8300 * Add the options to the option array...
8301 */
8302
8303 num_options = cupsParseOptions(line + 15, num_options, &options);
8304 }
8305
8306 /*
8307 * Done with the file; see if we have any options...
8308 */
8309
8310 cupsFileClose(fp);
8311
8312 if (num_options == 0)
8313 return;
8314
8315 /*
8316 * OK, convert the options to an attribute list, and apply them to
8317 * the request...
8318 */
8319
8320 ticket = ippNew();
8321 cupsEncodeOptions(ticket, num_options, options);
8322
8323 /*
8324 * See what the user wants to change.
8325 */
8326
fa73b229 8327 for (attr = ticket->attrs; attr; attr = attr->next)
ef416fc2 8328 {
8329 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
8330 continue;
8331
8332 if (!strcmp(attr->name, "job-originating-host-name") ||
8333 !strcmp(attr->name, "job-originating-user-name") ||
8334 !strcmp(attr->name, "job-media-sheets-completed") ||
8335 !strcmp(attr->name, "job-k-octets") ||
8336 !strcmp(attr->name, "job-id") ||
8337 !strncmp(attr->name, "job-state", 9) ||
8338 !strncmp(attr->name, "time-at-", 8))
8339 continue; /* Read-only attrs */
8340
fa73b229 8341 if ((attr2 = ippFindAttribute(con->request, attr->name,
8342 IPP_TAG_ZERO)) != NULL)
ef416fc2 8343 {
8344 /*
8345 * Some other value; first free the old value...
8346 */
8347
8348 if (con->request->attrs == attr2)
8349 {
8350 con->request->attrs = attr2->next;
8351 prev2 = NULL;
8352 }
8353 else
8354 {
fa73b229 8355 for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
ef416fc2 8356 if (prev2->next == attr2)
8357 {
8358 prev2->next = attr2->next;
8359 break;
8360 }
8361 }
8362
8363 if (con->request->last == attr2)
8364 con->request->last = prev2;
8365
a2326b5b 8366 ippDeleteAttribute(NULL, attr2);
ef416fc2 8367 }
8368
8369 /*
8370 * Add new option by copying it...
8371 */
8372
a2326b5b 8373 ippCopyAttribute(con->request, attr, 0);
ef416fc2 8374 }
8375
8376 /*
8377 * Then free the attribute list and option array...
8378 */
8379
8380 ippDelete(ticket);
8381 cupsFreeOptions(num_options, options);
8382}
8383
8384
8385/*
8386 * 'reject_jobs()' - Reject print jobs to a printer.
8387 */
8388
8389static void
8390reject_jobs(cupsd_client_t *con, /* I - Client connection */
8391 ipp_attribute_t *uri) /* I - Printer or class URI */
8392{
8393 http_status_t status; /* Policy status */
bc44d920 8394 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 8395 cupsd_printer_t *printer; /* Printer data */
8396 ipp_attribute_t *attr; /* printer-state-message text */
8397
8398
8399 cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
8400 con->http.fd, uri->values[0].string.text);
8401
8402 /*
8403 * Is the destination valid?
8404 */
8405
f7deaa1a 8406 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 8407 {
8408 /*
8409 * Bad URI...
8410 */
8411
8412 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 8413 _("The printer or class does not exist."));
ef416fc2 8414 return;
8415 }
8416
8417 /*
8418 * Check policy...
8419 */
8420
8421 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8422 {
f899b121 8423 send_http_error(con, status, printer);
ef416fc2 8424 return;
8425 }
8426
8427 /*
8428 * Reject jobs sent to the printer...
8429 */
8430
8431 printer->accepting = 0;
8432
8433 if ((attr = ippFindAttribute(con->request, "printer-state-message",
8434 IPP_TAG_TEXT)) == NULL)
5a9febac
MS
8435 strlcpy(printer->state_message, "Rejecting Jobs",
8436 sizeof(printer->state_message));
ef416fc2 8437 else
8438 strlcpy(printer->state_message, attr->values[0].string.text,
8439 sizeof(printer->state_message));
8440
f8b3a85b
MS
8441 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
8442 "No longer accepting jobs.");
8443
ef416fc2 8444 if (dtype & CUPS_PRINTER_CLASS)
8445 {
3dfe78b3 8446 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
ef416fc2 8447
8448 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
f7deaa1a 8449 printer->name, get_username(con));
ef416fc2 8450 }
8451 else
8452 {
3dfe78b3 8453 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 8454
8455 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
f7deaa1a 8456 printer->name, get_username(con));
ef416fc2 8457 }
8458
8459 /*
8460 * Everything was ok, so return OK status...
8461 */
8462
8463 con->response->request.status.status_code = IPP_OK;
8464}
8465
8466
61cf44e2
MS
8467/*
8468 * 'release_held_new_jobs()' - Release pending/new jobs on a printer or class.
8469 */
8470
8471static void
8472release_held_new_jobs(
8473 cupsd_client_t *con, /* I - Connection */
8474 ipp_attribute_t *uri) /* I - Printer URI */
8475{
8476 http_status_t status; /* Policy status */
8477 cups_ptype_t dtype; /* Destination type (printer/class) */
8478 cupsd_printer_t *printer; /* Printer data */
8479
8480
8481 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_held_new_jobs(%p[%d], %s)", con,
8482 con->http.fd, uri->values[0].string.text);
8483
8484 /*
8485 * Is the destination valid?
8486 */
8487
8488 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8489 {
8490 /*
8491 * Bad URI...
8492 */
8493
8494 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 8495 _("The printer or class does not exist."));
61cf44e2
MS
8496 return;
8497 }
8498
8499 /*
8500 * Check policy...
8501 */
8502
8503 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8504 {
8505 send_http_error(con, status, printer);
8506 return;
8507 }
8508
8509 /*
8510 * Hold pending/new jobs sent to the printer...
8511 */
8512
8513 printer->holding_new_jobs = 0;
8514
8515 cupsdSetPrinterReasons(printer, "-hold-new-jobs");
61cf44e2
MS
8516
8517 if (dtype & CUPS_PRINTER_CLASS)
8518 cupsdLogMessage(CUPSD_LOG_INFO,
8519 "Class \"%s\" now printing pending/new jobs (\"%s\").",
8520 printer->name, get_username(con));
8521 else
8522 cupsdLogMessage(CUPSD_LOG_INFO,
8523 "Printer \"%s\" now printing pending/new jobs (\"%s\").",
8524 printer->name, get_username(con));
8525
8526 /*
8527 * Everything was ok, so return OK status...
8528 */
8529
8530 con->response->request.status.status_code = IPP_OK;
8531}
8532
8533
ef416fc2 8534/*
8535 * 'release_job()' - Release a held print job.
8536 */
8537
8538static void
8539release_job(cupsd_client_t *con, /* I - Client connection */
8540 ipp_attribute_t *uri) /* I - Job or Printer URI */
8541{
8542 ipp_attribute_t *attr; /* Current attribute */
8543 int jobid; /* Job ID */
61cf44e2 8544 char scheme[HTTP_MAX_URI], /* Method portion of URI */
ef416fc2 8545 username[HTTP_MAX_URI], /* Username portion of URI */
8546 host[HTTP_MAX_URI], /* Host portion of URI */
8547 resource[HTTP_MAX_URI]; /* Resource portion of URI */
8548 int port; /* Port portion of URI */
8549 cupsd_job_t *job; /* Job information */
8550
8551
8552 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
8553 con->http.fd, uri->values[0].string.text);
8554
8555 /*
8556 * See if we have a job URI or a printer URI...
8557 */
8558
8559 if (!strcmp(uri->name, "printer-uri"))
8560 {
8561 /*
8562 * Got a printer URI; see if we also have a job-id attribute...
8563 */
8564
fa73b229 8565 if ((attr = ippFindAttribute(con->request, "job-id",
8566 IPP_TAG_INTEGER)) == NULL)
ef416fc2 8567 {
8568 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 8569 _("Got a printer-uri attribute but no job-id."));
ef416fc2 8570 return;
8571 }
8572
8573 jobid = attr->values[0].integer;
8574 }
8575 else
8576 {
8577 /*
8578 * Got a job URI; parse it to get the job ID...
8579 */
8580
61cf44e2
MS
8581 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8582 sizeof(scheme), username, sizeof(username), host,
a4d04587 8583 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 8584
8585 if (strncmp(resource, "/jobs/", 6))
8586 {
8587 /*
8588 * Not a valid URI!
8589 */
8590
84315f46 8591 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
ef416fc2 8592 uri->values[0].string.text);
8593 return;
8594 }
8595
8596 jobid = atoi(resource + 6);
8597 }
8598
8599 /*
8600 * See if the job exists...
8601 */
8602
8603 if ((job = cupsdFindJob(jobid)) == NULL)
8604 {
8605 /*
8606 * Nope - return a "not found" error...
8607 */
8608
84315f46 8609 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
ef416fc2 8610 return;
8611 }
8612
8613 /*
8614 * See if job is "held"...
8615 */
8616
bd7854cb 8617 if (job->state_value != IPP_JOB_HELD)
ef416fc2 8618 {
8619 /*
8620 * Nope - return a "not possible" error...
8621 */
8622
84315f46 8623 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held."), jobid);
ef416fc2 8624 return;
8625 }
8626
8627 /*
8628 * See if the job is owned by the requesting user...
8629 */
8630
8631 if (!validate_user(job, con, job->username, username, sizeof(username)))
8632 {
b0f6947b
MS
8633 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8634 cupsdFindDest(job->dest));
ef416fc2 8635 return;
8636 }
8637
8638 /*
8639 * Reset the job-hold-until value to "no-hold"...
8640 */
8641
fa73b229 8642 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
8643 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 8644 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
8645
fa73b229 8646 if (attr)
ef416fc2 8647 {
757d2cad 8648 _cupsStrFree(attr->values[0].string.text);
4400e98d 8649
ef416fc2 8650 attr->value_tag = IPP_TAG_KEYWORD;
757d2cad 8651 attr->values[0].string.text = _cupsStrAlloc("no-hold");
d09495fa 8652
01ce6322 8653 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
d09495fa 8654 "Job job-hold-until value changed by user.");
3e7fe0ca 8655 ippSetString(job->attrs, &job->reasons, 0, "none");
ef416fc2 8656 }
8657
8658 /*
8659 * Release the job and return...
8660 */
8661
8662 cupsdReleaseJob(job);
8663
01ce6322 8664 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
d09495fa 8665 "Job released by user.");
8666
75bd9771 8667 cupsdLogJob(job, CUPSD_LOG_INFO, "Released by \"%s\".", username);
ef416fc2 8668
8669 con->response->request.status.status_code = IPP_OK;
005dd1eb
MS
8670
8671 cupsdCheckJobs();
ef416fc2 8672}
8673
8674
8675/*
8676 * 'renew_subscription()' - Renew an existing subscription...
8677 */
8678
8679static void
8680renew_subscription(
8681 cupsd_client_t *con, /* I - Client connection */
8682 int sub_id) /* I - Subscription ID */
8683{
bd7854cb 8684 http_status_t status; /* Policy status */
8685 cupsd_subscription_t *sub; /* Subscription */
8686 ipp_attribute_t *lease; /* notify-lease-duration */
8687
8688
8689 cupsdLogMessage(CUPSD_LOG_DEBUG2,
8690 "renew_subscription(con=%p[%d], sub_id=%d)",
8691 con, con->http.fd, sub_id);
8692
8693 /*
8694 * Is the subscription ID valid?
8695 */
8696
8697 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
8698 {
8699 /*
8700 * Bad subscription ID...
8701 */
8702
84315f46
MS
8703 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
8704 sub_id);
bd7854cb 8705 return;
8706 }
8707
8708 if (sub->job)
8709 {
8710 /*
8711 * Job subscriptions cannot be renewed...
8712 */
8713
8714 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 8715 _("Job subscriptions cannot be renewed."));
bd7854cb 8716 return;
8717 }
8718
8719 /*
8720 * Check policy...
8721 */
8722
8723 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
8724 DefaultPolicyPtr,
8725 con, sub->owner)) != HTTP_OK)
8726 {
f899b121 8727 send_http_error(con, status, sub->dest);
bd7854cb 8728 return;
8729 }
8730
8731 /*
8732 * Renew the subscription...
8733 */
8734
8735 lease = ippFindAttribute(con->request, "notify-lease-duration",
8736 IPP_TAG_INTEGER);
8737
8738 sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
8739
8740 if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
8741 {
8742 cupsdLogMessage(CUPSD_LOG_INFO,
8743 "renew_subscription: Limiting notify-lease-duration to "
8744 "%d seconds.",
8745 MaxLeaseDuration);
8746 sub->lease = MaxLeaseDuration;
8747 }
8748
8749 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
8750
3dfe78b3 8751 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
bd7854cb 8752
8753 con->response->request.status.status_code = IPP_OK;
b94498cf 8754
8755 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
8756 "notify-lease-duration", sub->lease);
ef416fc2 8757}
8758
8759
8760/*
8761 * 'restart_job()' - Restart an old print job.
8762 */
8763
8764static void
8765restart_job(cupsd_client_t *con, /* I - Client connection */
8766 ipp_attribute_t *uri) /* I - Job or Printer URI */
8767{
8768 ipp_attribute_t *attr; /* Current attribute */
8769 int jobid; /* Job ID */
238c3832 8770 cupsd_job_t *job; /* Job information */
61cf44e2 8771 char scheme[HTTP_MAX_URI], /* Method portion of URI */
ef416fc2 8772 username[HTTP_MAX_URI], /* Username portion of URI */
8773 host[HTTP_MAX_URI], /* Host portion of URI */
8774 resource[HTTP_MAX_URI]; /* Resource portion of URI */
8775 int port; /* Port portion of URI */
ef416fc2 8776
8777
8778 cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
8779 con->http.fd, uri->values[0].string.text);
8780
8781 /*
8782 * See if we have a job URI or a printer URI...
8783 */
8784
8785 if (!strcmp(uri->name, "printer-uri"))
8786 {
8787 /*
8788 * Got a printer URI; see if we also have a job-id attribute...
8789 */
8790
fa73b229 8791 if ((attr = ippFindAttribute(con->request, "job-id",
8792 IPP_TAG_INTEGER)) == NULL)
ef416fc2 8793 {
8794 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 8795 _("Got a printer-uri attribute but no job-id."));
ef416fc2 8796 return;
8797 }
8798
8799 jobid = attr->values[0].integer;
8800 }
8801 else
8802 {
8803 /*
8804 * Got a job URI; parse it to get the job ID...
8805 */
8806
61cf44e2
MS
8807 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8808 sizeof(scheme), username, sizeof(username), host,
a4d04587 8809 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 8810
fa73b229 8811 if (strncmp(resource, "/jobs/", 6))
ef416fc2 8812 {
8813 /*
8814 * Not a valid URI!
8815 */
8816
84315f46 8817 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
ef416fc2 8818 uri->values[0].string.text);
8819 return;
8820 }
8821
8822 jobid = atoi(resource + 6);
8823 }
8824
8825 /*
8826 * See if the job exists...
8827 */
8828
8829 if ((job = cupsdFindJob(jobid)) == NULL)
8830 {
8831 /*
8832 * Nope - return a "not found" error...
8833 */
8834
84315f46 8835 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
ef416fc2 8836 return;
8837 }
8838
8839 /*
8840 * See if job is in any of the "completed" states...
8841 */
8842
bd7854cb 8843 if (job->state_value <= IPP_JOB_PROCESSING)
ef416fc2 8844 {
8845 /*
8846 * Nope - return a "not possible" error...
8847 */
8848
84315f46 8849 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete."),
ef416fc2 8850 jobid);
8851 return;
8852 }
8853
8854 /*
8855 * See if we have retained the job files...
8856 */
8857
bd7854cb 8858 cupsdLoadJob(job);
8859
07725fee 8860 if (!job->attrs || job->num_files == 0)
ef416fc2 8861 {
8862 /*
8863 * Nope - return a "not possible" error...
8864 */
8865
8866 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 8867 _("Job #%d cannot be restarted - no files."), jobid);
ef416fc2 8868 return;
8869 }
8870
8871 /*
8872 * See if the job is owned by the requesting user...
8873 */
8874
8875 if (!validate_user(job, con, job->username, username, sizeof(username)))
8876 {
b0f6947b
MS
8877 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8878 cupsdFindDest(job->dest));
ef416fc2 8879 return;
8880 }
8881
8882 /*
238c3832 8883 * See if the job-hold-until attribute is specified...
ef416fc2 8884 */
8885
238c3832
MS
8886 if ((attr = ippFindAttribute(con->request, "job-hold-until",
8887 IPP_TAG_KEYWORD)) == NULL)
8888 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
8889
8890 if (attr && strcmp(attr->values[0].string.text, "no-hold"))
8891 {
8892 /*
8893 * Return the job to a held state...
8894 */
8895
8896 cupsdLogJob(job, CUPSD_LOG_DEBUG,
8897 "Restarted by \"%s\" with job-hold-until=%s.",
8898 username, attr->values[0].string.text);
8899 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
8900
8901 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE,
8902 NULL, job, "Job restarted by user with job-hold-until=%s",
8903 attr->values[0].string.text);
8904 }
8905 else
8906 {
8907 /*
8908 * Restart the job...
8909 */
8910
8911 cupsdRestartJob(job);
f11a948a 8912 cupsdCheckJobs();
238c3832 8913 }
ef416fc2 8914
75bd9771 8915 cupsdLogJob(job, CUPSD_LOG_INFO, "Restarted by \"%s\".", username);
ef416fc2 8916
8917 con->response->request.status.status_code = IPP_OK;
8918}
8919
8920
8921/*
8922 * 'save_auth_info()' - Save authentication information for a job.
8923 */
8924
8925static void
f7deaa1a 8926save_auth_info(
8927 cupsd_client_t *con, /* I - Client connection */
8928 cupsd_job_t *job, /* I - Job */
8929 ipp_attribute_t *auth_info) /* I - auth-info attribute, if any */
ef416fc2 8930{
09a101d6 8931 int i; /* Looping var */
8932 char filename[1024]; /* Job authentication filename */
8933 cups_file_t *fp; /* Job authentication file */
85dda01c 8934 char line[65536]; /* Line for file */
09a101d6 8935 cupsd_printer_t *dest; /* Destination printer/class */
ef416fc2 8936
8937
8938 /*
8939 * This function saves the in-memory authentication information for
8940 * a job so that it can be used to authenticate with a remote host.
8941 * The information is stored in a file that is readable only by the
f7deaa1a 8942 * root user. The fields are Base-64 encoded, each on a separate line,
8943 * followed by random number (up to 1024) of newlines to limit the
8944 * amount of information that is exposed.
ef416fc2 8945 *
8946 * Because of the potential for exposing of authentication information,
8947 * this functionality is only enabled when running cupsd as root.
8948 *
8949 * This caching only works for the Basic and BasicDigest authentication
8950 * types. Digest authentication cannot be cached this way, and in
8951 * the future Kerberos authentication may make all of this obsolete.
8952 *
8953 * Authentication information is saved whenever an authenticated
8954 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
8955 * performed.
8956 *
8957 * This information is deleted after a job is completed or canceled,
8958 * so reprints may require subsequent re-authentication.
8959 */
8960
8961 if (RunUser)
8962 return;
8963
09a101d6 8964 if ((dest = cupsdFindDest(job->dest)) == NULL)
8965 return;
8966
ef416fc2 8967 /*
8968 * Create the authentication file and change permissions...
8969 */
8970
8971 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
8972 if ((fp = cupsFileOpen(filename, "w")) == NULL)
8973 {
8974 cupsdLogMessage(CUPSD_LOG_ERROR,
8975 "Unable to save authentication info to \"%s\" - %s",
8976 filename, strerror(errno));
8977 return;
8978 }
8979
8980 fchown(cupsFileNumber(fp), 0, 0);
8981 fchmod(cupsFileNumber(fp), 0400);
8982
94436c5a 8983 cupsFilePuts(fp, "CUPSD-AUTH-V3\n");
dcb445bc 8984
88f9aafc
MS
8985 for (i = 0;
8986 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
8987 i ++)
8988 cupsdClearString(job->auth_env + i);
8989
09a101d6 8990 if (auth_info && auth_info->num_values == dest->num_auth_info_required)
f7deaa1a 8991 {
8992 /*
09a101d6 8993 * Write 1 to 3 auth values...
f7deaa1a 8994 */
ef416fc2 8995
88f9aafc
MS
8996 for (i = 0;
8997 i < auth_info->num_values &&
8998 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
8999 i ++)
f7deaa1a 9000 {
94436c5a
MS
9001 if (strcmp(dest->auth_info_required[i], "negotiate"))
9002 {
9003 httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text,
9004 strlen(auth_info->values[i].string.text));
9005 cupsFilePutConf(fp, dest->auth_info_required[i], line);
9006 }
9007 else
9008 cupsFilePutConf(fp, dest->auth_info_required[i],
9009 auth_info->values[i].string.text);
09a101d6 9010
9011 if (!strcmp(dest->auth_info_required[i], "username"))
88f9aafc 9012 cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s",
09a101d6 9013 auth_info->values[i].string.text);
9014 else if (!strcmp(dest->auth_info_required[i], "domain"))
88f9aafc 9015 cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s",
09a101d6 9016 auth_info->values[i].string.text);
9017 else if (!strcmp(dest->auth_info_required[i], "password"))
88f9aafc
MS
9018 cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s",
9019 auth_info->values[i].string.text);
9020 else if (!strcmp(dest->auth_info_required[i], "negotiate"))
9021 cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s",
9022 auth_info->values[i].string.text);
9023 else
dcb445bc 9024 i --;
f7deaa1a 9025 }
9026 }
dcb445bc
MS
9027 else if (auth_info && auth_info->num_values == 2 &&
9028 dest->num_auth_info_required == 1 &&
9029 !strcmp(dest->auth_info_required[0], "negotiate"))
9030 {
9031 /*
9032 * Allow fallback to username+password for Kerberized queues...
9033 */
9034
9035 httpEncode64_2(line, sizeof(line), auth_info->values[0].string.text,
9036 strlen(auth_info->values[0].string.text));
9037 cupsFilePutConf(fp, "username", line);
9038
9039 cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s",
9040 auth_info->values[0].string.text);
9041
9042 httpEncode64_2(line, sizeof(line), auth_info->values[1].string.text,
9043 strlen(auth_info->values[1].string.text));
9044 cupsFilePutConf(fp, "password", line);
9045
9046 cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s",
9047 auth_info->values[1].string.text);
9048 }
09a101d6 9049 else if (con->username[0])
f7deaa1a 9050 {
9051 /*
9052 * Write the authenticated username...
9053 */
ef416fc2 9054
f7deaa1a 9055 httpEncode64_2(line, sizeof(line), con->username, strlen(con->username));
dcb445bc 9056 cupsFilePutConf(fp, "username", line);
ef416fc2 9057
88f9aafc 9058 cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s", con->username);
09a101d6 9059
f7deaa1a 9060 /*
9061 * Write the authenticated password...
9062 */
9063
9064 httpEncode64_2(line, sizeof(line), con->password, strlen(con->password));
dcb445bc 9065 cupsFilePutConf(fp, "password", line);
09a101d6 9066
88f9aafc 9067 cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s", con->password);
f7deaa1a 9068 }
ef416fc2 9069
07ed0e9a 9070#ifdef HAVE_GSSAPI
eac3a0a0 9071 if (con->gss_uid > 0)
07ed0e9a 9072 {
dcb445bc 9073 cupsFilePrintf(fp, "uid %d\n", (int)con->gss_uid);
07ed0e9a
MS
9074 cupsdSetStringf(&job->auth_uid, "AUTH_UID=%d", (int)con->gss_uid);
9075 }
9076#endif /* HAVE_GSSAPI */
9077
ef416fc2 9078 /*
9079 * Write a random number of newlines to the end of the file...
9080 */
9081
41681883 9082 for (i = (CUPS_RAND() % 1024); i >= 0; i --)
ef416fc2 9083 cupsFilePutChar(fp, '\n');
9084
9085 /*
9086 * Close the file and return...
9087 */
9088
9089 cupsFileClose(fp);
9090}
9091
9092
9093/*
9094 * 'send_document()' - Send a file to a printer or class.
9095 */
9096
9097static void
9098send_document(cupsd_client_t *con, /* I - Client connection */
9099 ipp_attribute_t *uri) /* I - Printer URI */
9100{
9101 ipp_attribute_t *attr; /* Current attribute */
a0f6818e
MS
9102 ipp_attribute_t *format; /* Request's document-format attribute */
9103 ipp_attribute_t *jformat; /* Job's document-format attribute */
f7deaa1a 9104 const char *default_format;/* document-format-default value */
ef416fc2 9105 int jobid; /* Job ID number */
9106 cupsd_job_t *job; /* Current job */
9107 char job_uri[HTTP_MAX_URI],
9108 /* Job URI */
61cf44e2 9109 scheme[HTTP_MAX_URI],
ef416fc2 9110 /* Method portion of URI */
9111 username[HTTP_MAX_URI],
9112 /* Username portion of URI */
9113 host[HTTP_MAX_URI],
9114 /* Host portion of URI */
9115 resource[HTTP_MAX_URI];
9116 /* Resource portion of URI */
9117 int port; /* Port portion of URI */
9118 mime_type_t *filetype; /* Type of file */
9119 char super[MIME_MAX_SUPER],
9120 /* Supertype of file */
9121 type[MIME_MAX_TYPE],
9122 /* Subtype of file */
9123 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
9124 /* Textual name of mime type */
9125 char filename[1024]; /* Job filename */
9126 cupsd_printer_t *printer; /* Current printer */
9127 struct stat fileinfo; /* File information */
9128 int kbytes; /* Size of file */
9129 int compression; /* Type of compression */
75bd9771 9130 int start_job; /* Start the job? */
ef416fc2 9131
9132
9133 cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
9134 con->http.fd, uri->values[0].string.text);
9135
9136 /*
9137 * See if we have a job URI or a printer URI...
9138 */
9139
9140 if (!strcmp(uri->name, "printer-uri"))
9141 {
9142 /*
9143 * Got a printer URI; see if we also have a job-id attribute...
9144 */
9145
fa73b229 9146 if ((attr = ippFindAttribute(con->request, "job-id",
9147 IPP_TAG_INTEGER)) == NULL)
ef416fc2 9148 {
9149 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 9150 _("Got a printer-uri attribute but no job-id."));
ef416fc2 9151 return;
9152 }
9153
9154 jobid = attr->values[0].integer;
9155 }
9156 else
9157 {
9158 /*
9159 * Got a job URI; parse it to get the job ID...
9160 */
9161
61cf44e2
MS
9162 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9163 sizeof(scheme), username, sizeof(username), host,
a4d04587 9164 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 9165
9166 if (strncmp(resource, "/jobs/", 6))
9167 {
9168 /*
9169 * Not a valid URI!
9170 */
9171
84315f46 9172 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
ef416fc2 9173 uri->values[0].string.text);
9174 return;
9175 }
9176
9177 jobid = atoi(resource + 6);
9178 }
9179
9180 /*
9181 * See if the job exists...
9182 */
9183
9184 if ((job = cupsdFindJob(jobid)) == NULL)
9185 {
9186 /*
9187 * Nope - return a "not found" error...
9188 */
9189
84315f46 9190 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
ef416fc2 9191 return;
9192 }
9193
f301802f 9194 printer = cupsdFindDest(job->dest);
9195
ef416fc2 9196 /*
9197 * See if the job is owned by the requesting user...
9198 */
9199
9200 if (!validate_user(job, con, job->username, username, sizeof(username)))
9201 {
b0f6947b
MS
9202 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9203 cupsdFindDest(job->dest));
ef416fc2 9204 return;
9205 }
9206
9207 /*
9208 * OK, see if the client is sending the document compressed - CUPS
9209 * only supports "none" and "gzip".
9210 */
9211
9212 compression = CUPS_FILE_NONE;
9213
fa73b229 9214 if ((attr = ippFindAttribute(con->request, "compression",
9215 IPP_TAG_KEYWORD)) != NULL)
ef416fc2 9216 {
9217 if (strcmp(attr->values[0].string.text, "none")
9218#ifdef HAVE_LIBZ
9219 && strcmp(attr->values[0].string.text, "gzip")
9220#endif /* HAVE_LIBZ */
9221 )
9222 {
84315f46 9223 send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\"."),
ef416fc2 9224 attr->values[0].string.text);
9225 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
9226 "compression", NULL, attr->values[0].string.text);
9227 return;
9228 }
9229
9230#ifdef HAVE_LIBZ
9231 if (!strcmp(attr->values[0].string.text, "gzip"))
9232 compression = CUPS_FILE_GZIP;
9233#endif /* HAVE_LIBZ */
9234 }
9235
9236 /*
9237 * Do we have a file to print?
9238 */
9239
83e08001
MS
9240 if ((attr = ippFindAttribute(con->request, "last-document",
9241 IPP_TAG_BOOLEAN)) == NULL)
9242 {
9243 send_ipp_status(con, IPP_BAD_REQUEST,
9244 _("Missing last-document attribute in request."));
9245 return;
9246 }
9247
ef416fc2 9248 if (!con->filename)
9249 {
6d2f911b
MS
9250 /*
9251 * Check for an empty request with "last-document" set to true, which is
9252 * used to close an "open" job by RFC 2911, section 3.3.2.
9253 */
9254
83e08001 9255 if (job->num_files > 0 && attr->values[0].boolean)
6d2f911b
MS
9256 goto last_document;
9257
84315f46 9258 send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
ef416fc2 9259 return;
9260 }
9261
9262 /*
9263 * Is it a format we support?
9264 */
9265
9266 if ((format = ippFindAttribute(con->request, "document-format",
9267 IPP_TAG_MIMETYPE)) != NULL)
9268 {
9269 /*
9270 * Grab format from client...
9271 */
9272
c5b24bfa 9273 if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]",
bc44d920 9274 super, type) != 2)
ef416fc2 9275 {
84315f46 9276 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"."),
ef416fc2 9277 format->values[0].string.text);
9278 return;
9279 }
9280 }
f7deaa1a 9281 else if ((default_format = cupsGetOption("document-format",
9282 printer->num_options,
9283 printer->options)) != NULL)
9284 {
9285 /*
9286 * Use default document format...
9287 */
9288
c5b24bfa 9289 if (sscanf(default_format, "%15[^/]/%255[^;]", super, type) != 2)
f7deaa1a 9290 {
9291 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 9292 _("Bad document-format-default \"%s\"."), default_format);
f7deaa1a 9293 return;
9294 }
9295 }
ef416fc2 9296 else
9297 {
9298 /*
9299 * No document format attribute? Auto-type it!
9300 */
9301
5a9febac
MS
9302 strlcpy(super, "application", sizeof(super));
9303 strlcpy(type, "octet-stream", sizeof(type));
ef416fc2 9304 }
9305
fa73b229 9306 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
ef416fc2 9307 {
9308 /*
9309 * Auto-type the file...
9310 */
9311
bd7854cb 9312 ipp_attribute_t *doc_name; /* document-name attribute */
9313
9314
75bd9771 9315 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Auto-typing file...");
ef416fc2 9316
bd7854cb 9317 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
9318 filetype = mimeFileType(MimeDatabase, con->filename,
9319 doc_name ? doc_name->values[0].string.text : NULL,
9320 &compression);
ef416fc2 9321
f7deaa1a 9322 if (!filetype)
f7faf1f5 9323 filetype = mimeType(MimeDatabase, super, type);
a0f6818e 9324
0268488e
MS
9325 if (filetype)
9326 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.",
9327 filetype->super, filetype->type);
ed486911 9328 }
f7faf1f5 9329 else
9330 filetype = mimeType(MimeDatabase, super, type);
9331
49d87452 9332 if (filetype)
f7deaa1a 9333 {
9334 /*
9335 * Replace the document-format attribute value with the auto-typed or
9336 * default one.
9337 */
9338
9339 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9340 filetype->type);
9341
49d87452
MS
9342 if ((jformat = ippFindAttribute(job->attrs, "document-format",
9343 IPP_TAG_MIMETYPE)) != NULL)
f7deaa1a 9344 {
a0f6818e 9345 _cupsStrFree(jformat->values[0].string.text);
f7deaa1a 9346
a0f6818e 9347 jformat->values[0].string.text = _cupsStrAlloc(mimetype);
f7deaa1a 9348 }
9349 else
a0f6818e 9350 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
f7deaa1a 9351 "document-format", NULL, mimetype);
9352 }
9353 else if (!filetype)
ef416fc2 9354 {
9355 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
84315f46 9356 _("Unsupported document-format \"%s/%s\"."), super, type);
ef416fc2 9357 cupsdLogMessage(CUPSD_LOG_INFO,
9358 "Hint: Do you have the raw file printing rules enabled?");
9359
9360 if (format)
9361 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9362 "document-format", NULL, format->values[0].string.text);
9363
9364 return;
9365 }
9366
80ca4592 9367 if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
9368 {
9369 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9370 filetype->type);
9371
9372 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
84315f46 9373 _("Unsupported document-format \"%s\"."), mimetype);
80ca4592 9374
9375 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9376 "document-format", NULL, mimetype);
9377
9378 return;
9379 }
9380
ef416fc2 9381 /*
9382 * Add the file to the job...
9383 */
9384
bd7854cb 9385 cupsdLoadJob(job);
9386
ef416fc2 9387 if (add_file(con, job, filetype, compression))
9388 return;
9389
ef416fc2 9390 if (stat(con->filename, &fileinfo))
9391 kbytes = 0;
9392 else
9393 kbytes = (fileinfo.st_size + 1023) / 1024;
9394
9395 cupsdUpdateQuota(printer, job->username, 0, kbytes);
9396
fa73b229 9397 if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
9398 IPP_TAG_INTEGER)) != NULL)
ef416fc2 9399 attr->values[0].integer += kbytes;
9400
9401 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
9402 job->num_files);
9403 rename(con->filename, filename);
9404
9405 cupsdClearString(&con->filename);
9406
75bd9771
MS
9407 cupsdLogJob(job, CUPSD_LOG_INFO, "File of type %s/%s queued by \"%s\".",
9408 filetype->super, filetype->type, job->username);
ef416fc2 9409
9410 /*
9411 * Start the job if this is the last document...
9412 */
9413
6d2f911b
MS
9414 last_document:
9415
fa73b229 9416 if ((attr = ippFindAttribute(con->request, "last-document",
9417 IPP_TAG_BOOLEAN)) != NULL &&
ef416fc2 9418 attr->values[0].boolean)
9419 {
9420 /*
9421 * See if we need to add the ending sheet...
9422 */
9423
91c84a35
MS
9424 if (cupsdTimeoutJob(job))
9425 return;
ef416fc2 9426
bd7854cb 9427 if (job->state_value == IPP_JOB_STOPPED)
9428 {
ef416fc2 9429 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 9430 job->state_value = IPP_JOB_PENDING;
12f89d24
MS
9431
9432 ippSetString(job->attrs, &job->reasons, 0, "none");
bd7854cb 9433 }
9434 else if (job->state_value == IPP_JOB_HELD)
ef416fc2 9435 {
fa73b229 9436 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9437 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 9438 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9439
fa73b229 9440 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
bd7854cb 9441 {
ef416fc2 9442 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 9443 job->state_value = IPP_JOB_PENDING;
12f89d24
MS
9444
9445 ippSetString(job->attrs, &job->reasons, 0, "none");
bd7854cb 9446 }
12f89d24
MS
9447 else
9448 ippSetString(job->attrs, &job->reasons, 0, "job-hold-until-specified");
ef416fc2 9449 }
9450
3dfe78b3
MS
9451 job->dirty = 1;
9452 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 9453
75bd9771 9454 start_job = 1;
ef416fc2 9455 }
9456 else
9457 {
fa73b229 9458 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9459 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 9460 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9461
fa73b229 9462 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
ef416fc2 9463 {
9464 job->state->values[0].integer = IPP_JOB_HELD;
bd7854cb 9465 job->state_value = IPP_JOB_HELD;
dfd5680b 9466 job->hold_until = time(NULL) + MultipleOperationTimeout;
3dfe78b3 9467
12f89d24
MS
9468 ippSetString(job->attrs, &job->reasons, 0, "job-incoming");
9469
9470 job->dirty = 1;
3dfe78b3 9471 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 9472 }
75bd9771
MS
9473
9474 start_job = 0;
ef416fc2 9475 }
9476
9477 /*
9478 * Fill in the response info...
9479 */
9480
83e08001 9481 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
f2534050 9482 con->clientname, con->clientport, "/jobs/%d", jobid);
ef416fc2 9483 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
9484 job_uri);
9485
9486 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
9487
9488 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
75bd9771 9489 job->state_value);
12f89d24
MS
9490 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons",
9491 NULL, job->reasons->values[0].string.text);
ef416fc2 9492
9493 con->response->request.status.status_code = IPP_OK;
75bd9771
MS
9494
9495 /*
9496 * Start the job if necessary...
9497 */
9498
9499 if (start_job)
9500 cupsdCheckJobs();
ef416fc2 9501}
9502
9503
9504/*
9505 * 'send_http_error()' - Send a HTTP error back to the IPP client.
9506 */
9507
9508static void
f899b121 9509send_http_error(
9510 cupsd_client_t *con, /* I - Client connection */
9511 http_status_t status, /* I - HTTP status code */
9512 cupsd_printer_t *printer) /* I - Printer, if any */
ef416fc2 9513{
9f5eb9be
MS
9514 ipp_attribute_t *uri; /* Request URI, if any */
9515
9516
9517 if ((uri = ippFindAttribute(con->request, "printer-uri",
9518 IPP_TAG_URI)) == NULL)
9519 uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI);
9520
9521 cupsdLogMessage(status == HTTP_FORBIDDEN ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
21f36711
MS
9522 "[Client %d] Returning HTTP %s for %s (%s) from %s",
9523 con->http.fd, httpStatus(status),
321d8d57
MS
9524 con->request ?
9525 ippOpString(con->request->request.op.operation_id) :
9526 "no operation-id",
9f5eb9be
MS
9527 uri ? uri->values[0].string.text : "no URI",
9528 con->http.hostname);
ef416fc2 9529
5f64df29
MS
9530 if (printer)
9531 {
db0bd74a 9532 int auth_type; /* Type of authentication required */
2fb76298
MS
9533
9534
5f64df29
MS
9535 auth_type = CUPSD_AUTH_NONE;
9536
9537 if (status == HTTP_UNAUTHORIZED &&
9538 printer->num_auth_info_required > 0 &&
9539 !strcmp(printer->auth_info_required[0], "negotiate") &&
9540 con->request &&
9541 (con->request->request.op.operation_id == IPP_PRINT_JOB ||
9542 con->request->request.op.operation_id == IPP_CREATE_JOB ||
9543 con->request->request.op.operation_id == CUPS_AUTHENTICATE_JOB))
9544 {
9545 /*
9546 * Creating and authenticating jobs requires Kerberos...
9547 */
9548
9549 auth_type = CUPSD_AUTH_NEGOTIATE;
9550 }
db0bd74a 9551 else
5f64df29
MS
9552 {
9553 /*
9554 * Use policy/location-defined authentication requirements...
9555 */
9556
9557 char resource[HTTP_MAX_URI]; /* Resource portion of URI */
9558 cupsd_location_t *auth; /* Pointer to authentication element */
9559
9560
9561 if (printer->type & CUPS_PRINTER_CLASS)
9562 snprintf(resource, sizeof(resource), "/classes/%s", printer->name);
9563 else
9564 snprintf(resource, sizeof(resource), "/printers/%s", printer->name);
9565
9566 if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
9567 auth->type == CUPSD_AUTH_NONE)
9568 auth = cupsdFindPolicyOp(printer->op_policy_ptr,
9569 con->request ?
9570 con->request->request.op.operation_id :
9571 IPP_PRINT_JOB);
9572
9573 if (auth)
9574 {
9575 if (auth->type == CUPSD_AUTH_DEFAULT)
dcb445bc 9576 auth_type = cupsdDefaultAuthType();
5f64df29
MS
9577 else
9578 auth_type = auth->type;
9579 }
9580 }
2fb76298 9581
db0bd74a 9582 cupsdSendError(con, status, auth_type);
2fb76298 9583 }
f899b121 9584 else
5bd77a73 9585 cupsdSendError(con, status, CUPSD_AUTH_NONE);
ef416fc2 9586
9587 ippDelete(con->response);
9588 con->response = NULL;
9589
9590 return;
9591}
9592
9593
9594/*
9595 * 'send_ipp_status()' - Send a status back to the IPP client.
9596 */
9597
9598static void
9599send_ipp_status(cupsd_client_t *con, /* I - Client connection */
1f0275e3
MS
9600 ipp_status_t status, /* I - IPP status code */
9601 const char *message,/* I - Status message */
9602 ...) /* I - Additional args as needed */
ef416fc2 9603{
9604 va_list ap; /* Pointer to additional args */
9605 char formatted[1024]; /* Formatted errror message */
9606
9607
3d8365b8 9608 va_start(ap, message);
9609 vsnprintf(formatted, sizeof(formatted),
9610 _cupsLangString(con->language, message), ap);
9611 va_end(ap);
ef416fc2 9612
3d8365b8 9613 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
9614 ippOpString(con->request->request.op.operation_id),
9615 ippErrorString(status), formatted);
ef416fc2 9616
9617 con->response->request.status.status_code = status;
9618
fa73b229 9619 if (ippFindAttribute(con->response, "attributes-charset",
9620 IPP_TAG_ZERO) == NULL)
ef416fc2 9621 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
f8b3a85b 9622 "attributes-charset", NULL, "utf-8");
ef416fc2 9623
9624 if (ippFindAttribute(con->response, "attributes-natural-language",
9625 IPP_TAG_ZERO) == NULL)
9626 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
9627 "attributes-natural-language", NULL, DefaultLanguage);
9628
3d8365b8 9629 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
9630 "status-message", NULL, formatted);
ef416fc2 9631}
9632
9633
9634/*
9635 * 'set_default()' - Set the default destination...
9636 */
9637
9638static void
9639set_default(cupsd_client_t *con, /* I - Client connection */
9640 ipp_attribute_t *uri) /* I - Printer URI */
9641{
9642 http_status_t status; /* Policy status */
bc44d920 9643 cups_ptype_t dtype; /* Destination type (printer/class) */
dd1abb6b
MS
9644 cupsd_printer_t *printer, /* Printer */
9645 *oldprinter; /* Old default printer */
ef416fc2 9646
9647
9648 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
9649 con->http.fd, uri->values[0].string.text);
9650
9651 /*
9652 * Is the destination valid?
9653 */
9654
f7deaa1a 9655 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 9656 {
9657 /*
9658 * Bad URI...
9659 */
9660
9661 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 9662 _("The printer or class does not exist."));
ef416fc2 9663 return;
9664 }
9665
9666 /*
9667 * Check policy...
9668 */
9669
9670 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
9671 {
f899b121 9672 send_http_error(con, status, NULL);
ef416fc2 9673 return;
9674 }
9675
9676 /*
9677 * Set it as the default...
9678 */
9679
dd1abb6b 9680 oldprinter = DefaultPrinter;
ef416fc2 9681 DefaultPrinter = printer;
9682
dd1abb6b
MS
9683 if (oldprinter)
9684 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, oldprinter, NULL,
9685 "%s is no longer the default printer.", oldprinter->name);
9686
9687 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
9688 "%s is now the default printer.", printer->name);
9689
3dfe78b3 9690 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS | CUPSD_DIRTY_CLASSES |
a2326b5b 9691 CUPSD_DIRTY_PRINTCAP);
ef416fc2 9692
9693 cupsdLogMessage(CUPSD_LOG_INFO,
f7deaa1a 9694 "Default destination set to \"%s\" by \"%s\".",
9695 printer->name, get_username(con));
ef416fc2 9696
9697 /*
9698 * Everything was ok, so return OK status...
9699 */
9700
9701 con->response->request.status.status_code = IPP_OK;
9702}
9703
9704
9705/*
9706 * 'set_job_attrs()' - Set job attributes.
9707 */
9708
9709static void
9710set_job_attrs(cupsd_client_t *con, /* I - Client connection */
9711 ipp_attribute_t *uri) /* I - Job URI */
9712{
9713 ipp_attribute_t *attr, /* Current attribute */
9714 *attr2; /* Job attribute */
9715 int jobid; /* Job ID */
9716 cupsd_job_t *job; /* Current job */
61cf44e2 9717 char scheme[HTTP_MAX_URI],
ef416fc2 9718 /* Method portion of URI */
9719 username[HTTP_MAX_URI],
9720 /* Username portion of URI */
9721 host[HTTP_MAX_URI],
9722 /* Host portion of URI */
9723 resource[HTTP_MAX_URI];
9724 /* Resource portion of URI */
9725 int port; /* Port portion of URI */
d09495fa 9726 int event; /* Events? */
005dd1eb 9727 int check_jobs; /* Check jobs? */
ef416fc2 9728
9729
9730 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
9731 con->http.fd, uri->values[0].string.text);
9732
9733 /*
9734 * Start with "everything is OK" status...
9735 */
9736
9737 con->response->request.status.status_code = IPP_OK;
9738
9739 /*
9740 * See if we have a job URI or a printer URI...
9741 */
9742
fa73b229 9743 if (!strcmp(uri->name, "printer-uri"))
ef416fc2 9744 {
9745 /*
9746 * Got a printer URI; see if we also have a job-id attribute...
9747 */
9748
fa73b229 9749 if ((attr = ippFindAttribute(con->request, "job-id",
9750 IPP_TAG_INTEGER)) == NULL)
ef416fc2 9751 {
9752 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 9753 _("Got a printer-uri attribute but no job-id."));
ef416fc2 9754 return;
9755 }
9756
9757 jobid = attr->values[0].integer;
9758 }
9759 else
9760 {
9761 /*
9762 * Got a job URI; parse it to get the job ID...
9763 */
9764
61cf44e2
MS
9765 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9766 sizeof(scheme), username, sizeof(username), host,
a4d04587 9767 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 9768
fa73b229 9769 if (strncmp(resource, "/jobs/", 6))
ef416fc2 9770 {
9771 /*
9772 * Not a valid URI!
9773 */
9774
84315f46 9775 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
ef416fc2 9776 uri->values[0].string.text);
9777 return;
9778 }
9779
9780 jobid = atoi(resource + 6);
9781 }
9782
9783 /*
9784 * See if the job exists...
9785 */
9786
9787 if ((job = cupsdFindJob(jobid)) == NULL)
9788 {
9789 /*
9790 * Nope - return a "not found" error...
9791 */
9792
84315f46 9793 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
ef416fc2 9794 return;
9795 }
9796
9797 /*
9798 * See if the job has been completed...
9799 */
9800
bd7854cb 9801 if (job->state_value > IPP_JOB_STOPPED)
ef416fc2 9802 {
9803 /*
9804 * Return a "not-possible" error...
9805 */
9806
9807 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 9808 _("Job #%d is finished and cannot be altered."), jobid);
ef416fc2 9809 return;
9810 }
9811
9812 /*
9813 * See if the job is owned by the requesting user...
9814 */
9815
9816 if (!validate_user(job, con, job->username, username, sizeof(username)))
9817 {
b0f6947b
MS
9818 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9819 cupsdFindDest(job->dest));
ef416fc2 9820 return;
9821 }
9822
9823 /*
9824 * See what the user wants to change.
9825 */
9826
bd7854cb 9827 cupsdLoadJob(job);
9828
005dd1eb
MS
9829 check_jobs = 0;
9830 event = 0;
d09495fa 9831
fa73b229 9832 for (attr = con->request->attrs; attr; attr = attr->next)
ef416fc2 9833 {
9834 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
9835 continue;
9836
9837 if (!strcmp(attr->name, "attributes-charset") ||
9838 !strcmp(attr->name, "attributes-natural-language") ||
9839 !strcmp(attr->name, "document-compression") ||
9840 !strcmp(attr->name, "document-format") ||
9841 !strcmp(attr->name, "job-detailed-status-messages") ||
9842 !strcmp(attr->name, "job-document-access-errors") ||
9843 !strcmp(attr->name, "job-id") ||
5d6412a9 9844 !strcmp(attr->name, "job-impressions-completed") ||
ef416fc2 9845 !strcmp(attr->name, "job-k-octets") ||
9846 !strcmp(attr->name, "job-originating-host-name") ||
9847 !strcmp(attr->name, "job-originating-user-name") ||
9848 !strcmp(attr->name, "job-printer-up-time") ||
9849 !strcmp(attr->name, "job-printer-uri") ||
9850 !strcmp(attr->name, "job-sheets") ||
9851 !strcmp(attr->name, "job-state-message") ||
9852 !strcmp(attr->name, "job-state-reasons") ||
9853 !strcmp(attr->name, "job-uri") ||
9854 !strcmp(attr->name, "number-of-documents") ||
9855 !strcmp(attr->name, "number-of-intervening-jobs") ||
9856 !strcmp(attr->name, "output-device-assigned") ||
9857 !strncmp(attr->name, "date-time-at-", 13) ||
ef416fc2 9858 !strncmp(attr->name, "job-k-octets", 12) ||
9859 !strncmp(attr->name, "job-media-sheets", 16) ||
9860 !strncmp(attr->name, "time-at-", 8))
9861 {
9862 /*
9863 * Read-only attrs!
9864 */
9865
9866 send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
9867 _("%s cannot be changed."), attr->name);
9868
a2326b5b
MS
9869 attr2 = ippCopyAttribute(con->response, attr, 0);
9870 ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
ef416fc2 9871 continue;
9872 }
9873
9874 if (!strcmp(attr->name, "job-priority"))
9875 {
9876 /*
9877 * Change the job priority...
9878 */
9879
9880 if (attr->value_tag != IPP_TAG_INTEGER)
9881 {
84315f46 9882 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value."));
ef416fc2 9883
a2326b5b
MS
9884 attr2 = ippCopyAttribute(con->response, attr, 0);
9885 ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
ef416fc2 9886 }
bd7854cb 9887 else if (job->state_value >= IPP_JOB_PROCESSING)
ef416fc2 9888 {
9889 send_ipp_status(con, IPP_NOT_POSSIBLE,
9890 _("Job is completed and cannot be changed."));
9891 return;
9892 }
9893 else if (con->response->request.status.status_code == IPP_OK)
d09495fa 9894 {
005dd1eb
MS
9895 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-priority to %d",
9896 attr->values[0].integer);
ef416fc2 9897 cupsdSetJobPriority(job, attr->values[0].integer);
005dd1eb
MS
9898
9899 check_jobs = 1;
9900 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED |
9901 CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED;
d09495fa 9902 }
ef416fc2 9903 }
9904 else if (!strcmp(attr->name, "job-state"))
9905 {
9906 /*
9907 * Change the job state...
9908 */
9909
9910 if (attr->value_tag != IPP_TAG_ENUM)
9911 {
84315f46 9912 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value."));
ef416fc2 9913
a2326b5b
MS
9914 attr2 = ippCopyAttribute(con->response, attr, 0);
9915 ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
ef416fc2 9916 }
9917 else
9918 {
9919 switch (attr->values[0].integer)
9920 {
9921 case IPP_JOB_PENDING :
9922 case IPP_JOB_HELD :
bd7854cb 9923 if (job->state_value > IPP_JOB_HELD)
ef416fc2 9924 {
9925 send_ipp_status(con, IPP_NOT_POSSIBLE,
9926 _("Job state cannot be changed."));
9927 return;
9928 }
9929 else if (con->response->request.status.status_code == IPP_OK)
bd7854cb 9930 {
005dd1eb
MS
9931 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
9932 attr->values[0].integer);
b9faaae1
MS
9933 cupsdSetJobState(job, attr->values[0].integer,
9934 CUPSD_JOB_DEFAULT,
9935 "Job state changed by \"%s\"", username);
005dd1eb 9936 check_jobs = 1;
bd7854cb 9937 }
ef416fc2 9938 break;
9939
9940 case IPP_JOB_PROCESSING :
9941 case IPP_JOB_STOPPED :
bd7854cb 9942 if (job->state_value != attr->values[0].integer)
ef416fc2 9943 {
9944 send_ipp_status(con, IPP_NOT_POSSIBLE,
9945 _("Job state cannot be changed."));
9946 return;
9947 }
9948 break;
9949
d09495fa 9950 case IPP_JOB_CANCELED :
ef416fc2 9951 case IPP_JOB_ABORTED :
9952 case IPP_JOB_COMPLETED :
bd7854cb 9953 if (job->state_value > IPP_JOB_PROCESSING)
ef416fc2 9954 {
9955 send_ipp_status(con, IPP_NOT_POSSIBLE,
9956 _("Job state cannot be changed."));
9957 return;
9958 }
9959 else if (con->response->request.status.status_code == IPP_OK)
005dd1eb
MS
9960 {
9961 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
9962 attr->values[0].integer);
b9faaae1
MS
9963 cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer,
9964 CUPSD_JOB_DEFAULT,
9965 "Job state changed by \"%s\"", username);
005dd1eb
MS
9966 check_jobs = 1;
9967 }
ef416fc2 9968 break;
9969 }
9970 }
9971 }
9972 else if (con->response->request.status.status_code != IPP_OK)
9973 continue;
fa73b229 9974 else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
9975 IPP_TAG_ZERO)) != NULL)
ef416fc2 9976 {
9977 /*
9978 * Some other value; first free the old value...
9979 */
9980
9981 if (job->attrs->prev)
9982 job->attrs->prev->next = attr2->next;
9983 else
9984 job->attrs->attrs = attr2->next;
9985
9986 if (job->attrs->last == attr2)
9987 job->attrs->last = job->attrs->prev;
9988
a2326b5b 9989 ippDeleteAttribute(NULL, attr2);
ef416fc2 9990
9991 /*
9992 * Then copy the attribute...
9993 */
9994
a2326b5b 9995 ippCopyAttribute(job->attrs, attr, 0);
ef416fc2 9996
9997 /*
9998 * See if the job-name or job-hold-until is being changed.
9999 */
10000
10001 if (!strcmp(attr->name, "job-hold-until"))
10002 {
005dd1eb
MS
10003 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s",
10004 attr->values[0].string.text);
b9faaae1 10005 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
ef416fc2 10006
10007 if (!strcmp(attr->values[0].string.text, "no-hold"))
b9faaae1 10008 {
ef416fc2 10009 cupsdReleaseJob(job);
b9faaae1
MS
10010 check_jobs = 1;
10011 }
ef416fc2 10012 else
b9faaae1
MS
10013 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT,
10014 "Job held by \"%s\".", username);
d09495fa 10015
b9faaae1 10016 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
ef416fc2 10017 }
10018 }
10019 else if (attr->value_tag == IPP_TAG_DELETEATTR)
10020 {
10021 /*
10022 * Delete the attribute...
10023 */
10024
10025 if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10026 IPP_TAG_ZERO)) != NULL)
10027 {
10028 if (job->attrs->prev)
10029 job->attrs->prev->next = attr2->next;
10030 else
10031 job->attrs->attrs = attr2->next;
10032
10033 if (attr2 == job->attrs->last)
10034 job->attrs->last = job->attrs->prev;
10035
a2326b5b 10036 ippDeleteAttribute(NULL, attr2);
d09495fa 10037
10038 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
ef416fc2 10039 }
10040 }
10041 else
10042 {
10043 /*
10044 * Add new option by copying it...
10045 */
10046
a2326b5b 10047 ippCopyAttribute(job->attrs, attr, 0);
d09495fa 10048
10049 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
ef416fc2 10050 }
10051 }
10052
10053 /*
10054 * Save the job...
10055 */
10056
3dfe78b3
MS
10057 job->dirty = 1;
10058 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 10059
d09495fa 10060 /*
10061 * Send events as needed...
10062 */
10063
d9bca400 10064 if (event & CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED)
01ce6322
MS
10065 cupsdAddEvent(CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED,
10066 cupsdFindDest(job->dest), job,
d9bca400
MS
10067 "Job priority changed by user.");
10068
d09495fa 10069 if (event & CUPSD_EVENT_JOB_STATE)
01ce6322 10070 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
d09495fa 10071 job->state_value == IPP_JOB_HELD ?
10072 "Job held by user." : "Job restarted by user.");
10073
10074 if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
01ce6322 10075 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
d09495fa 10076 "Job options changed by user.");
10077
ef416fc2 10078 /*
10079 * Start jobs if possible...
10080 */
10081
005dd1eb
MS
10082 if (check_jobs)
10083 cupsdCheckJobs();
ef416fc2 10084}
10085
10086
c168a833
MS
10087/*
10088 * 'set_printer_attrs()' - Set printer attributes.
10089 */
10090
10091static void
10092set_printer_attrs(cupsd_client_t *con, /* I - Client connection */
10093 ipp_attribute_t *uri) /* I - Printer */
10094{
10095 http_status_t status; /* Policy status */
10096 cups_ptype_t dtype; /* Destination type (printer/class) */
10097 cupsd_printer_t *printer; /* Printer/class */
10098 ipp_attribute_t *attr; /* Printer attribute */
bf3816c7 10099 int changed = 0; /* Was anything changed? */
c168a833
MS
10100
10101
10102 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_attrs(%p[%d], %s)", con,
10103 con->http.fd, uri->values[0].string.text);
10104
10105 /*
10106 * Is the destination valid?
10107 */
10108
10109 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10110 {
10111 /*
10112 * Bad URI...
10113 */
10114
10115 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 10116 _("The printer or class does not exist."));
c168a833
MS
10117 return;
10118 }
10119
10120 /*
10121 * Check policy...
10122 */
10123
10124 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10125 {
10126 send_http_error(con, status, printer);
10127 return;
10128 }
10129
10130 /*
10131 * Return a list of attributes that can be set via Set-Printer-Attributes.
10132 */
10133
10134 if ((attr = ippFindAttribute(con->request, "printer-location",
10135 IPP_TAG_TEXT)) != NULL)
10136 {
10137 cupsdSetString(&printer->location, attr->values[0].string.text);
10138 changed = 1;
10139 }
10140
10141 if ((attr = ippFindAttribute(con->request, "printer-info",
10142 IPP_TAG_TEXT)) != NULL)
10143 {
10144 cupsdSetString(&printer->info, attr->values[0].string.text);
10145 changed = 1;
10146 }
10147
10148 /*
10149 * Update the printer attributes and return...
10150 */
10151
10152 if (changed)
10153 {
10154 cupsdSetPrinterAttrs(printer);
10155 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
10156
10157 cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL,
10158 "Printer \"%s\" description or location changed by \"%s\".",
10159 printer->name, get_username(con));
10160
10161 cupsdLogMessage(CUPSD_LOG_INFO,
10162 "Printer \"%s\" description or location changed by \"%s\".",
10163 printer->name, get_username(con));
10164 }
10165
10166 con->response->request.status.status_code = IPP_OK;
10167}
10168
10169
b423cd4c 10170/*
10171 * 'set_printer_defaults()' - Set printer default options from a request.
10172 */
10173
10174static void
10175set_printer_defaults(
10176 cupsd_client_t *con, /* I - Client connection */
10177 cupsd_printer_t *printer) /* I - Printer */
10178{
10179 int i; /* Looping var */
10180 ipp_attribute_t *attr; /* Current attribute */
10181 int namelen; /* Length of attribute name */
10182 char name[256], /* New attribute name */
10183 value[256]; /* String version of integer attrs */
10184
10185
10186 for (attr = con->request->attrs; attr; attr = attr->next)
10187 {
10188 /*
10189 * Skip non-printer attributes...
10190 */
10191
10192 if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
10193 continue;
10194
10195 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
10196
10197 if (!strcmp(attr->name, "job-sheets-default"))
10198 {
10199 /*
10200 * Only allow keywords and names...
10201 */
10202
10203 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10204 continue;
10205
10206 /*
10207 * Only allow job-sheets-default to be set when running without a
10208 * system high classification level...
10209 */
10210
10211 if (Classification)
10212 continue;
10213
10214 cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
10215
10216 if (attr->num_values > 1)
10217 cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
10218 else
10219 cupsdSetString(&printer->job_sheets[1], "none");
10220 }
10221 else if (!strcmp(attr->name, "requesting-user-name-allowed"))
10222 {
10d09e33 10223 cupsdFreeStrings(&(printer->users));
b423cd4c 10224
10225 printer->deny_users = 0;
10226
10227 if (attr->value_tag == IPP_TAG_NAME &&
10228 (attr->num_values > 1 ||
10229 strcmp(attr->values[0].string.text, "all")))
10230 {
10231 for (i = 0; i < attr->num_values; i ++)
10d09e33 10232 cupsdAddString(&(printer->users), attr->values[i].string.text);
b423cd4c 10233 }
10234 }
10235 else if (!strcmp(attr->name, "requesting-user-name-denied"))
10236 {
10d09e33 10237 cupsdFreeStrings(&(printer->users));
b423cd4c 10238
10239 printer->deny_users = 1;
10240
10241 if (attr->value_tag == IPP_TAG_NAME &&
10242 (attr->num_values > 1 ||
10243 strcmp(attr->values[0].string.text, "none")))
10244 {
10245 for (i = 0; i < attr->num_values; i ++)
10d09e33 10246 cupsdAddString(&(printer->users), attr->values[i].string.text);
b423cd4c 10247 }
10248 }
10249 else if (!strcmp(attr->name, "job-quota-period"))
10250 {
10251 if (attr->value_tag != IPP_TAG_INTEGER)
10252 continue;
10253
10254 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
10255 attr->values[0].integer);
10256 cupsdFreeQuotas(printer);
10257
10258 printer->quota_period = attr->values[0].integer;
10259 }
10260 else if (!strcmp(attr->name, "job-k-limit"))
10261 {
10262 if (attr->value_tag != IPP_TAG_INTEGER)
10263 continue;
10264
10265 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
10266 attr->values[0].integer);
10267 cupsdFreeQuotas(printer);
10268
10269 printer->k_limit = attr->values[0].integer;
10270 }
10271 else if (!strcmp(attr->name, "job-page-limit"))
10272 {
10273 if (attr->value_tag != IPP_TAG_INTEGER)
10274 continue;
10275
10276 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
10277 attr->values[0].integer);
10278 cupsdFreeQuotas(printer);
10279
10280 printer->page_limit = attr->values[0].integer;
10281 }
10282 else if (!strcmp(attr->name, "printer-op-policy"))
10283 {
10284 cupsd_policy_t *p; /* Policy */
10285
10286
10287 if (attr->value_tag != IPP_TAG_NAME)
10288 continue;
10289
10290 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
10291 {
10292 cupsdLogMessage(CUPSD_LOG_DEBUG,
10293 "Setting printer-op-policy to \"%s\"...",
10294 attr->values[0].string.text);
10295 cupsdSetString(&printer->op_policy, attr->values[0].string.text);
10296 printer->op_policy_ptr = p;
10297 }
10298 else
10299 {
10300 send_ipp_status(con, IPP_NOT_POSSIBLE,
10301 _("Unknown printer-op-policy \"%s\"."),
10302 attr->values[0].string.text);
10303 return;
10304 }
10305 }
10306 else if (!strcmp(attr->name, "printer-error-policy"))
10307 {
10308 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10309 continue;
10310
f11a948a 10311 if (strcmp(attr->values[0].string.text, "retry-current-job") &&
a2326b5b 10312 ((printer->type & CUPS_PRINTER_CLASS) ||
f11a948a
MS
10313 (strcmp(attr->values[0].string.text, "abort-job") &&
10314 strcmp(attr->values[0].string.text, "retry-job") &&
10315 strcmp(attr->values[0].string.text, "stop-printer"))))
b423cd4c 10316 {
10317 send_ipp_status(con, IPP_NOT_POSSIBLE,
10318 _("Unknown printer-error-policy \"%s\"."),
10319 attr->values[0].string.text);
10320 return;
10321 }
10322
10323 cupsdLogMessage(CUPSD_LOG_DEBUG,
10324 "Setting printer-error-policy to \"%s\"...",
10325 attr->values[0].string.text);
10326 cupsdSetString(&printer->error_policy, attr->values[0].string.text);
10327 }
b423cd4c 10328
10329 /*
10330 * Skip any other non-default attributes...
10331 */
10332
10333 namelen = strlen(attr->name);
10334 if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
10335 namelen > (sizeof(name) - 1) || attr->num_values != 1)
10336 continue;
10337
10338 /*
10339 * OK, anything else must be a user-defined default...
10340 */
10341
10342 strlcpy(name, attr->name, sizeof(name));
10343 name[namelen - 8] = '\0'; /* Strip "-default" */
10344
10345 switch (attr->value_tag)
10346 {
10347 case IPP_TAG_DELETEATTR :
10348 printer->num_options = cupsRemoveOption(name,
10349 printer->num_options,
10350 &(printer->options));
10351 cupsdLogMessage(CUPSD_LOG_DEBUG,
10352 "Deleting %s", attr->name);
10353 break;
10354
10355 case IPP_TAG_NAME :
5a9febac 10356 case IPP_TAG_TEXT :
b423cd4c 10357 case IPP_TAG_KEYWORD :
10358 case IPP_TAG_URI :
10359 printer->num_options = cupsAddOption(name,
10360 attr->values[0].string.text,
10361 printer->num_options,
10362 &(printer->options));
10363 cupsdLogMessage(CUPSD_LOG_DEBUG,
10364 "Setting %s to \"%s\"...", attr->name,
10365 attr->values[0].string.text);
10366 break;
10367
10368 case IPP_TAG_BOOLEAN :
10369 printer->num_options = cupsAddOption(name,
10370 attr->values[0].boolean ?
10371 "true" : "false",
10372 printer->num_options,
10373 &(printer->options));
10374 cupsdLogMessage(CUPSD_LOG_DEBUG,
10375 "Setting %s to %s...", attr->name,
10376 attr->values[0].boolean ? "true" : "false");
10377 break;
10378
10379 case IPP_TAG_INTEGER :
10380 case IPP_TAG_ENUM :
10381 sprintf(value, "%d", attr->values[0].integer);
10382 printer->num_options = cupsAddOption(name, value,
10383 printer->num_options,
10384 &(printer->options));
10385 cupsdLogMessage(CUPSD_LOG_DEBUG,
10386 "Setting %s to %s...", attr->name, value);
10387 break;
10388
10389 case IPP_TAG_RANGE :
10390 sprintf(value, "%d-%d", attr->values[0].range.lower,
10391 attr->values[0].range.upper);
10392 printer->num_options = cupsAddOption(name, value,
10393 printer->num_options,
10394 &(printer->options));
10395 cupsdLogMessage(CUPSD_LOG_DEBUG,
10396 "Setting %s to %s...", attr->name, value);
10397 break;
10398
10399 case IPP_TAG_RESOLUTION :
10400 sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
10401 attr->values[0].resolution.yres,
10402 attr->values[0].resolution.units == IPP_RES_PER_INCH ?
3e7fe0ca 10403 "dpi" : "dpcm");
b423cd4c 10404 printer->num_options = cupsAddOption(name, value,
10405 printer->num_options,
10406 &(printer->options));
10407 cupsdLogMessage(CUPSD_LOG_DEBUG,
10408 "Setting %s to %s...", attr->name, value);
10409 break;
10410
10411 default :
10412 /* Do nothing for other values */
10413 break;
10414 }
10415 }
10416}
10417
10418
ef416fc2 10419/*
10420 * 'start_printer()' - Start a printer.
10421 */
10422
10423static void
10424start_printer(cupsd_client_t *con, /* I - Client connection */
10425 ipp_attribute_t *uri) /* I - Printer URI */
10426{
10d09e33 10427 int i; /* Temporary variable */
ef416fc2 10428 http_status_t status; /* Policy status */
bc44d920 10429 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 10430 cupsd_printer_t *printer; /* Printer data */
10431
10432
10433 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
10434 con->http.fd, uri->values[0].string.text);
10435
10436 /*
10437 * Is the destination valid?
10438 */
10439
f7deaa1a 10440 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 10441 {
10442 /*
10443 * Bad URI...
10444 */
10445
10446 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 10447 _("The printer or class does not exist."));
ef416fc2 10448 return;
10449 }
10450
10451 /*
10452 * Check policy...
10453 */
10454
10455 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10456 {
f899b121 10457 send_http_error(con, status, printer);
ef416fc2 10458 return;
10459 }
10460
10461 /*
10462 * Start the printer...
10463 */
10464
10465 printer->state_message[0] = '\0';
10466
10467 cupsdStartPrinter(printer, 1);
10468
10469 if (dtype & CUPS_PRINTER_CLASS)
f7deaa1a 10470 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".",
10471 printer->name, get_username(con));
e00b005a 10472 else
f7deaa1a 10473 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".",
10474 printer->name, get_username(con));
ef416fc2 10475
10476 cupsdCheckJobs();
10477
10d09e33
MS
10478 /*
10479 * Check quotas...
10480 */
10481
10482 if ((i = check_quotas(con, printer)) < 0)
10483 {
10484 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
10485 return;
10486 }
10487 else if (i == 0)
10488 {
10489 send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print."));
10490 return;
10491 }
10492
ef416fc2 10493 /*
10494 * Everything was ok, so return OK status...
10495 */
10496
10497 con->response->request.status.status_code = IPP_OK;
10498}
10499
10500
10501/*
10502 * 'stop_printer()' - Stop a printer.
10503 */
10504
10505static void
10506stop_printer(cupsd_client_t *con, /* I - Client connection */
10507 ipp_attribute_t *uri) /* I - Printer URI */
10508{
10509 http_status_t status; /* Policy status */
bc44d920 10510 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 10511 cupsd_printer_t *printer; /* Printer data */
10512 ipp_attribute_t *attr; /* printer-state-message attribute */
10513
10514
10515 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
10516 con->http.fd, uri->values[0].string.text);
10517
10518 /*
10519 * Is the destination valid?
10520 */
10521
f7deaa1a 10522 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 10523 {
10524 /*
10525 * Bad URI...
10526 */
10527
10528 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 10529 _("The printer or class does not exist."));
ef416fc2 10530 return;
10531 }
10532
10533 /*
10534 * Check policy...
10535 */
10536
10537 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10538 {
f899b121 10539 send_http_error(con, status, printer);
ef416fc2 10540 return;
10541 }
10542
10543 /*
10544 * Stop the printer...
10545 */
10546
10547 if ((attr = ippFindAttribute(con->request, "printer-state-message",
10548 IPP_TAG_TEXT)) == NULL)
5a9febac 10549 strlcpy(printer->state_message, "Paused", sizeof(printer->state_message));
ef416fc2 10550 else
10551 {
10552 strlcpy(printer->state_message, attr->values[0].string.text,
10553 sizeof(printer->state_message));
10554 }
10555
10556 cupsdStopPrinter(printer, 1);
10557
10558 if (dtype & CUPS_PRINTER_CLASS)
f7deaa1a 10559 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".",
10560 printer->name, get_username(con));
ef416fc2 10561 else
f7deaa1a 10562 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".",
10563 printer->name, get_username(con));
ef416fc2 10564
10565 /*
10566 * Everything was ok, so return OK status...
10567 */
10568
10569 con->response->request.status.status_code = IPP_OK;
10570}
10571
10572
89d46774 10573/*
10574 * 'url_encode_attr()' - URL-encode a string attribute.
10575 */
10576
10577static void
10578url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
10579 char *buffer,/* I - String buffer */
10580 int bufsize)/* I - Size of buffer */
10581{
10582 int i; /* Looping var */
10583 char *bufptr, /* Pointer into buffer */
b94498cf 10584 *bufend; /* End of buffer */
89d46774 10585
10586
10587 strlcpy(buffer, attr->name, bufsize);
10588 bufptr = buffer + strlen(buffer);
10589 bufend = buffer + bufsize - 1;
10590
10591 for (i = 0; i < attr->num_values; i ++)
10592 {
10593 if (bufptr >= bufend)
10594 break;
10595
10596 if (i)
10597 *bufptr++ = ',';
10598 else
10599 *bufptr++ = '=';
10600
10601 if (bufptr >= bufend)
10602 break;
10603
10604 *bufptr++ = '\'';
10605
b94498cf 10606 bufptr = url_encode_string(attr->values[i].string.text,
10607 bufptr, bufend - bufptr + 1);
89d46774 10608
10609 if (bufptr >= bufend)
10610 break;
10611
10612 *bufptr++ = '\'';
10613 }
10614
10615 *bufptr = '\0';
10616}
10617
10618
b94498cf 10619/*
10620 * 'url_encode_string()' - URL-encode a string.
10621 */
10622
10623static char * /* O - End of string */
10624url_encode_string(const char *s, /* I - String */
10625 char *buffer, /* I - String buffer */
10626 int bufsize) /* I - Size of buffer */
10627{
10628 char *bufptr, /* Pointer into buffer */
10629 *bufend; /* End of buffer */
10630 static const char *hex = "0123456789ABCDEF";
10631 /* Hex digits */
10632
10633
10634 bufptr = buffer;
10635 bufend = buffer + bufsize - 1;
10636
10637 while (*s && bufptr < bufend)
10638 {
f701418f 10639 if (*s == ' ' || *s == '%' || *s == '+')
b94498cf 10640 {
10641 if (bufptr >= (bufend - 2))
10642 break;
10643
10644 *bufptr++ = '%';
10645 *bufptr++ = hex[(*s >> 4) & 15];
10646 *bufptr++ = hex[*s & 15];
10647
10648 s ++;
10649 }
10650 else if (*s == '\'' || *s == '\\')
10651 {
10652 if (bufptr >= (bufend - 1))
10653 break;
10654
10655 *bufptr++ = '\\';
10656 *bufptr++ = *s++;
10657 }
10658 else
10659 *bufptr++ = *s++;
10660 }
10661
10662 *bufptr = '\0';
10663
10664 return (bufptr);
10665}
10666
10667
ef416fc2 10668/*
10669 * 'user_allowed()' - See if a user is allowed to print to a queue.
10670 */
10671
10672static int /* O - 0 if not allowed, 1 if allowed */
10673user_allowed(cupsd_printer_t *p, /* I - Printer or class */
10674 const char *username) /* I - Username */
10675{
ef416fc2 10676 struct passwd *pw; /* User password data */
5bd77a73 10677 char baseuser[256], /* Base username */
10d09e33
MS
10678 *baseptr, /* Pointer to "@" in base username */
10679 *name; /* Current user name */
ef416fc2 10680
10681
10d09e33 10682 if (cupsArrayCount(p->users) == 0)
ef416fc2 10683 return (1);
10684
10685 if (!strcmp(username, "root"))
10686 return (1);
10687
5bd77a73
MS
10688 if (strchr(username, '@'))
10689 {
10690 /*
10691 * Strip @REALM for username check...
10692 */
10693
10694 strlcpy(baseuser, username, sizeof(baseuser));
10695
10696 if ((baseptr = strchr(baseuser, '@')) != NULL)
10697 *baseptr = '\0';
10698
10699 username = baseuser;
10700 }
10701
ef416fc2 10702 pw = getpwnam(username);
10703 endpwent();
10704
10d09e33
MS
10705 for (name = (char *)cupsArrayFirst(p->users);
10706 name;
10707 name = (char *)cupsArrayNext(p->users))
ef416fc2 10708 {
10d09e33 10709 if (name[0] == '@')
ef416fc2 10710 {
10711 /*
10712 * Check group membership...
10713 */
10714
10d09e33 10715 if (cupsdCheckGroup(username, pw, name + 1))
ef416fc2 10716 break;
10717 }
10d09e33 10718 else if (name[0] == '#')
8922323b
MS
10719 {
10720 /*
10721 * Check UUID...
10722 */
10723
10d09e33 10724 if (cupsdCheckGroup(username, pw, name))
8922323b
MS
10725 break;
10726 }
88f9aafc 10727 else if (!_cups_strcasecmp(username, name))
ef416fc2 10728 break;
10729 }
10730
10d09e33 10731 return ((name != NULL) != p->deny_users);
ef416fc2 10732}
10733
10734
10735/*
10736 * 'validate_job()' - Validate printer options and destination.
10737 */
10738
10739static void
10740validate_job(cupsd_client_t *con, /* I - Client connection */
10741 ipp_attribute_t *uri) /* I - Printer URI */
10742{
10743 http_status_t status; /* Policy status */
c7017ecc
MS
10744 ipp_attribute_t *attr, /* Current attribute */
10745 *auth_info; /* auth-info attribute */
5a9febac
MS
10746 ipp_attribute_t *format, /* Document-format attribute */
10747 *name; /* Job-name attribute */
bc44d920 10748 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 10749 char super[MIME_MAX_SUPER],
10750 /* Supertype of file */
10751 type[MIME_MAX_TYPE];
10752 /* Subtype of file */
10753 cupsd_printer_t *printer; /* Printer */
10754
10755
10756 cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
10757 con->http.fd, uri->values[0].string.text);
10758
10759 /*
10760 * OK, see if the client is sending the document compressed - CUPS
10761 * doesn't support compression yet...
10762 */
10763
fa73b229 10764 if ((attr = ippFindAttribute(con->request, "compression",
c7017ecc 10765 IPP_TAG_KEYWORD)) != NULL)
ef416fc2 10766 {
c7017ecc
MS
10767 if (strcmp(attr->values[0].string.text, "none")
10768#ifdef HAVE_LIBZ
10769 && strcmp(attr->values[0].string.text, "gzip")
10770#endif /* HAVE_LIBZ */
10771 )
10772 {
10773 send_ipp_status(con, IPP_ATTRIBUTES,
5a9febac 10774 _("Unsupported 'compression' value \"%s\"."),
c7017ecc
MS
10775 attr->values[0].string.text);
10776 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
10777 "compression", NULL, attr->values[0].string.text);
10778 return;
10779 }
ef416fc2 10780 }
10781
10782 /*
10783 * Is it a format we support?
10784 */
10785
10786 if ((format = ippFindAttribute(con->request, "document-format",
10787 IPP_TAG_MIMETYPE)) != NULL)
10788 {
c5b24bfa 10789 if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]",
bc44d920 10790 super, type) != 2)
ef416fc2 10791 {
5a9febac
MS
10792 send_ipp_status(con, IPP_BAD_REQUEST,
10793 _("Bad 'document-format' value \"%s\"."),
ef416fc2 10794 format->values[0].string.text);
10795 return;
10796 }
10797
fa73b229 10798 if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
10799 !mimeType(MimeDatabase, super, type))
ef416fc2 10800 {
10801 cupsdLogMessage(CUPSD_LOG_INFO,
10802 "Hint: Do you have the raw file printing rules enabled?");
10803 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
5a9febac 10804 _("Unsupported 'document-format' value \"%s\"."),
ef416fc2 10805 format->values[0].string.text);
10806 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
10807 "document-format", NULL, format->values[0].string.text);
10808 return;
10809 }
10810 }
10811
5a9febac
MS
10812 /*
10813 * Is the job-name valid?
10814 */
10815
10816 if ((name = ippFindAttribute(con->request, "job-name", IPP_TAG_ZERO)) != NULL)
10817 {
10818 int bad_name = 0; /* Is the job-name value bad? */
10819
10820 if ((name->value_tag != IPP_TAG_NAME && name->value_tag != IPP_TAG_NAMELANG) ||
10821 name->num_values != 1)
10822 {
10823 bad_name = 1;
10824 }
10825 else
10826 {
10827 /*
10828 * Validate that job-name conforms to RFC 5198 (Network Unicode) and
10829 * IPP Everywhere requirements for "name" values...
10830 */
10831
10832 const unsigned char *nameptr; /* Pointer into "job-name" attribute */
10833
10834 for (nameptr = (unsigned char *)name->values[0].string.text;
10835 *nameptr;
10836 nameptr ++)
10837 {
10838 if (*nameptr < ' ' && *nameptr != '\t')
10839 break;
10840 else if (*nameptr == 0x7f)
10841 break;
10842 else if ((*nameptr & 0xe0) == 0xc0)
10843 {
10844 if ((nameptr[1] & 0xc0) != 0x80)
10845 break;
10846
10847 nameptr ++;
10848 }
10849 else if ((*nameptr & 0xf0) == 0xe0)
10850 {
10851 if ((nameptr[1] & 0xc0) != 0x80 ||
10852 (nameptr[2] & 0xc0) != 0x80)
10853 break;
10854
10855 nameptr += 2;
10856 }
10857 else if ((*nameptr & 0xf8) == 0xf0)
10858 {
10859 if ((nameptr[1] & 0xc0) != 0x80 ||
10860 (nameptr[2] & 0xc0) != 0x80 ||
10861 (nameptr[3] & 0xc0) != 0x80)
10862 break;
10863
10864 nameptr += 3;
10865 }
10866 else if (*nameptr & 0x80)
10867 break;
10868 }
10869
10870 if (*nameptr)
10871 bad_name = 1;
10872 }
10873
10874 if (bad_name)
10875 {
10876 if (StrictConformance)
10877 {
10878 send_ipp_status(con, IPP_ATTRIBUTES,
10879 _("Unsupported 'job-name' value."));
10880 ippCopyAttribute(con->response, name, 0);
10881 return;
10882 }
10883 else
10884 {
10885 cupsdLogMessage(CUPSD_LOG_WARN,
10886 "Unsupported 'job-name' value, deleting from request.");
10887 ippDeleteAttribute(con->request, name);
10888 }
10889 }
10890 }
10891
ef416fc2 10892 /*
10893 * Is the destination valid?
10894 */
10895
f7deaa1a 10896 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 10897 {
10898 /*
10899 * Bad URI...
10900 */
10901
10902 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 10903 _("The printer or class does not exist."));
ef416fc2 10904 return;
10905 }
10906
10907 /*
10908 * Check policy...
10909 */
10910
c7017ecc
MS
10911 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
10912
ef416fc2 10913 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10914 {
f899b121 10915 send_http_error(con, status, printer);
ef416fc2 10916 return;
10917 }
c7017ecc
MS
10918 else if (printer->num_auth_info_required == 1 &&
10919 !strcmp(printer->auth_info_required[0], "negotiate") &&
10920 !con->username[0])
10921 {
10922 send_http_error(con, HTTP_UNAUTHORIZED, printer);
10923 return;
10924 }
10925#ifdef HAVE_SSL
10926 else if (auth_info && !con->http.tls &&
10927 !httpAddrLocalhost(con->http.hostaddr))
10928 {
10929 /*
10930 * Require encryption of auth-info over non-local connections...
10931 */
10932
10933 send_http_error(con, HTTP_UPGRADE_REQUIRED, printer);
10934 return;
10935 }
10936#endif /* HAVE_SSL */
ef416fc2 10937
10938 /*
10939 * Everything was ok, so return OK status...
10940 */
10941
10942 con->response->request.status.status_code = IPP_OK;
10943}
10944
10945
10946/*
10947 * 'validate_name()' - Make sure the printer name only contains valid chars.
10948 */
10949
bc44d920 10950static int /* O - 0 if name is no good, 1 if good */
ef416fc2 10951validate_name(const char *name) /* I - Name to check */
10952{
10953 const char *ptr; /* Pointer into name */
10954
10955
10956 /*
10957 * Scan the whole name...
10958 */
10959
10960 for (ptr = name; *ptr; ptr ++)
f7deaa1a 10961 if ((*ptr > 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
ef416fc2 10962 return (0);
10963
10964 /*
10965 * All the characters are good; validate the length, too...
10966 */
10967
10968 return ((ptr - name) < 128);
10969}
10970
10971
10972/*
10973 * 'validate_user()' - Validate the user for the request.
10974 */
10975
10976static int /* O - 1 if permitted, 0 otherwise */
10977validate_user(cupsd_job_t *job, /* I - Job */
10978 cupsd_client_t *con, /* I - Client connection */
10979 const char *owner, /* I - Owner of job/resource */
10980 char *username, /* O - Authenticated username */
10981 int userlen) /* I - Length of username */
10982{
ef416fc2 10983 cupsd_printer_t *printer; /* Printer for job */
10984
10985
10986 cupsdLogMessage(CUPSD_LOG_DEBUG2,
10987 "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, "
10988 "userlen=%d)",
91c84a35
MS
10989 job->id, con ? con->http.fd : 0,
10990 owner ? owner : "(null)", username, userlen);
ef416fc2 10991
10992 /*
10993 * Validate input...
10994 */
10995
10996 if (!con || !owner || !username || userlen <= 0)
10997 return (0);
10998
10999 /*
11000 * Get the best authenticated username that is available.
11001 */
11002
e00b005a 11003 strlcpy(username, get_username(con), userlen);
ef416fc2 11004
11005 /*
11006 * Check the username against the owner...
11007 */
11008
80ca4592 11009 printer = cupsdFindDest(job->dest);
ef416fc2 11010
11011 return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
11012 con, owner) == HTTP_OK);
11013}
11014
11015
11016/*
71f63681 11017 * End of "$Id: ipp.c 11734 2014-03-25 18:01:47Z msweet $".
ef416fc2 11018 */