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