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