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