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