]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/ipp.c
Validate notify-recipient-uri values.
[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);
beb37da6 2035 if (access(notifier, X_OK) || !strcmp(scheme, ".") || !strcmp(scheme, ".."))
3dfe78b3
MS
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 {
656c109a
MS
5338 _cupsRWLockWrite(&printer->lock);
5339
95ece0cb
MS
5340 if ((!printer->info || !*(printer->info)) && (attr = ippFindAttribute(response, "printer-info", IPP_TAG_TEXT)) != NULL)
5341 cupsdSetString(&printer->info, ippGetString(attr, 0, NULL));
5342
5343 if ((!printer->location || !*(printer->location)) && (attr = ippFindAttribute(response, "printer-location", IPP_TAG_TEXT)) != NULL)
5344 cupsdSetString(&printer->location, ippGetString(attr, 0, NULL));
5345
5346 if ((!printer->geo_location || !*(printer->geo_location)) && (attr = ippFindAttribute(response, "printer-geo-location", IPP_TAG_URI)) != NULL)
5347 cupsdSetString(&printer->geo_location, ippGetString(attr, 0, NULL));
5348
656c109a
MS
5349 _cupsRWUnlock(&printer->lock);
5350
95ece0cb 5351 if ((from = cupsFileOpen(fromppd, "r")) == NULL)
c585706d
MS
5352 {
5353 cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to read generated PPD: %s", printer->name, strerror(errno));
95ece0cb 5354 return (NULL);
c585706d 5355 }
95ece0cb
MS
5356
5357 snprintf(toppd, sizeof(toppd), "%s/ppd/%s.ppd", ServerRoot, printer->name);
5358 if ((to = cupsdCreateConfFile(toppd, ConfigFilePerm)) == NULL)
5359 {
c585706d 5360 cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to create PPD for printer: %s", printer->name, strerror(errno));
95ece0cb
MS
5361 cupsFileClose(from);
5362 return (NULL);
5363 }
5364
5365 while (cupsFileGets(from, line, sizeof(line)))
5366 cupsFilePrintf(to, "%s\n", line);
5367
5368 cupsFileClose(from);
5369 if (!cupsdCloseCreatedConfFile(to, toppd))
5370 {
c585706d
MS
5371 printer->config_time = time(NULL);
5372 printer->state = IPP_PSTATE_IDLE;
5373 printer->accepting = 1;
95ece0cb
MS
5374
5375 cupsdSetPrinterAttrs(printer);
c585706d
MS
5376
5377 cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL, "Printer \"%s\" is now available.", printer->name);
5378 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" is now available.", printer->name);
95ece0cb
MS
5379 }
5380 }
c585706d 5381 else
956bb127 5382 cupsdLogMessage(CUPSD_LOG_ERROR, "%s: PPD creation failed: %s", printer->name, cupsLastErrorString());
95ece0cb
MS
5383
5384 return (NULL);
5385}
5386
5387
7ae00c35
MS
5388/*
5389 * 'create_local_printer()' - Create a local (temporary) print queue.
5390 */
5391
5392static void
5393create_local_printer(
5394 cupsd_client_t *con) /* I - Client connection */
5395{
95ece0cb
MS
5396 ipp_attribute_t *device_uri, /* device-uri attribute */
5397 *printer_geo_location, /* printer-geo-location attribute */
5398 *printer_info, /* printer-info attribute */
5399 *printer_location, /* printer-location attribute */
5400 *printer_name; /* printer-name attribute */
5401 cupsd_printer_t *printer; /* New printer */
5402 http_status_t status; /* Policy status */
5403 char name[128], /* Sanitized printer name */
5404 *nameptr, /* Pointer into name */
5405 uri[1024]; /* printer-uri-supported value */
5406 const char *ptr; /* Pointer into attribute value */
5407
5408
5409 /*
5410 * Require local access to create a local printer...
5411 */
5412
5413 if (!httpAddrLocalhost(httpGetAddress(con->http)))
5414 {
5415 send_ipp_status(con, IPP_STATUS_ERROR_FORBIDDEN, _("Only local users can create a local printer."));
5416 return;
5417 }
5418
5419 /*
5420 * Check any other policy limits...
5421 */
5422
5423 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5424 {
5425 send_http_error(con, status, NULL);
5426 return;
5427 }
5428
5429 /*
5430 * Grab needed attributes...
5431 */
5432
5433 if ((printer_name = ippFindAttribute(con->request, "printer-name", IPP_TAG_ZERO)) == NULL || ippGetGroupTag(printer_name) != IPP_TAG_PRINTER || ippGetValueTag(printer_name) != IPP_TAG_NAME)
5434 {
5435 if (!printer_name)
5436 send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Missing required attribute \"%s\"."), "printer-name");
5437 else if (ippGetGroupTag(printer_name) != IPP_TAG_PRINTER)
5438 send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" is in the wrong group."), "printer-name");
5439 else
5440 send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" is the wrong value type."), "printer-name");
5441
5442 return;
5443 }
5444
5445 for (nameptr = name, ptr = ippGetString(printer_name, 0, NULL); *ptr && nameptr < (name + sizeof(name) - 1); ptr ++)
5446 {
5447 /*
5448 * Sanitize the printer name...
5449 */
5450
5451 if (_cups_isalnum(*ptr))
5452 *nameptr++ = *ptr;
5453 else if (nameptr == name || nameptr[-1] != '_')
5454 *nameptr++ = '_';
5455 }
5456
5457 *nameptr = '\0';
5458
5459 if ((device_uri = ippFindAttribute(con->request, "device-uri", IPP_TAG_ZERO)) == NULL || ippGetGroupTag(device_uri) != IPP_TAG_PRINTER || ippGetValueTag(device_uri) != IPP_TAG_URI)
5460 {
5461 if (!device_uri)
5462 send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Missing required attribute \"%s\"."), "device-uri");
5463 else if (ippGetGroupTag(device_uri) != IPP_TAG_PRINTER)
5464 send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" is in the wrong group."), "device-uri");
5465 else
5466 send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" is the wrong value type."), "device-uri");
5467
5468 return;
5469 }
5470
5471 printer_geo_location = ippFindAttribute(con->request, "printer-geo-location", IPP_TAG_URI);
5472 printer_info = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT);
5473 printer_location = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT);
5474
46385a1a
MS
5475 /*
5476 * See if the printer already exists...
5477 */
5478
5479 if ((printer = cupsdFindDest(name)) != NULL)
5480 {
5481 send_ipp_status(con, IPP_STATUS_ERROR_NOT_POSSIBLE, _("Printer \"%s\" already exists."), name);
5482 goto add_printer_attributes;
5483 }
5484
95ece0cb
MS
5485 /*
5486 * Create the printer...
5487 */
5488
5489 if ((printer = cupsdAddPrinter(name)) == NULL)
5490 {
5491 send_ipp_status(con, IPP_STATUS_ERROR_INTERNAL, _("Unable to create printer."));
5492 return;
5493 }
5494
167e0655
MS
5495 printer->shared = 0;
5496 printer->temporary = 1;
42deee7e 5497
95ece0cb
MS
5498 cupsdSetDeviceURI(printer, ippGetString(device_uri, 0, NULL));
5499
5500 if (printer_geo_location)
5501 cupsdSetString(&printer->geo_location, ippGetString(printer_geo_location, 0, NULL));
5502 if (printer_info)
5503 cupsdSetString(&printer->info, ippGetString(printer_info, 0, NULL));
5504 if (printer_location)
5505 cupsdSetString(&printer->location, ippGetString(printer_location, 0, NULL));
5506
5507 cupsdSetPrinterAttrs(printer);
5508
5509 /*
5510 * Run a background thread to create the PPD...
5511 */
5512
5513 _cupsThreadCreate((_cups_thread_func_t)create_local_bg_thread, printer);
5514
5515 /*
5516 * Return printer attributes...
5517 */
5518
5519 send_ipp_status(con, IPP_STATUS_OK, _("Local printer created."));
5520
46385a1a
MS
5521 add_printer_attributes:
5522
95ece0cb
MS
5523 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", (char)printer->accepting);
5524 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", printer->state);
5525 add_printer_state_reasons(con, printer);
5526
5527 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), httpIsEncrypted(con->http) ? "ipps" : "ipp", NULL, con->clientname, con->clientport, "/printers/%s", printer->name);
5528 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri);
7ae00c35
MS
5529}
5530
5531
ef416fc2 5532/*
5533 * 'create_requested_array()' - Create an array for the requested-attributes.
5534 */
5535
5536static cups_array_t * /* O - Array of attributes or NULL */
5537create_requested_array(ipp_t *request) /* I - IPP request */
5538{
ef416fc2 5539 cups_array_t *ra; /* Requested attributes array */
ef416fc2 5540
5541
5542 /*
db8b865d 5543 * Create the array for standard attributes...
ef416fc2 5544 */
5545
db8b865d 5546 ra = ippCreateRequestedArray(request);
ef416fc2 5547
5548 /*
db8b865d 5549 * Add CUPS defaults as needed...
ef416fc2 5550 */
5551
db8b865d 5552 if (cupsArrayFind(ra, "printer-defaults"))
ef416fc2 5553 {
db8b865d
MS
5554 /*
5555 * Include user-set defaults...
5556 */
ef416fc2 5557
db8b865d 5558 char *name; /* Option name */
f7deaa1a 5559
db8b865d 5560 cupsArrayRemove(ra, "printer-defaults");
f7deaa1a 5561
db8b865d
MS
5562 for (name = (char *)cupsArrayFirst(CommonDefaults);
5563 name;
5564 name = (char *)cupsArrayNext(CommonDefaults))
5565 if (!cupsArrayFind(ra, name))
f7deaa1a 5566 cupsArrayAdd(ra, name);
ef416fc2 5567 }
5568
5569 return (ra);
5570}
5571
5572
5573/*
42404685 5574 * 'create_subscriptions()' - Create one or more notification subscriptions.
ef416fc2 5575 */
5576
5577static void
42404685 5578create_subscriptions(
ef416fc2 5579 cupsd_client_t *con, /* I - Client connection */
5580 ipp_attribute_t *uri) /* I - Printer URI */
5581{
5582 http_status_t status; /* Policy status */
5583 int i; /* Looping var */
5584 ipp_attribute_t *attr; /* Current attribute */
bc44d920 5585 cups_ptype_t dtype; /* Destination type (printer/class) */
ed486911 5586 char scheme[HTTP_MAX_URI],
5587 /* Scheme portion of URI */
ef416fc2 5588 userpass[HTTP_MAX_URI],
5589 /* Username portion of URI */
5590 host[HTTP_MAX_URI],
5591 /* Host portion of URI */
5592 resource[HTTP_MAX_URI];
5593 /* Resource portion of URI */
5594 int port; /* Port portion of URI */
5595 cupsd_printer_t *printer; /* Printer/class */
5596 cupsd_job_t *job; /* Job */
5597 int jobid; /* Job ID */
5598 cupsd_subscription_t *sub; /* Subscription object */
bc44d920 5599 const char *username, /* requesting-user-name or
5600 authenticated username */
ef416fc2 5601 *recipient, /* notify-recipient-uri */
5602 *pullmethod; /* notify-pull-method */
5603 ipp_attribute_t *user_data; /* notify-user-data */
5604 int interval, /* notify-time-interval */
5605 lease; /* notify-lease-duration */
5606 unsigned mask; /* notify-events */
f7deaa1a 5607 ipp_attribute_t *notify_events,/* notify-events(-default) */
5608 *notify_lease; /* notify-lease-duration(-default) */
ef416fc2 5609
5610
bd7854cb 5611#ifdef DEBUG
5612 for (attr = con->request->attrs; attr; attr = attr->next)
5613 {
5614 if (attr->group_tag != IPP_TAG_ZERO)
3dfe78b3 5615 cupsdLogMessage(CUPSD_LOG_DEBUG2, "g%04x v%04x %s", attr->group_tag,
bd7854cb 5616 attr->value_tag, attr->name);
5617 else
3dfe78b3 5618 cupsdLogMessage(CUPSD_LOG_DEBUG2, "----SEP----");
bd7854cb 5619 }
5620#endif /* DEBUG */
5621
ef416fc2 5622 /*
5623 * Is the destination valid?
5624 */
5625
42404685 5626 cupsdLogMessage(CUPSD_LOG_DEBUG, "create_subscriptions(con=%p(%d), uri=\"%s\")", con, con->number, uri->values[0].string.text);
fa73b229 5627
ed486911 5628 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
5629 sizeof(scheme), userpass, sizeof(userpass), host,
a4d04587 5630 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 5631
5632 if (!strcmp(resource, "/"))
5633 {
ef416fc2 5634 dtype = (cups_ptype_t)0;
5635 printer = NULL;
5636 }
5637 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
5638 {
ef416fc2 5639 dtype = (cups_ptype_t)0;
5640 printer = NULL;
5641 }
5642 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
5643 {
ef416fc2 5644 dtype = CUPS_PRINTER_CLASS;
5645 printer = NULL;
5646 }
f7deaa1a 5647 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 5648 {
5649 /*
5650 * Bad URI...
5651 */
5652
5653 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 5654 _("The printer or class does not exist."));
ef416fc2 5655 return;
5656 }
5657
5658 /*
5659 * Check policy...
5660 */
5661
5662 if (printer)
5663 {
f7deaa1a 5664 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
5665 NULL)) != HTTP_OK)
ef416fc2 5666 {
f899b121 5667 send_http_error(con, status, printer);
ef416fc2 5668 return;
5669 }
5670 }
5671 else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5672 {
f899b121 5673 send_http_error(con, status, NULL);
ef416fc2 5674 return;
5675 }
5676
5677 /*
5678 * Get the user that is requesting the subscription...
5679 */
5680
e00b005a 5681 username = get_username(con);
ef416fc2 5682
5683 /*
5684 * Find the first subscription group attribute; return if we have
5685 * none...
5686 */
5687
5688 for (attr = con->request->attrs; attr; attr = attr->next)
5689 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
5690 break;
5691
5692 if (!attr)
5693 {
5694 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 5695 _("No subscription attributes in request."));
ef416fc2 5696 return;
5697 }
5698
5699 /*
5700 * Process the subscription attributes in the request...
5701 */
5702
fa73b229 5703 con->response->request.status.status_code = IPP_BAD_REQUEST;
5704
ef416fc2 5705 while (attr)
5706 {
5707 recipient = NULL;
5708 pullmethod = NULL;
5709 user_data = NULL;
5710 interval = 0;
5711 lease = DefaultLeaseDuration;
5712 jobid = 0;
5713 mask = CUPSD_EVENT_NONE;
5714
f7deaa1a 5715 if (printer)
5716 {
5717 notify_events = ippFindAttribute(printer->attrs, "notify-events-default",
5718 IPP_TAG_KEYWORD);
5719 notify_lease = ippFindAttribute(printer->attrs,
5720 "notify-lease-duration-default",
5721 IPP_TAG_INTEGER);
5722
5723 if (notify_lease)
5724 lease = notify_lease->values[0].integer;
5725 }
5726 else
5727 {
5728 notify_events = NULL;
5729 notify_lease = NULL;
5730 }
5731
ef416fc2 5732 while (attr && attr->group_tag != IPP_TAG_ZERO)
5733 {
7594b224 5734 if (!strcmp(attr->name, "notify-recipient-uri") &&
ef416fc2 5735 attr->value_tag == IPP_TAG_URI)
ed486911 5736 {
5737 /*
5738 * Validate the recipient scheme against the ServerBin/notifier
5739 * directory...
5740 */
5741
5742 char notifier[1024]; /* Notifier filename */
5743
5744
ef416fc2 5745 recipient = attr->values[0].string.text;
ed486911 5746
8ca02f3c 5747 if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
5748 scheme, sizeof(scheme), userpass, sizeof(userpass),
5749 host, sizeof(host), &port,
5750 resource, sizeof(resource)) < HTTP_URI_OK)
ed486911 5751 {
5752 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 5753 _("Bad notify-recipient-uri \"%s\"."), recipient);
ed486911 5754 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5755 "notify-status-code", IPP_URI_SCHEME);
5756 return;
5757 }
5758
5759 snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
5760 scheme);
5761 if (access(notifier, X_OK))
5762 {
5763 send_ipp_status(con, IPP_NOT_POSSIBLE,
bc44d920 5764 _("notify-recipient-uri URI \"%s\" uses unknown "
84315f46 5765 "scheme."), recipient);
ed486911 5766 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5767 "notify-status-code", IPP_URI_SCHEME);
5768 return;
5769 }
3dfe78b3
MS
5770
5771 if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
5772 {
5773 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 5774 _("notify-recipient-uri URI \"%s\" is already used."),
3dfe78b3
MS
5775 recipient);
5776 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5777 "notify-status-code", IPP_ATTRIBUTES);
5778 return;
5779 }
ed486911 5780 }
ef416fc2 5781 else if (!strcmp(attr->name, "notify-pull-method") &&
5782 attr->value_tag == IPP_TAG_KEYWORD)
ed486911 5783 {
ef416fc2 5784 pullmethod = attr->values[0].string.text;
ed486911 5785
5786 if (strcmp(pullmethod, "ippget"))
5787 {
5788 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 5789 _("Bad notify-pull-method \"%s\"."), pullmethod);
ed486911 5790 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
5791 "notify-status-code", IPP_ATTRIBUTES);
5792 return;
5793 }
5794 }
ef416fc2 5795 else if (!strcmp(attr->name, "notify-charset") &&
5796 attr->value_tag == IPP_TAG_CHARSET &&
5797 strcmp(attr->values[0].string.text, "us-ascii") &&
5798 strcmp(attr->values[0].string.text, "utf-8"))
5799 {
5800 send_ipp_status(con, IPP_CHARSET,
84315f46 5801 _("Character set \"%s\" not supported."),
ef416fc2 5802 attr->values[0].string.text);
5803 return;
5804 }
5805 else if (!strcmp(attr->name, "notify-natural-language") &&
5806 (attr->value_tag != IPP_TAG_LANGUAGE ||
5807 strcmp(attr->values[0].string.text, DefaultLanguage)))
5808 {
5809 send_ipp_status(con, IPP_CHARSET,
84315f46 5810 _("Language \"%s\" not supported."),
ef416fc2 5811 attr->values[0].string.text);
5812 return;
5813 }
5814 else if (!strcmp(attr->name, "notify-user-data") &&
5815 attr->value_tag == IPP_TAG_STRING)
5816 {
5817 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
5818 {
5819 send_ipp_status(con, IPP_REQUEST_VALUE,
5820 _("The notify-user-data value is too large "
84315f46 5821 "(%d > 63 octets)."),
ef416fc2 5822 attr->values[0].unknown.length);
5823 return;
5824 }
5825
5826 user_data = attr;
5827 }
5828 else if (!strcmp(attr->name, "notify-events") &&
5829 attr->value_tag == IPP_TAG_KEYWORD)
f7deaa1a 5830 notify_events = attr;
ef416fc2 5831 else if (!strcmp(attr->name, "notify-lease-duration") &&
5832 attr->value_tag == IPP_TAG_INTEGER)
5833 lease = attr->values[0].integer;
5834 else if (!strcmp(attr->name, "notify-time-interval") &&
5835 attr->value_tag == IPP_TAG_INTEGER)
5836 interval = attr->values[0].integer;
5837 else if (!strcmp(attr->name, "notify-job-id") &&
5838 attr->value_tag == IPP_TAG_INTEGER)
5839 jobid = attr->values[0].integer;
5840
5841 attr = attr->next;
5842 }
5843
f7deaa1a 5844 if (notify_events)
5845 {
5846 for (i = 0; i < notify_events->num_values; i ++)
5847 mask |= cupsdEventValue(notify_events->values[i].string.text);
5848 }
5849
bd7854cb 5850 if (recipient)
4cadd620 5851 {
bd7854cb 5852 cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
4cadd620
MS
5853
5854
5855 if (!strncmp(recipient, "mailto:", 7) && user_data)
5856 {
5857 char temp[64]; /* Temporary string */
5858
5859 memcpy(temp, user_data->values[0].unknown.data, user_data->values[0].unknown.length);
5860 temp[user_data->values[0].unknown.length] = '\0';
5861
5862 if (httpSeparateURI(HTTP_URI_CODING_ALL, temp, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_OK)
5863 {
5864 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad notify-user-data \"%s\"."), temp);
5865 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, "notify-status-code", IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES);
5866 return;
5867 }
5868 }
5869 }
5870
bd7854cb 5871 if (pullmethod)
5872 cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
5873 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
5874 cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-time-interval=%d", interval);
fa73b229 5875
ef416fc2 5876 if (!recipient && !pullmethod)
5877 break;
5878
5879 if (mask == CUPSD_EVENT_NONE)
5880 {
5881 if (jobid)
5882 mask = CUPSD_EVENT_JOB_COMPLETED;
5883 else if (printer)
5884 mask = CUPSD_EVENT_PRINTER_STATE_CHANGED;
5885 else
5886 {
5887 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 5888 _("notify-events not specified."));
ef416fc2 5889 return;
5890 }
5891 }
5892
bd7854cb 5893 if (MaxLeaseDuration && (lease == 0 || lease > MaxLeaseDuration))
ef416fc2 5894 {
5895 cupsdLogMessage(CUPSD_LOG_INFO,
42404685 5896 "create_subscriptions: Limiting notify-lease-duration to "
ef416fc2 5897 "%d seconds.",
5898 MaxLeaseDuration);
5899 lease = MaxLeaseDuration;
5900 }
5901
5902 if (jobid)
5903 {
5904 if ((job = cupsdFindJob(jobid)) == NULL)
5905 {
84315f46
MS
5906 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
5907 jobid);
ef416fc2 5908 return;
5909 }
5910 }
5911 else
5912 job = NULL;
5913
52f6f666
MS
5914 if ((sub = cupsdAddSubscription(mask, printer, job, recipient, 0)) == NULL)
5915 {
5916 send_ipp_status(con, IPP_TOO_MANY_SUBSCRIPTIONS,
5917 _("There are too many subscriptions."));
5918 return;
5919 }
ef416fc2 5920
fa73b229 5921 if (job)
84315f46 5922 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for job %d.",
fa73b229 5923 sub->id, job->id);
5924 else if (printer)
5925 cupsdLogMessage(CUPSD_LOG_DEBUG,
84315f46 5926 "Added subscription #%d for printer \"%s\".",
fa73b229 5927 sub->id, printer->name);
5928 else
84315f46 5929 cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for server.",
fa73b229 5930 sub->id);
5931
ef416fc2 5932 sub->interval = interval;
5933 sub->lease = lease;
bd7854cb 5934 sub->expire = lease ? time(NULL) + lease : 0;
ef416fc2 5935
5936 cupsdSetString(&sub->owner, username);
5937
5938 if (user_data)
5939 {
5940 sub->user_data_len = user_data->values[0].unknown.length;
5941 memcpy(sub->user_data, user_data->values[0].unknown.data,
07623986 5942 (size_t)sub->user_data_len);
ef416fc2 5943 }
5944
5945 ippAddSeparator(con->response);
5946 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5947 "notify-subscription-id", sub->id);
5948
fa73b229 5949 con->response->request.status.status_code = IPP_OK;
5950
ef416fc2 5951 if (attr)
5952 attr = attr->next;
5953 }
5954
3dfe78b3 5955 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
ef416fc2 5956}
5957
5958
5959/*
5960 * 'delete_printer()' - Remove a printer or class from the system.
5961 */
5962
5963static void
5964delete_printer(cupsd_client_t *con, /* I - Client connection */
5965 ipp_attribute_t *uri) /* I - URI of printer or class */
5966{
5967 http_status_t status; /* Policy status */
bc44d920 5968 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 5969 cupsd_printer_t *printer; /* Printer/class */
5970 char filename[1024]; /* Script/PPD filename */
7ae00c35 5971 int temporary; /* Temporary queue? */
ef416fc2 5972
5973
5974 cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", con,
996acce8 5975 con->number, uri->values[0].string.text);
ef416fc2 5976
5977 /*
5978 * Do we have a valid URI?
5979 */
5980
f7deaa1a 5981 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 5982 {
5983 /*
5984 * Bad URI...
5985 */
5986
5987 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 5988 _("The printer or class does not exist."));
ef416fc2 5989 return;
5990 }
5991
5992 /*
5993 * Check policy...
5994 */
5995
5996 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5997 {
f899b121 5998 send_http_error(con, status, NULL);
ef416fc2 5999 return;
6000 }
6001
6002 /*
6003 * Remove old jobs...
6004 */
6005
f7deaa1a 6006 cupsdCancelJobs(printer->name, NULL, 1);
ef416fc2 6007
6008 /*
6009 * Remove old subscriptions and send a "deleted printer" event...
6010 */
6011
6012 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
6013 "%s \"%s\" deleted by \"%s\".",
6014 (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
f7deaa1a 6015 printer->name, get_username(con));
ef416fc2 6016
6017 cupsdExpireSubscriptions(printer, NULL);
f7deaa1a 6018
ef416fc2 6019 /*
6020 * Remove any old PPD or script files...
6021 */
6022
f7deaa1a 6023 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
6024 printer->name);
ef416fc2 6025 unlink(filename);
928b43f7
MS
6026 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd.O", ServerRoot,
6027 printer->name);
6028 unlink(filename);
ef416fc2 6029
7cf5915e 6030 snprintf(filename, sizeof(filename), "%s/%s.png", CacheDir, printer->name);
61cf44e2
MS
6031 unlink(filename);
6032
f14324a7 6033 snprintf(filename, sizeof(filename), "%s/%s.data", CacheDir, printer->name);
54afec33
MS
6034 unlink(filename);
6035
568fa3fa
MS
6036 /*
6037 * Unregister color profiles...
6038 */
6039
a29fd7dd 6040 cupsdUnregisterColor(printer);
568fa3fa 6041
7ae00c35
MS
6042 temporary = printer->temporary;
6043
ef416fc2 6044 if (dtype & CUPS_PRINTER_CLASS)
6045 {
f7deaa1a 6046 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".",
6047 printer->name, get_username(con));
ef416fc2 6048
6049 cupsdDeletePrinter(printer, 0);
7ae00c35
MS
6050 if (!temporary)
6051 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
ef416fc2 6052 }
6053 else
6054 {
f7deaa1a 6055 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".",
6056 printer->name, get_username(con));
ef416fc2 6057
7ae00c35 6058 if (cupsdDeletePrinter(printer, 0) && !temporary)
f8b3a85b
MS
6059 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
6060
7ae00c35
MS
6061 if (!temporary)
6062 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 6063 }
6064
7ae00c35
MS
6065 if (!temporary)
6066 cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
ef416fc2 6067
6068 /*
6069 * Return with no errors...
6070 */
6071
6072 con->response->request.status.status_code = IPP_OK;
6073}
6074
6075
6076/*
6077 * 'get_default()' - Get the default destination.
6078 */
6079
6080static void
6081get_default(cupsd_client_t *con) /* I - Client connection */
6082{
fa73b229 6083 http_status_t status; /* Policy status */
6084 cups_array_t *ra; /* Requested attributes array */
ef416fc2 6085
6086
996acce8 6087 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->number);
ef416fc2 6088
6089 /*
6090 * Check policy...
6091 */
6092
6093 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6094 {
f899b121 6095 send_http_error(con, status, NULL);
ef416fc2 6096 return;
6097 }
6098
fa73b229 6099 if (DefaultPrinter)
ef416fc2 6100 {
fa73b229 6101 ra = create_requested_array(con->request);
ef416fc2 6102
fa73b229 6103 copy_printer_attrs(con, DefaultPrinter, ra);
ef416fc2 6104
fa73b229 6105 cupsArrayDelete(ra);
ef416fc2 6106
fa73b229 6107 con->response->request.status.status_code = IPP_OK;
ef416fc2 6108 }
6109 else
84315f46 6110 send_ipp_status(con, IPP_NOT_FOUND, _("No default printer."));
ef416fc2 6111}
6112
6113
6114/*
6115 * 'get_devices()' - Get the list of available devices on the local system.
6116 */
6117
6118static void
6119get_devices(cupsd_client_t *con) /* I - Client connection */
6120{
6121 http_status_t status; /* Policy status */
ae71f5de
MS
6122 ipp_attribute_t *limit, /* limit attribute */
6123 *timeout, /* timeout attribute */
6124 *requested, /* requested-attributes attribute */
ed6e7faf
MS
6125 *exclude, /* exclude-schemes attribute */
6126 *include; /* include-schemes attribute */
ef416fc2 6127 char command[1024], /* cups-deviced command */
ed6e7faf 6128 options[2048], /* Options to pass to command */
ae71f5de 6129 requested_str[256],
89d46774 6130 /* String for requested attributes */
ed6e7faf
MS
6131 exclude_str[512],
6132 /* String for excluded schemes */
6133 include_str[512];
6134 /* String for included schemes */
ef416fc2 6135
6136
996acce8 6137 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->number);
ef416fc2 6138
6139 /*
6140 * Check policy...
6141 */
6142
6143 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6144 {
f899b121 6145 send_http_error(con, status, NULL);
ef416fc2 6146 return;
6147 }
6148
6149 /*
6150 * Run cups-deviced command with the given options...
6151 */
6152
ae71f5de
MS
6153 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
6154 timeout = ippFindAttribute(con->request, "timeout", IPP_TAG_INTEGER);
ef416fc2 6155 requested = ippFindAttribute(con->request, "requested-attributes",
6156 IPP_TAG_KEYWORD);
ae71f5de 6157 exclude = ippFindAttribute(con->request, "exclude-schemes", IPP_TAG_NAME);
ed6e7faf 6158 include = ippFindAttribute(con->request, "include-schemes", IPP_TAG_NAME);
ef416fc2 6159
6160 if (requested)
89d46774 6161 url_encode_attr(requested, requested_str, sizeof(requested_str));
ef416fc2 6162 else
89d46774 6163 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
ef416fc2 6164
ae71f5de
MS
6165 if (exclude)
6166 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
6167 else
6168 exclude_str[0] = '\0';
6169
ed6e7faf
MS
6170 if (include)
6171 url_encode_attr(include, include_str, sizeof(include_str));
6172 else
6173 include_str[0] = '\0';
6174
ef416fc2 6175 snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
6176 snprintf(options, sizeof(options),
ed6e7faf 6177 "%d+%d+%d+%d+%s%s%s%s%s",
ef416fc2 6178 con->request->request.op.request_id,
ae71f5de 6179 limit ? limit->values[0].integer : 0,
7a0cbd5e 6180 timeout ? timeout->values[0].integer : 15,
ae71f5de
MS
6181 (int)User,
6182 requested_str,
ed6e7faf
MS
6183 exclude_str[0] ? "%20" : "", exclude_str,
6184 include_str[0] ? "%20" : "", include_str);
ef416fc2 6185
6186 if (cupsdSendCommand(con, command, options, 1))
6187 {
6188 /*
6189 * Command started successfully, don't send an IPP response here...
6190 */
6191
6192 ippDelete(con->response);
6193 con->response = NULL;
6194 }
6195 else
6196 {
6197 /*
6198 * Command failed, return "internal error" so the user knows something
6199 * went wrong...
6200 */
6201
6202 send_ipp_status(con, IPP_INTERNAL_ERROR,
6203 _("cups-deviced failed to execute."));
6204 }
6205}
6206
6207
2e4ff8af
MS
6208/*
6209 * 'get_document()' - Get a copy of a job file.
6210 */
6211
6212static void
6213get_document(cupsd_client_t *con, /* I - Client connection */
6214 ipp_attribute_t *uri) /* I - Job URI */
6215{
6216 http_status_t status; /* Policy status */
6217 ipp_attribute_t *attr; /* Current attribute */
6218 int jobid; /* Job ID */
6219 int docnum; /* Document number */
6220 cupsd_job_t *job; /* Current job */
61cf44e2 6221 char scheme[HTTP_MAX_URI], /* Method portion of URI */
2e4ff8af
MS
6222 username[HTTP_MAX_URI], /* Username portion of URI */
6223 host[HTTP_MAX_URI], /* Host portion of URI */
6224 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6225 int port; /* Port portion of URI */
6226 char filename[1024], /* Filename for document */
6227 format[1024]; /* Format for document */
6228
6229
6230 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con,
996acce8 6231 con->number, uri->values[0].string.text);
2e4ff8af
MS
6232
6233 /*
6234 * See if we have a job URI or a printer URI...
6235 */
6236
6237 if (!strcmp(uri->name, "printer-uri"))
6238 {
6239 /*
6240 * Got a printer URI; see if we also have a job-id attribute...
6241 */
6242
6243 if ((attr = ippFindAttribute(con->request, "job-id",
6244 IPP_TAG_INTEGER)) == NULL)
6245 {
6246 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 6247 _("Got a printer-uri attribute but no job-id."));
2e4ff8af
MS
6248 return;
6249 }
6250
6251 jobid = attr->values[0].integer;
6252 }
6253 else
6254 {
6255 /*
6256 * Got a job URI; parse it to get the job ID...
6257 */
6258
61cf44e2
MS
6259 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6260 sizeof(scheme), username, sizeof(username), host,
2e4ff8af
MS
6261 sizeof(host), &port, resource, sizeof(resource));
6262
6263 if (strncmp(resource, "/jobs/", 6))
6264 {
6265 /*
6266 * Not a valid URI!
6267 */
6268
84315f46 6269 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
2e4ff8af
MS
6270 uri->values[0].string.text);
6271 return;
6272 }
6273
6274 jobid = atoi(resource + 6);
6275 }
6276
6277 /*
6278 * See if the job exists...
6279 */
6280
6281 if ((job = cupsdFindJob(jobid)) == NULL)
6282 {
6283 /*
6284 * Nope - return a "not found" error...
6285 */
6286
84315f46 6287 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
2e4ff8af
MS
6288 return;
6289 }
6290
6291 /*
6292 * Check policy...
6293 */
6294
e60ec91f
MS
6295 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con,
6296 job->username)) != HTTP_OK)
2e4ff8af
MS
6297 {
6298 send_http_error(con, status, NULL);
6299 return;
6300 }
6301
6302 /*
6303 * Get the document number...
6304 */
6305
6306 if ((attr = ippFindAttribute(con->request, "document-number",
6307 IPP_TAG_INTEGER)) == NULL)
6308 {
6309 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 6310 _("Missing document-number attribute."));
2e4ff8af
MS
6311 return;
6312 }
6313
6314 if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files ||
6315 attr->num_values > 1)
6316 {
84315f46
MS
6317 send_ipp_status(con, IPP_NOT_FOUND,
6318 _("Document #%d does not exist in job #%d."), docnum,
6319 jobid);
2e4ff8af
MS
6320 return;
6321 }
6322
6323 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, jobid,
6324 docnum);
6325 if ((con->file = open(filename, O_RDONLY)) == -1)
6326 {
6327 cupsdLogMessage(CUPSD_LOG_ERROR,
6328 "Unable to open document %d in job %d - %s", docnum, jobid,
6329 strerror(errno));
6330 send_ipp_status(con, IPP_NOT_FOUND,
84315f46
MS
6331 _("Unable to open document #%d in job #%d."), docnum,
6332 jobid);
2e4ff8af
MS
6333 return;
6334 }
6335
6336 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
6337
6338 cupsdLoadJob(job);
6339
6340 snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super,
6341 job->filetypes[docnum - 1]->type);
6342
6343 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
6344 NULL, format);
6345 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "document-number",
6346 docnum);
6347 if ((attr = ippFindAttribute(job->attrs, "document-name",
6348 IPP_TAG_NAME)) != NULL)
6349 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "document-name",
6350 NULL, attr->values[0].string.text);
6351}
6352
6353
ef416fc2 6354/*
fa73b229 6355 * 'get_job_attrs()' - Get job attributes.
ef416fc2 6356 */
6357
6358static void
fa73b229 6359get_job_attrs(cupsd_client_t *con, /* I - Client connection */
6360 ipp_attribute_t *uri) /* I - Job URI */
ef416fc2 6361{
6362 http_status_t status; /* Policy status */
fa73b229 6363 ipp_attribute_t *attr; /* Current attribute */
6364 int jobid; /* Job ID */
6365 cupsd_job_t *job; /* Current job */
ef55b745 6366 cupsd_printer_t *printer; /* Current printer */
10d09e33
MS
6367 cupsd_policy_t *policy; /* Current security policy */
6368 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
ef416fc2 6369 username[HTTP_MAX_URI], /* Username portion of URI */
6370 host[HTTP_MAX_URI], /* Host portion of URI */
6371 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6372 int port; /* Port portion of URI */
10d09e33
MS
6373 cups_array_t *ra, /* Requested attributes array */
6374 *exclude; /* Private attributes array */
ef416fc2 6375
6376
fa73b229 6377 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
996acce8 6378 con->number, uri->values[0].string.text);
ef416fc2 6379
6380 /*
fa73b229 6381 * See if we have a job URI or a printer URI...
6382 */
6383
6384 if (!strcmp(uri->name, "printer-uri"))
6385 {
6386 /*
6387 * Got a printer URI; see if we also have a job-id attribute...
6388 */
6389
6390 if ((attr = ippFindAttribute(con->request, "job-id",
6391 IPP_TAG_INTEGER)) == NULL)
6392 {
6393 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 6394 _("Got a printer-uri attribute but no job-id."));
fa73b229 6395 return;
6396 }
6397
6398 jobid = attr->values[0].integer;
6399 }
6400 else
6401 {
6402 /*
6403 * Got a job URI; parse it to get the job ID...
6404 */
6405
61cf44e2
MS
6406 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6407 sizeof(scheme), username, sizeof(username), host,
a4d04587 6408 sizeof(host), &port, resource, sizeof(resource));
fa73b229 6409
6410 if (strncmp(resource, "/jobs/", 6))
6411 {
6412 /*
6413 * Not a valid URI!
6414 */
6415
84315f46 6416 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
fa73b229 6417 uri->values[0].string.text);
6418 return;
6419 }
6420
6421 jobid = atoi(resource + 6);
6422 }
6423
6424 /*
6425 * See if the job exists...
6426 */
6427
6428 if ((job = cupsdFindJob(jobid)) == NULL)
6429 {
6430 /*
6431 * Nope - return a "not found" error...
6432 */
6433
84315f46 6434 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
fa73b229 6435 return;
6436 }
6437
6438 /*
6439 * Check policy...
6440 */
6441
ef55b745
MS
6442 if ((printer = job->printer) == NULL)
6443 printer = cupsdFindDest(job->dest);
6444
6445 if (printer)
10d09e33
MS
6446 policy = printer->op_policy_ptr;
6447 else
6448 policy = DefaultPolicyPtr;
6449
6450 if ((status = cupsdCheckPolicy(policy, con, job->username)) != HTTP_OK)
fa73b229 6451 {
f899b121 6452 send_http_error(con, status, NULL);
fa73b229 6453 return;
6454 }
6455
10d09e33
MS
6456 exclude = cupsdGetPrivateAttrs(policy, con, printer, job->username);
6457
fa73b229 6458 /*
6459 * Copy attributes...
6460 */
6461
bd7854cb 6462 cupsdLoadJob(job);
6463
fa73b229 6464 ra = create_requested_array(con->request);
10d09e33 6465 copy_job_attrs(con, job, ra, exclude);
fa73b229 6466 cupsArrayDelete(ra);
6467
6468 con->response->request.status.status_code = IPP_OK;
6469}
6470
6471
6472/*
6473 * 'get_jobs()' - Get a list of jobs for the specified printer.
6474 */
6475
6476static void
6477get_jobs(cupsd_client_t *con, /* I - Client connection */
6478 ipp_attribute_t *uri) /* I - Printer URI */
6479{
6480 http_status_t status; /* Policy status */
6481 ipp_attribute_t *attr; /* Current attribute */
6482 const char *dest; /* Destination */
bc44d920 6483 cups_ptype_t dtype; /* Destination type (printer/class) */
fa73b229 6484 cups_ptype_t dmask; /* Destination type mask */
f7deaa1a 6485 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
fa73b229 6486 username[HTTP_MAX_URI], /* Username portion of URI */
6487 host[HTTP_MAX_URI], /* Host portion of URI */
6488 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6489 int port; /* Port portion of URI */
aaf19ab0
MS
6490 int job_comparison; /* Job comparison */
6491 ipp_jstate_t job_state; /* job-state value */
f16ea703
MS
6492 int first_job_id = 1, /* First job ID */
6493 first_index = 1, /* First index */
9e6d7a0f
MS
6494 limit = 0, /* Maximum number of jobs to return */
6495 count, /* Number of jobs that match */
6496 need_load_job = 0; /* Do we need to load the job? */
f16ea703 6497 const char *job_attr; /* Job attribute requested */
aaf19ab0 6498 ipp_attribute_t *job_ids; /* job-ids attribute */
fa73b229 6499 cupsd_job_t *job; /* Current job pointer */
6500 cupsd_printer_t *printer; /* Printer */
6501 cups_array_t *list; /* Which job list... */
eec1fbc3 6502 int delete_list = 0; /* Delete the list afterwards? */
10d09e33
MS
6503 cups_array_t *ra, /* Requested attributes array */
6504 *exclude; /* Private attributes array */
6505 cupsd_policy_t *policy; /* Current policy */
fa73b229 6506
6507
996acce8 6508 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->number,
fa73b229 6509 uri->values[0].string.text);
6510
6511 /*
6512 * Is the destination valid?
ef416fc2 6513 */
6514
4b3f67ff
MS
6515 if (strcmp(uri->name, "printer-uri"))
6516 {
84315f46 6517 send_ipp_status(con, IPP_BAD_REQUEST, _("No printer-uri in request."));
4b3f67ff
MS
6518 return;
6519 }
6520
f7deaa1a 6521 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6522 sizeof(scheme), username, sizeof(username), host,
a4d04587 6523 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 6524
b9faaae1 6525 if (!strcmp(resource, "/") || !strcmp(resource, "/jobs"))
ef416fc2 6526 {
6527 dest = NULL;
6528 dtype = (cups_ptype_t)0;
6529 dmask = (cups_ptype_t)0;
6530 printer = NULL;
6531 }
fa73b229 6532 else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
ef416fc2 6533 {
6534 dest = NULL;
6535 dtype = (cups_ptype_t)0;
6536 dmask = CUPS_PRINTER_CLASS;
6537 printer = NULL;
6538 }
fa73b229 6539 else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
ef416fc2 6540 {
6541 dest = NULL;
6542 dtype = CUPS_PRINTER_CLASS;
6543 dmask = CUPS_PRINTER_CLASS;
6544 printer = NULL;
6545 }
f7deaa1a 6546 else if ((dest = cupsdValidateDest(uri->values[0].string.text, &dtype,
6547 &printer)) == NULL)
ef416fc2 6548 {
6549 /*
6550 * Bad URI...
6551 */
6552
6553 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 6554 _("The printer or class does not exist."));
ef416fc2 6555 return;
6556 }
6557 else
b86bc4cf 6558 {
6559 dtype &= CUPS_PRINTER_CLASS;
ef416fc2 6560 dmask = CUPS_PRINTER_CLASS;
b86bc4cf 6561 }
ef416fc2 6562
6563 /*
6564 * Check policy...
6565 */
6566
6567 if (printer)
10d09e33
MS
6568 policy = printer->op_policy_ptr;
6569 else
6570 policy = DefaultPolicyPtr;
6571
6572 if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
ef416fc2 6573 {
f899b121 6574 send_http_error(con, status, NULL);
ef416fc2 6575 return;
6576 }
6577
aaf19ab0
MS
6578 job_ids = ippFindAttribute(con->request, "job-ids", IPP_TAG_INTEGER);
6579
ef416fc2 6580 /*
6581 * See if the "which-jobs" attribute have been specified...
6582 */
6583
fa73b229 6584 if ((attr = ippFindAttribute(con->request, "which-jobs",
aaf19ab0
MS
6585 IPP_TAG_KEYWORD)) != NULL && job_ids)
6586 {
6587 send_ipp_status(con, IPP_CONFLICT,
6588 _("The %s attribute cannot be provided with job-ids."),
6589 "which-jobs");
6590 return;
6591 }
6592 else if (!attr || !strcmp(attr->values[0].string.text, "not-completed"))
6593 {
6594 job_comparison = -1;
6595 job_state = IPP_JOB_STOPPED;
e584c068 6596 list = ActiveJobs;
aaf19ab0
MS
6597 }
6598 else if (!strcmp(attr->values[0].string.text, "completed"))
6599 {
6600 job_comparison = 1;
6601 job_state = IPP_JOB_CANCELED;
eec1fbc3
MS
6602 list = cupsdGetCompletedJobs(printer);
6603 delete_list = 1;
aaf19ab0
MS
6604 }
6605 else if (!strcmp(attr->values[0].string.text, "aborted"))
6606 {
6607 job_comparison = 0;
6608 job_state = IPP_JOB_ABORTED;
eec1fbc3
MS
6609 list = cupsdGetCompletedJobs(printer);
6610 delete_list = 1;
aaf19ab0
MS
6611 }
6612 else if (!strcmp(attr->values[0].string.text, "all"))
6613 {
6614 job_comparison = 1;
6615 job_state = IPP_JOB_PENDING;
6616 list = Jobs;
6617 }
6618 else if (!strcmp(attr->values[0].string.text, "canceled"))
6619 {
6620 job_comparison = 0;
6621 job_state = IPP_JOB_CANCELED;
eec1fbc3
MS
6622 list = cupsdGetCompletedJobs(printer);
6623 delete_list = 1;
aaf19ab0
MS
6624 }
6625 else if (!strcmp(attr->values[0].string.text, "pending"))
ef416fc2 6626 {
aaf19ab0
MS
6627 job_comparison = 0;
6628 job_state = IPP_JOB_PENDING;
6629 list = ActiveJobs;
ef416fc2 6630 }
aaf19ab0 6631 else if (!strcmp(attr->values[0].string.text, "pending-held"))
ef416fc2 6632 {
aaf19ab0
MS
6633 job_comparison = 0;
6634 job_state = IPP_JOB_HELD;
6635 list = ActiveJobs;
ef416fc2 6636 }
aaf19ab0 6637 else if (!strcmp(attr->values[0].string.text, "processing"))
3dfe78b3 6638 {
aaf19ab0
MS
6639 job_comparison = 0;
6640 job_state = IPP_JOB_PROCESSING;
6641 list = PrintingJobs;
6642 }
6643 else if (!strcmp(attr->values[0].string.text, "processing-stopped"))
6644 {
6645 job_comparison = 0;
6646 job_state = IPP_JOB_STOPPED;
6647 list = ActiveJobs;
3dfe78b3 6648 }
ef416fc2 6649 else
6650 {
aaf19ab0
MS
6651 send_ipp_status(con, IPP_ATTRIBUTES,
6652 _("The which-jobs value \"%s\" is not supported."),
6653 attr->values[0].string.text);
6654 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
6655 "which-jobs", NULL, attr->values[0].string.text);
6656 return;
ef416fc2 6657 }
6658
6659 /*
6660 * See if they want to limit the number of jobs reported...
6661 */
6662
f16ea703 6663 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
aaf19ab0
MS
6664 {
6665 if (job_ids)
6666 {
6667 send_ipp_status(con, IPP_CONFLICT,
6668 _("The %s attribute cannot be provided with job-ids."),
6669 "limit");
6670 return;
6671 }
6672
ef416fc2 6673 limit = attr->values[0].integer;
aaf19ab0 6674 }
ef416fc2 6675
f16ea703
MS
6676 if ((attr = ippFindAttribute(con->request, "first-index", IPP_TAG_INTEGER)) != NULL)
6677 {
6678 if (job_ids)
6679 {
6680 send_ipp_status(con, IPP_CONFLICT,
6681 _("The %s attribute cannot be provided with job-ids."),
6682 "first-index");
6683 return;
6684 }
6685
6686 first_index = attr->values[0].integer;
6687 }
6688 else if ((attr = ippFindAttribute(con->request, "first-job-id", IPP_TAG_INTEGER)) != NULL)
aaf19ab0
MS
6689 {
6690 if (job_ids)
6691 {
6692 send_ipp_status(con, IPP_CONFLICT,
6693 _("The %s attribute cannot be provided with job-ids."),
6694 "first-job-id");
6695 return;
6696 }
6697
ef416fc2 6698 first_job_id = attr->values[0].integer;
aaf19ab0 6699 }
ef416fc2 6700
6701 /*
6702 * See if we only want to see jobs for a specific user...
6703 */
6704
f16ea703 6705 if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL && job_ids)
aaf19ab0
MS
6706 {
6707 send_ipp_status(con, IPP_CONFLICT,
6708 _("The %s attribute cannot be provided with job-ids."),
6709 "my-jobs");
6710 return;
6711 }
6712 else if (attr && attr->values[0].boolean)
e00b005a 6713 strlcpy(username, get_username(con), sizeof(username));
ef416fc2 6714 else
6715 username[0] = '\0';
6716
db8b865d 6717 ra = create_requested_array(con->request);
f16ea703
MS
6718 for (job_attr = (char *)cupsArrayFirst(ra); job_attr; job_attr = (char *)cupsArrayNext(ra))
6719 if (strcmp(job_attr, "job-id") &&
6720 strcmp(job_attr, "job-k-octets") &&
6721 strcmp(job_attr, "job-media-progress") &&
6722 strcmp(job_attr, "job-more-info") &&
6723 strcmp(job_attr, "job-name") &&
6724 strcmp(job_attr, "job-originating-user-name") &&
6725 strcmp(job_attr, "job-preserved") &&
6726 strcmp(job_attr, "job-printer-up-time") &&
6727 strcmp(job_attr, "job-printer-uri") &&
6728 strcmp(job_attr, "job-state") &&
6729 strcmp(job_attr, "job-state-reasons") &&
6730 strcmp(job_attr, "job-uri") &&
6731 strcmp(job_attr, "time-at-completed") &&
6732 strcmp(job_attr, "time-at-creation") &&
6733 strcmp(job_attr, "number-of-documents"))
6734 {
6735 need_load_job = 1;
6736 break;
6737 }
6738
6739 if (need_load_job && (limit == 0 || limit > 500) && (list == Jobs || delete_list))
6740 {
6741 /*
6742 * Limit expensive Get-Jobs for job history to 500 jobs...
6743 */
6744
6745 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "limit", 500);
6746
6747 if (limit)
6748 ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER, "limit", limit);
6749
6750 limit = 500;
6751
6752 cupsdLogClient(con, CUPSD_LOG_INFO, "Limiting Get-Jobs response to %d jobs.", limit);
6753 }
ef416fc2 6754
6755 /*
6756 * OK, build a list of jobs for this printer...
6757 */
6758
aaf19ab0 6759 if (job_ids)
ef416fc2 6760 {
aaf19ab0 6761 int i; /* Looping var */
ef416fc2 6762
aaf19ab0
MS
6763 for (i = 0; i < job_ids->num_values; i ++)
6764 {
4220952d 6765 if (!cupsdFindJob(job_ids->values[i].integer))
aaf19ab0
MS
6766 break;
6767 }
ef416fc2 6768
aaf19ab0
MS
6769 if (i < job_ids->num_values)
6770 {
84315f46 6771 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
aaf19ab0
MS
6772 job_ids->values[i].integer);
6773 return;
6774 }
0a682745 6775
aaf19ab0
MS
6776 for (i = 0; i < job_ids->num_values; i ++)
6777 {
6778 job = cupsdFindJob(job_ids->values[i].integer);
0a682745 6779
f16ea703 6780 if (need_load_job && !job->attrs)
aaf19ab0 6781 {
f16ea703
MS
6782 cupsdLoadJob(job);
6783
6784 if (!job->attrs)
6785 {
6786 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d", job->id);
6787 continue;
6788 }
aaf19ab0 6789 }
ef416fc2 6790
aaf19ab0
MS
6791 if (i > 0)
6792 ippAddSeparator(con->response);
bd7854cb 6793
10d09e33
MS
6794 exclude = cupsdGetPrivateAttrs(job->printer ?
6795 job->printer->op_policy_ptr :
6796 policy, con, job->printer,
6797 job->username);
6798
6799 copy_job_attrs(con, job, ra, exclude);
d1c13e16 6800 }
aaf19ab0
MS
6801 }
6802 else
6803 {
9e6d7a0f
MS
6804 if (first_index > 1)
6805 job = (cupsd_job_t *)cupsArrayIndex(list, first_index - 1);
6806 else
6807 job = (cupsd_job_t *)cupsArrayFirst(list);
6808
6809 for (count = 0; (limit <= 0 || count < limit) && job; job = (cupsd_job_t *)cupsArrayNext(list))
aaf19ab0
MS
6810 {
6811 /*
6812 * Filter out jobs that don't match...
6813 */
bd7854cb 6814
aaf19ab0
MS
6815 cupsdLogMessage(CUPSD_LOG_DEBUG2,
6816 "get_jobs: job->id=%d, dest=\"%s\", username=\"%s\", "
6817 "state_value=%d, attrs=%p", job->id, job->dest,
6818 job->username, job->state_value, job->attrs);
7594b224 6819
aaf19ab0
MS
6820 if (!job->dest || !job->username)
6821 cupsdLoadJob(job);
fa73b229 6822
aaf19ab0
MS
6823 if (!job->dest || !job->username)
6824 continue;
ef416fc2 6825
aaf19ab0
MS
6826 if ((dest && strcmp(job->dest, dest)) &&
6827 (!job->printer || !dest || strcmp(job->printer->name, dest)))
6828 continue;
6829 if ((job->dtype & dmask) != dtype &&
6830 (!job->printer || (job->printer->type & dmask) != dtype))
6831 continue;
ef416fc2 6832
aaf19ab0
MS
6833 if ((job_comparison < 0 && job->state_value > job_state) ||
6834 (job_comparison == 0 && job->state_value != job_state) ||
6835 (job_comparison > 0 && job->state_value < job_state))
6836 continue;
6837
6838 if (job->id < first_job_id)
6839 continue;
6840
f16ea703 6841 if (need_load_job && !job->attrs)
aaf19ab0 6842 {
f16ea703
MS
6843 cupsdLoadJob(job);
6844
6845 if (!job->attrs)
6846 {
6847 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d", job->id);
6848 continue;
6849 }
aaf19ab0
MS
6850 }
6851
88f9aafc 6852 if (username[0] && _cups_strcasecmp(username, job->username))
aaf19ab0
MS
6853 continue;
6854
6855 if (count > 0)
6856 ippAddSeparator(con->response);
6857
6858 count ++;
6859
10d09e33
MS
6860 exclude = cupsdGetPrivateAttrs(job->printer ?
6861 job->printer->op_policy_ptr :
6862 policy, con, job->printer,
6863 job->username);
6864
6865 copy_job_attrs(con, job, ra, exclude);
aaf19ab0
MS
6866 }
6867
6868 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count=%d", count);
6869 }
d1c13e16 6870
fa73b229 6871 cupsArrayDelete(ra);
ef416fc2 6872
eec1fbc3
MS
6873 if (delete_list)
6874 cupsArrayDelete(list);
6875
fa73b229 6876 con->response->request.status.status_code = IPP_OK;
ef416fc2 6877}
6878
6879
6880/*
6881 * 'get_notifications()' - Get events for a subscription.
6882 */
6883
6884static void
bd7854cb 6885get_notifications(cupsd_client_t *con) /* I - Client connection */
ef416fc2 6886{
bd7854cb 6887 int i, j; /* Looping vars */
6888 http_status_t status; /* Policy status */
6889 cupsd_subscription_t *sub; /* Subscription */
6890 ipp_attribute_t *ids, /* notify-subscription-ids */
6891 *sequences; /* notify-sequence-numbers */
6892 int min_seq; /* Minimum sequence number */
6893 int interval; /* Poll interval */
6894
6895
f7deaa1a 6896 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])",
996acce8 6897 con, con->number);
bd7854cb 6898
6899 /*
6900 * Get subscription attributes...
6901 */
6902
6903 ids = ippFindAttribute(con->request, "notify-subscription-ids",
6904 IPP_TAG_INTEGER);
6905 sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
6906 IPP_TAG_INTEGER);
6907
6908 if (!ids)
6909 {
6910 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 6911 _("Missing notify-subscription-ids attribute."));
bd7854cb 6912 return;
6913 }
6914
6915 /*
6916 * Are the subscription IDs valid?
6917 */
6918
6919 for (i = 0, interval = 60; i < ids->num_values; i ++)
6920 {
6921 if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
6922 {
6923 /*
6924 * Bad subscription ID...
6925 */
6926
84315f46 6927 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
bd7854cb 6928 ids->values[i].integer);
6929 return;
6930 }
6931
6932 /*
6933 * Check policy...
6934 */
6935
6936 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
6937 DefaultPolicyPtr,
6938 con, sub->owner)) != HTTP_OK)
6939 {
f899b121 6940 send_http_error(con, status, sub->dest);
bd7854cb 6941 return;
6942 }
6943
6944 /*
6945 * Check the subscription type and update the interval accordingly.
6946 */
6947
6948 if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
6949 interval > 10)
6950 interval = 10;
6951 else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
6952 interval = 0;
6953 else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
6954 interval > 30)
6955 interval = 30;
6956 }
6957
6958 /*
6959 * Tell the client to poll again in N seconds...
6960 */
6961
6962 if (interval > 0)
6963 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
6964 "notify-get-interval", interval);
6965
6966 ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
6967 "printer-up-time", time(NULL));
6968
6969 /*
6970 * Copy the subscription event attributes to the response.
6971 */
6972
6973 con->response->request.status.status_code =
6974 interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
6975
6976 for (i = 0; i < ids->num_values; i ++)
6977 {
6978 /*
6979 * Get the subscription and sequence number...
6980 */
6981
6982 sub = cupsdFindSubscription(ids->values[i].integer);
6983
6984 if (sequences && i < sequences->num_values)
6985 min_seq = sequences->values[i].integer;
6986 else
6987 min_seq = 1;
6988
6989 /*
6990 * If we don't have any new events, nothing to do here...
6991 */
6992
10d09e33 6993 if (min_seq > (sub->first_event_id + cupsArrayCount(sub->events)))
bd7854cb 6994 continue;
6995
6996 /*
6997 * Otherwise copy all of the new events...
6998 */
6999
7000 if (sub->first_event_id > min_seq)
7001 j = 0;
7002 else
7003 j = min_seq - sub->first_event_id;
7004
10d09e33 7005 for (; j < cupsArrayCount(sub->events); j ++)
b423cd4c 7006 {
7007 ippAddSeparator(con->response);
ef416fc2 7008
10d09e33
MS
7009 copy_attrs(con->response,
7010 ((cupsd_event_t *)cupsArrayIndex(sub->events, j))->attrs, NULL,
7011 IPP_TAG_EVENT_NOTIFICATION, 0, NULL);
b423cd4c 7012 }
7013 }
7014}
ef416fc2 7015
ef416fc2 7016
b94498cf 7017/*
7018 * 'get_ppd()' - Get a named PPD from the local system.
7019 */
7020
7021static void
7022get_ppd(cupsd_client_t *con, /* I - Client connection */
7023 ipp_attribute_t *uri) /* I - Printer URI or PPD name */
7024{
7025 http_status_t status; /* Policy status */
7026 cupsd_printer_t *dest; /* Destination */
7027 cups_ptype_t dtype; /* Destination type */
7028
7029
7030 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", con,
996acce8 7031 con->number, uri, uri->name, uri->values[0].string.text);
b94498cf 7032
e67e2f9e 7033 if (!strcmp(ippGetName(uri), "ppd-name"))
b94498cf 7034 {
7035 /*
7036 * Return a PPD file from cups-driverd...
7037 */
7038
e67e2f9e
MS
7039 const char *ppd_name = ippGetString(uri, 0, NULL);
7040 /* ppd-name value */
7041 char command[1024], /* cups-driverd command */
7042 options[1024], /* Options to pass to command */
7043 oppd_name[1024]; /* Escaped ppd-name */
b94498cf 7044
7045 /*
7046 * Check policy...
7047 */
7048
7049 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7050 {
7051 send_http_error(con, status, NULL);
7052 return;
7053 }
7054
e67e2f9e
MS
7055 /*
7056 * Check ppd-name value...
7057 */
7058
7059 if (strstr(ppd_name, "../"))
7060 {
7061 send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Invalid ppd-name value."));
7062 return;
7063 }
7064
b94498cf 7065 /*
7066 * Run cups-driverd command with the given options...
7067 */
7068
7069 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
e67e2f9e
MS
7070 url_encode_string(ppd_name, oppd_name, sizeof(oppd_name));
7071 snprintf(options, sizeof(options), "get+%d+%s", ippGetRequestId(con->request), oppd_name);
b94498cf 7072
7073 if (cupsdSendCommand(con, command, options, 0))
7074 {
7075 /*
7076 * Command started successfully, don't send an IPP response here...
7077 */
7078
7079 ippDelete(con->response);
7080 con->response = NULL;
7081 }
7082 else
7083 {
7084 /*
7085 * Command failed, return "internal error" so the user knows something
7086 * went wrong...
7087 */
7088
e67e2f9e 7089 send_ipp_status(con, IPP_INTERNAL_ERROR, _("cups-driverd failed to execute."));
b94498cf 7090 }
7091 }
e67e2f9e 7092 else if (!strcmp(ippGetName(uri), "printer-uri") && cupsdValidateDest(ippGetString(uri, 0, NULL), &dtype, &dest))
b94498cf 7093 {
7094 int i; /* Looping var */
7095 char filename[1024]; /* PPD filename */
7096
b94498cf 7097 /*
7098 * Check policy...
7099 */
7100
7101 if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK)
7102 {
2fb76298 7103 send_http_error(con, status, dest);
b94498cf 7104 return;
7105 }
7106
7107 /*
7108 * See if we need the PPD for a class or remote printer...
7109 */
7110
e67e2f9e 7111 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest->name);
bc44d920 7112
7113 if ((dtype & CUPS_PRINTER_REMOTE) && access(filename, 0))
b94498cf 7114 {
e67e2f9e
MS
7115 send_ipp_status(con, IPP_STATUS_CUPS_SEE_OTHER, _("See remote printer."));
7116 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, dest->uri);
b94498cf 7117 return;
7118 }
a2326b5b 7119 else if (dtype & CUPS_PRINTER_CLASS)
b94498cf 7120 {
7121 for (i = 0; i < dest->num_printers; i ++)
a2326b5b 7122 if (!(dest->printers[i]->type & CUPS_PRINTER_CLASS))
bc44d920 7123 {
e67e2f9e 7124 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest->printers[i]->name);
bc44d920 7125
7126 if (!access(filename, 0))
7127 break;
7128 }
b94498cf 7129
7130 if (i < dest->num_printers)
7131 dest = dest->printers[i];
7132 else
7133 {
e67e2f9e
MS
7134 send_ipp_status(con, IPP_STATUS_CUPS_SEE_OTHER, _("See remote printer."));
7135 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, dest->printers[0]->uri);
b94498cf 7136 return;
7137 }
7138 }
7139
7140 /*
7141 * Found the printer with the PPD file, now see if there is one...
7142 */
7143
b94498cf 7144 if ((con->file = open(filename, O_RDONLY)) < 0)
7145 {
e67e2f9e 7146 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 7147 return;
7148 }
7149
7150 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
7151
7152 con->pipe_pid = 0;
7153
e67e2f9e 7154 ippSetStatusCode(con->response, IPP_STATUS_OK);
b94498cf 7155 }
7156 else
e67e2f9e 7157 send_ipp_status(con, IPP_STATUS_ERROR_NOT_FOUND, _("The PPD file \"%s\" could not be found."), ippGetString(uri, 0, NULL));
b94498cf 7158}
7159
7160
b423cd4c 7161/*
7162 * 'get_ppds()' - Get the list of PPD files on the local system.
7163 */
ef416fc2 7164
b423cd4c 7165static void
7166get_ppds(cupsd_client_t *con) /* I - Client connection */
7167{
7168 http_status_t status; /* Policy status */
b423cd4c 7169 ipp_attribute_t *limit, /* Limit attribute */
b94498cf 7170 *device, /* ppd-device-id attribute */
7171 *language, /* ppd-natural-language attribute */
b423cd4c 7172 *make, /* ppd-make attribute */
b94498cf 7173 *model, /* ppd-make-and-model attribute */
3d8365b8 7174 *model_number, /* ppd-model-number attribute */
b94498cf 7175 *product, /* ppd-product attribute */
7176 *psversion, /* ppd-psverion attribute */
3d8365b8 7177 *type, /* ppd-type attribute */
ed6e7faf
MS
7178 *requested, /* requested-attributes attribute */
7179 *exclude, /* exclude-schemes attribute */
7180 *include; /* include-schemes attribute */
b94498cf 7181 char command[1024], /* cups-driverd command */
ed6e7faf 7182 options[4096], /* Options to pass to command */
b94498cf 7183 device_str[256],/* Escaped ppd-device-id string */
7184 language_str[256],
bc44d920 7185 /* Escaped ppd-natural-language */
b94498cf 7186 make_str[256], /* Escaped ppd-make string */
7187 model_str[256], /* Escaped ppd-make-and-model string */
3d8365b8 7188 model_number_str[256],
7189 /* ppd-model-number string */
b94498cf 7190 product_str[256],
7191 /* Escaped ppd-product string */
7192 psversion_str[256],
7193 /* Escaped ppd-psversion string */
3d8365b8 7194 type_str[256], /* Escaped ppd-type string */
ed6e7faf 7195 requested_str[256],
89d46774 7196 /* String for requested attributes */
ed6e7faf
MS
7197 exclude_str[512],
7198 /* String for excluded schemes */
7199 include_str[512];
7200 /* String for included schemes */
ef416fc2 7201
ef416fc2 7202
996acce8 7203 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->number);
ef416fc2 7204
7205 /*
b423cd4c 7206 * Check policy...
ef416fc2 7207 */
7208
b423cd4c 7209 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
ef416fc2 7210 {
f899b121 7211 send_http_error(con, status, NULL);
b423cd4c 7212 return;
ef416fc2 7213 }
ef416fc2 7214
b423cd4c 7215 /*
7216 * Run cups-driverd command with the given options...
7217 */
7218
3d8365b8 7219 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
7220 device = ippFindAttribute(con->request, "ppd-device-id", IPP_TAG_TEXT);
7221 language = ippFindAttribute(con->request, "ppd-natural-language",
7222 IPP_TAG_LANGUAGE);
7223 make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
7224 model = ippFindAttribute(con->request, "ppd-make-and-model",
7225 IPP_TAG_TEXT);
7226 model_number = ippFindAttribute(con->request, "ppd-model-number",
7227 IPP_TAG_INTEGER);
7228 product = ippFindAttribute(con->request, "ppd-product", IPP_TAG_TEXT);
7229 psversion = ippFindAttribute(con->request, "ppd-psversion", IPP_TAG_TEXT);
7230 type = ippFindAttribute(con->request, "ppd-type", IPP_TAG_KEYWORD);
7231 requested = ippFindAttribute(con->request, "requested-attributes",
7232 IPP_TAG_KEYWORD);
8b450588
MS
7233 exclude = ippFindAttribute(con->request, "exclude-schemes",
7234 IPP_TAG_NAME);
7235 include = ippFindAttribute(con->request, "include-schemes",
7236 IPP_TAG_NAME);
b423cd4c 7237
7238 if (requested)
89d46774 7239 url_encode_attr(requested, requested_str, sizeof(requested_str));
7240 else
7241 strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
ef416fc2 7242
b94498cf 7243 if (device)
7244 url_encode_attr(device, device_str, sizeof(device_str));
7245 else
7246 device_str[0] = '\0';
7247
7248 if (language)
7249 url_encode_attr(language, language_str, sizeof(language_str));
7250 else
7251 language_str[0] = '\0';
7252
89d46774 7253 if (make)
7254 url_encode_attr(make, make_str, sizeof(make_str));
b423cd4c 7255 else
89d46774 7256 make_str[0] = '\0';
fa73b229 7257
b94498cf 7258 if (model)
7259 url_encode_attr(model, model_str, sizeof(model_str));
7260 else
7261 model_str[0] = '\0';
7262
3d8365b8 7263 if (model_number)
7264 snprintf(model_number_str, sizeof(model_number_str), "ppd-model-number=%d",
7265 model_number->values[0].integer);
7266 else
7267 model_number_str[0] = '\0';
7268
b94498cf 7269 if (product)
7270 url_encode_attr(product, product_str, sizeof(product_str));
7271 else
7272 product_str[0] = '\0';
7273
7274 if (psversion)
7275 url_encode_attr(psversion, psversion_str, sizeof(psversion_str));
7276 else
7277 psversion_str[0] = '\0';
7278
3d8365b8 7279 if (type)
7280 url_encode_attr(type, type_str, sizeof(type_str));
7281 else
7282 type_str[0] = '\0';
7283
ed6e7faf
MS
7284 if (exclude)
7285 url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
7286 else
7287 exclude_str[0] = '\0';
7288
7289 if (include)
7290 url_encode_attr(include, include_str, sizeof(include_str));
7291 else
7292 include_str[0] = '\0';
7293
b423cd4c 7294 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
3d8365b8 7295 snprintf(options, sizeof(options),
ed6e7faf 7296 "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 7297 con->request->request.op.request_id,
7298 limit ? limit->values[0].integer : 0,
b94498cf 7299 requested_str,
7300 device ? "%20" : "", device_str,
7301 language ? "%20" : "", language_str,
7302 make ? "%20" : "", make_str,
7303 model ? "%20" : "", model_str,
3d8365b8 7304 model_number ? "%20" : "", model_number_str,
b94498cf 7305 product ? "%20" : "", product_str,
3d8365b8 7306 psversion ? "%20" : "", psversion_str,
ed6e7faf
MS
7307 type ? "%20" : "", type_str,
7308 exclude_str[0] ? "%20" : "", exclude_str,
7309 include_str[0] ? "%20" : "", include_str);
ef416fc2 7310
b423cd4c 7311 if (cupsdSendCommand(con, command, options, 0))
7312 {
7313 /*
7314 * Command started successfully, don't send an IPP response here...
7315 */
7316
7317 ippDelete(con->response);
7318 con->response = NULL;
7319 }
7320 else
7321 {
7322 /*
7323 * Command failed, return "internal error" so the user knows something
7324 * went wrong...
7325 */
7326
7327 send_ipp_status(con, IPP_INTERNAL_ERROR,
7328 _("cups-driverd failed to execute."));
7329 }
ef416fc2 7330}
7331
7332
7333/*
b423cd4c 7334 * 'get_printer_attrs()' - Get printer attributes.
ef416fc2 7335 */
7336
7337static void
b423cd4c 7338get_printer_attrs(cupsd_client_t *con, /* I - Client connection */
7339 ipp_attribute_t *uri) /* I - Printer URI */
ef416fc2 7340{
7341 http_status_t status; /* Policy status */
bc44d920 7342 cups_ptype_t dtype; /* Destination type (printer/class) */
b423cd4c 7343 cupsd_printer_t *printer; /* Printer/class */
ef416fc2 7344 cups_array_t *ra; /* Requested attributes array */
7345
7346
b423cd4c 7347 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
996acce8 7348 con->number, uri->values[0].string.text);
ef416fc2 7349
7350 /*
b423cd4c 7351 * Is the destination valid?
ef416fc2 7352 */
7353
f7deaa1a 7354 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 7355 {
7356 /*
b423cd4c 7357 * Bad URI...
ef416fc2 7358 */
7359
7360 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 7361 _("The printer or class does not exist."));
ef416fc2 7362 return;
7363 }
7364
7365 /*
7366 * Check policy...
7367 */
7368
b423cd4c 7369 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
ef416fc2 7370 {
f899b121 7371 send_http_error(con, status, printer);
ef416fc2 7372 return;
7373 }
7374
7375 /*
b423cd4c 7376 * Send the attributes...
ef416fc2 7377 */
7378
7379 ra = create_requested_array(con->request);
7380
b423cd4c 7381 copy_printer_attrs(con, printer, ra);
ef416fc2 7382
7383 cupsArrayDelete(ra);
7384
7385 con->response->request.status.status_code = IPP_OK;
7386}
7387
7388
c168a833
MS
7389/*
7390 * 'get_printer_supported()' - Get printer supported values.
7391 */
7392
7393static void
7394get_printer_supported(
7395 cupsd_client_t *con, /* I - Client connection */
7396 ipp_attribute_t *uri) /* I - Printer URI */
7397{
7398 http_status_t status; /* Policy status */
7399 cups_ptype_t dtype; /* Destination type (printer/class) */
7400 cupsd_printer_t *printer; /* Printer/class */
7401
7402
7403 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_supported(%p[%d], %s)", con,
996acce8 7404 con->number, uri->values[0].string.text);
c168a833
MS
7405
7406 /*
7407 * Is the destination valid?
7408 */
7409
7410 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7411 {
7412 /*
7413 * Bad URI...
7414 */
7415
7416 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 7417 _("The printer or class does not exist."));
c168a833
MS
7418 return;
7419 }
7420
7421 /*
7422 * Check policy...
7423 */
7424
7425 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7426 {
7427 send_http_error(con, status, printer);
7428 return;
7429 }
7430
7431 /*
7432 * Return a list of attributes that can be set via Set-Printer-Attributes.
7433 */
7434
9b4bd602
MS
7435 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7436 "printer-geo-location", 0);
c168a833
MS
7437 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7438 "printer-info", 0);
7439 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7440 "printer-location", 0);
9b4bd602
MS
7441 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7442 "printer-organization", 0);
7443 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7444 "printer-organizational-unit", 0);
c168a833
MS
7445
7446 con->response->request.status.status_code = IPP_OK;
7447}
7448
7449
ef416fc2 7450/*
b423cd4c 7451 * 'get_printers()' - Get a list of printers or classes.
ef416fc2 7452 */
7453
7454static void
b423cd4c 7455get_printers(cupsd_client_t *con, /* I - Client connection */
7456 int type) /* I - 0 or CUPS_PRINTER_CLASS */
ef416fc2 7457{
b423cd4c 7458 http_status_t status; /* Policy status */
7459 ipp_attribute_t *attr; /* Current attribute */
bc44d920 7460 int limit; /* Max number of printers to return */
b423cd4c 7461 int count; /* Number of printers that match */
409f5497 7462 int printer_id; /* Printer we are interested in */
b423cd4c 7463 cupsd_printer_t *printer; /* Current printer pointer */
7e86f2f6 7464 cups_ptype_t printer_type, /* printer-type attribute */
b423cd4c 7465 printer_mask; /* printer-type-mask attribute */
7466 char *location; /* Location string */
7467 const char *username; /* Current user */
7468 char *first_printer_name; /* first-printer-name attribute */
7469 cups_array_t *ra; /* Requested attributes array */
e07d4801 7470 int local; /* Local connection? */
ef416fc2 7471
ef416fc2 7472
b423cd4c 7473 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
996acce8 7474 con->number, type);
ef416fc2 7475
7476 /*
7477 * Check policy...
7478 */
7479
b423cd4c 7480 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
ef416fc2 7481 {
f899b121 7482 send_http_error(con, status, NULL);
ef416fc2 7483 return;
7484 }
7485
7486 /*
b423cd4c 7487 * Check for printers...
ef416fc2 7488 */
7489
b423cd4c 7490 if (!Printers || !cupsArrayCount(Printers))
7491 {
7492 send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
7493 return;
7494 }
7495
7496 /*
7497 * See if they want to limit the number of printers reported...
7498 */
ef416fc2 7499
fa73b229 7500 if ((attr = ippFindAttribute(con->request, "limit",
7501 IPP_TAG_INTEGER)) != NULL)
ef416fc2 7502 limit = attr->values[0].integer;
7503 else
b423cd4c 7504 limit = 10000000;
7505
7506 if ((attr = ippFindAttribute(con->request, "first-printer-name",
7507 IPP_TAG_NAME)) != NULL)
7508 first_printer_name = attr->values[0].string.text;
7509 else
7510 first_printer_name = NULL;
ef416fc2 7511
7512 /*
b423cd4c 7513 * Support filtering...
ef416fc2 7514 */
7515
409f5497
MS
7516 if ((attr = ippFindAttribute(con->request, "printer-id", IPP_TAG_INTEGER)) != NULL)
7517 {
7518 if ((printer_id = ippGetInteger(attr, 0)) <= 0)
7519 {
7520 send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Bad \"printer-id\" value %d."), printer_id);
7521 return;
7522 }
7523 }
7524 else
7525 printer_id = 0;
7526
b423cd4c 7527 if ((attr = ippFindAttribute(con->request, "printer-type",
7528 IPP_TAG_ENUM)) != NULL)
7e86f2f6 7529 printer_type = (cups_ptype_t)attr->values[0].integer;
ef416fc2 7530 else
7e86f2f6 7531 printer_type = (cups_ptype_t)0;
ef416fc2 7532
b423cd4c 7533 if ((attr = ippFindAttribute(con->request, "printer-type-mask",
7534 IPP_TAG_ENUM)) != NULL)
7e86f2f6 7535 printer_mask = (cups_ptype_t)attr->values[0].integer;
ef416fc2 7536 else
7e86f2f6 7537 printer_mask = (cups_ptype_t)0;
e00b005a 7538
e07d4801
MS
7539 local = httpAddrLocalhost(&(con->clientaddr));
7540
b423cd4c 7541 if ((attr = ippFindAttribute(con->request, "printer-location",
7542 IPP_TAG_TEXT)) != NULL)
7543 location = attr->values[0].string.text;
7544 else
7545 location = NULL;
e00b005a 7546
7547 if (con->username[0])
b423cd4c 7548 username = con->username;
e00b005a 7549 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7550 IPP_TAG_NAME)) != NULL)
b423cd4c 7551 username = attr->values[0].string.text;
e00b005a 7552 else
b423cd4c 7553 username = NULL;
ef416fc2 7554
b423cd4c 7555 ra = create_requested_array(con->request);
ef416fc2 7556
7557 /*
b423cd4c 7558 * OK, build a list of printers for this printer...
ef416fc2 7559 */
7560
b423cd4c 7561 if (first_printer_name)
ef416fc2 7562 {
b423cd4c 7563 if ((printer = cupsdFindDest(first_printer_name)) == NULL)
7564 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
ef416fc2 7565 }
7566 else
b423cd4c 7567 printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
ef416fc2 7568
b423cd4c 7569 for (count = 0;
7570 count < limit && printer;
7571 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
7572 {
e07d4801
MS
7573 if (!local && !printer->shared)
7574 continue;
7575
409f5497
MS
7576 if (printer_id && printer->printer_id != printer_id)
7577 continue;
7578
b423cd4c 7579 if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
7580 (printer->type & printer_mask) == printer_type &&
b19ccc9e 7581 (!location ||
88f9aafc 7582 (printer->location && !_cups_strcasecmp(printer->location, location))))
ef416fc2 7583 {
b423cd4c 7584 /*
7585 * If a username is specified, see if it is allowed or denied
7586 * access...
7587 */
ef416fc2 7588
10d09e33
MS
7589 if (cupsArrayCount(printer->users) && username &&
7590 !user_allowed(printer, username))
b423cd4c 7591 continue;
ef416fc2 7592
b423cd4c 7593 /*
7594 * Add the group separator as needed...
7595 */
ef416fc2 7596
b423cd4c 7597 if (count > 0)
7598 ippAddSeparator(con->response);
ef416fc2 7599
b423cd4c 7600 count ++;
ef416fc2 7601
b423cd4c 7602 /*
7603 * Send the attributes...
7604 */
ef416fc2 7605
b423cd4c 7606 copy_printer_attrs(con, printer, ra);
7607 }
ef416fc2 7608 }
7609
b423cd4c 7610 cupsArrayDelete(ra);
ef416fc2 7611
7612 con->response->request.status.status_code = IPP_OK;
7613}
7614
7615
7616/*
b423cd4c 7617 * 'get_subscription_attrs()' - Get subscription attributes.
ef416fc2 7618 */
7619
7620static void
b423cd4c 7621get_subscription_attrs(
7622 cupsd_client_t *con, /* I - Client connection */
7623 int sub_id) /* I - Subscription ID */
ef416fc2 7624{
b423cd4c 7625 http_status_t status; /* Policy status */
7626 cupsd_subscription_t *sub; /* Subscription */
10d09e33
MS
7627 cupsd_policy_t *policy; /* Current security policy */
7628 cups_array_t *ra, /* Requested attributes array */
7629 *exclude; /* Private attributes array */
ef416fc2 7630
7631
b423cd4c 7632 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7633 "get_subscription_attrs(con=%p[%d], sub_id=%d)",
996acce8 7634 con, con->number, sub_id);
ef416fc2 7635
79d3cd17
MS
7636 /*
7637 * Expire subscriptions as needed...
7638 */
7639
7640 cupsdExpireSubscriptions(NULL, NULL);
7641
fa73b229 7642 /*
b423cd4c 7643 * Is the subscription ID valid?
fa73b229 7644 */
7645
b423cd4c 7646 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
fa73b229 7647 {
7648 /*
b423cd4c 7649 * Bad subscription ID...
fa73b229 7650 */
7651
84315f46
MS
7652 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
7653 sub_id);
fa73b229 7654 return;
7655 }
7656
7657 /*
7658 * Check policy...
7659 */
7660
10d09e33
MS
7661 if (sub->dest)
7662 policy = sub->dest->op_policy_ptr;
7663 else
7664 policy = DefaultPolicyPtr;
7665
7666 if ((status = cupsdCheckPolicy(policy, con, sub->owner)) != HTTP_OK)
fa73b229 7667 {
f899b121 7668 send_http_error(con, status, sub->dest);
fa73b229 7669 return;
7670 }
7671
10d09e33
MS
7672 exclude = cupsdGetPrivateAttrs(policy, con, sub->dest, sub->owner);
7673
ef416fc2 7674 /*
b423cd4c 7675 * Copy the subscription attributes to the response using the
7676 * requested-attributes attribute that may be provided by the client.
ef416fc2 7677 */
7678
b423cd4c 7679 ra = create_requested_array(con->request);
fa73b229 7680
10d09e33 7681 copy_subscription_attrs(con, sub, ra, exclude);
ef416fc2 7682
b423cd4c 7683 cupsArrayDelete(ra);
fa73b229 7684
b423cd4c 7685 con->response->request.status.status_code = IPP_OK;
7686}
fa73b229 7687
fa73b229 7688
b423cd4c 7689/*
7690 * 'get_subscriptions()' - Get subscriptions.
7691 */
ef416fc2 7692
b423cd4c 7693static void
7694get_subscriptions(cupsd_client_t *con, /* I - Client connection */
7695 ipp_attribute_t *uri) /* I - Printer/job URI */
7696{
7697 http_status_t status; /* Policy status */
7698 int count; /* Number of subscriptions */
7699 int limit; /* Limit */
7700 cupsd_subscription_t *sub; /* Subscription */
7701 cups_array_t *ra; /* Requested attributes array */
7702 ipp_attribute_t *attr; /* Attribute */
bc44d920 7703 cups_ptype_t dtype; /* Destination type (printer/class) */
f7deaa1a 7704 char scheme[HTTP_MAX_URI],
7705 /* Scheme portion of URI */
b423cd4c 7706 username[HTTP_MAX_URI],
7707 /* Username portion of URI */
7708 host[HTTP_MAX_URI],
7709 /* Host portion of URI */
7710 resource[HTTP_MAX_URI];
7711 /* Resource portion of URI */
7712 int port; /* Port portion of URI */
7713 cupsd_job_t *job; /* Job pointer */
7714 cupsd_printer_t *printer; /* Printer */
10d09e33
MS
7715 cupsd_policy_t *policy; /* Policy */
7716 cups_array_t *exclude; /* Private attributes array */
fa73b229 7717
fa73b229 7718
b423cd4c 7719 cupsdLogMessage(CUPSD_LOG_DEBUG2,
7720 "get_subscriptions(con=%p[%d], uri=%s)",
996acce8 7721 con, con->number, uri->values[0].string.text);
b423cd4c 7722
7723 /*
7724 * Is the destination valid?
7725 */
7726
f7deaa1a 7727 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7728 sizeof(scheme), username, sizeof(username), host,
b423cd4c 7729 sizeof(host), &port, resource, sizeof(resource));
7730
7731 if (!strcmp(resource, "/") ||
7732 (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
7733 (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
7734 (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
7735 {
7736 printer = NULL;
7737 job = NULL;
ef416fc2 7738 }
b423cd4c 7739 else if (!strncmp(resource, "/jobs/", 6) && resource[6])
ef416fc2 7740 {
b423cd4c 7741 printer = NULL;
7742 job = cupsdFindJob(atoi(resource + 6));
ef416fc2 7743
b423cd4c 7744 if (!job)
ef416fc2 7745 {
84315f46
MS
7746 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
7747 atoi(resource + 6));
ef416fc2 7748 return;
7749 }
b423cd4c 7750 }
f7deaa1a 7751 else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
b423cd4c 7752 {
fa73b229 7753 /*
b423cd4c 7754 * Bad URI...
fa73b229 7755 */
7756
b423cd4c 7757 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 7758 _("The printer or class does not exist."));
b423cd4c 7759 return;
7760 }
7761 else if ((attr = ippFindAttribute(con->request, "notify-job-id",
7762 IPP_TAG_INTEGER)) != NULL)
7763 {
7764 job = cupsdFindJob(attr->values[0].integer);
ef416fc2 7765
b423cd4c 7766 if (!job)
fa73b229 7767 {
84315f46 7768 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
b423cd4c 7769 attr->values[0].integer);
fa73b229 7770 return;
7771 }
ef416fc2 7772 }
b423cd4c 7773 else
7774 job = NULL;
ef416fc2 7775
7776 /*
b423cd4c 7777 * Check policy...
ef416fc2 7778 */
7779
10d09e33
MS
7780 if (printer)
7781 policy = printer->op_policy_ptr;
7782 else
7783 policy = DefaultPolicyPtr;
7784
7785 if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
ef416fc2 7786 {
f899b121 7787 send_http_error(con, status, printer);
b423cd4c 7788 return;
7789 }
ef416fc2 7790
79d3cd17
MS
7791 /*
7792 * Expire subscriptions as needed...
7793 */
7794
7795 cupsdExpireSubscriptions(NULL, NULL);
7796
b423cd4c 7797 /*
7798 * Copy the subscription attributes to the response using the
7799 * requested-attributes attribute that may be provided by the client.
7800 */
ef416fc2 7801
b423cd4c 7802 ra = create_requested_array(con->request);
ef416fc2 7803
b423cd4c 7804 if ((attr = ippFindAttribute(con->request, "limit",
7805 IPP_TAG_INTEGER)) != NULL)
7806 limit = attr->values[0].integer;
7807 else
7808 limit = 0;
ef416fc2 7809
b423cd4c 7810 /*
7811 * See if we only want to see subscriptions for a specific user...
7812 */
ef416fc2 7813
b423cd4c 7814 if ((attr = ippFindAttribute(con->request, "my-subscriptions",
7815 IPP_TAG_BOOLEAN)) != NULL &&
7816 attr->values[0].boolean)
7817 strlcpy(username, get_username(con), sizeof(username));
fa73b229 7818 else
b423cd4c 7819 username[0] = '\0';
ef416fc2 7820
b423cd4c 7821 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
7822 sub;
7823 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
7824 if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
88f9aafc 7825 (!username[0] || !_cups_strcasecmp(username, sub->owner)))
fa73b229 7826 {
b423cd4c 7827 ippAddSeparator(con->response);
10d09e33
MS
7828
7829 exclude = cupsdGetPrivateAttrs(sub->dest ? sub->dest->op_policy_ptr :
7830 policy, con, sub->dest,
7831 sub->owner);
7832
7833 copy_subscription_attrs(con, sub, ra, exclude);
ef416fc2 7834
b423cd4c 7835 count ++;
7836 if (limit && count >= limit)
7837 break;
7838 }
ef416fc2 7839
b423cd4c 7840 cupsArrayDelete(ra);
fa73b229 7841
b423cd4c 7842 if (count)
7843 con->response->request.status.status_code = IPP_OK;
7844 else
7845 send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
7846}
ef416fc2 7847
ef416fc2 7848
b423cd4c 7849/*
7850 * 'get_username()' - Get the username associated with a request.
7851 */
ef416fc2 7852
b423cd4c 7853static const char * /* O - Username */
7854get_username(cupsd_client_t *con) /* I - Connection */
7855{
7856 ipp_attribute_t *attr; /* Attribute */
ef416fc2 7857
ef416fc2 7858
b423cd4c 7859 if (con->username[0])
7860 return (con->username);
7861 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7862 IPP_TAG_NAME)) != NULL)
7863 return (attr->values[0].string.text);
7864 else
7865 return ("anonymous");
ef416fc2 7866}
7867
7868
7869/*
b423cd4c 7870 * 'hold_job()' - Hold a print job.
ef416fc2 7871 */
7872
b423cd4c 7873static void
7874hold_job(cupsd_client_t *con, /* I - Client connection */
7875 ipp_attribute_t *uri) /* I - Job or Printer URI */
ef416fc2 7876{
b9faaae1
MS
7877 ipp_attribute_t *attr; /* Current job-hold-until */
7878 const char *when; /* New value */
b423cd4c 7879 int jobid; /* Job ID */
61cf44e2 7880 char scheme[HTTP_MAX_URI], /* Method portion of URI */
b423cd4c 7881 username[HTTP_MAX_URI], /* Username portion of URI */
7882 host[HTTP_MAX_URI], /* Host portion of URI */
7883 resource[HTTP_MAX_URI]; /* Resource portion of URI */
7884 int port; /* Port portion of URI */
7885 cupsd_job_t *job; /* Job information */
7886
ef416fc2 7887
996acce8 7888 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->number,
b423cd4c 7889 uri->values[0].string.text);
ef416fc2 7890
7891 /*
b423cd4c 7892 * See if we have a job URI or a printer URI...
ef416fc2 7893 */
7894
b423cd4c 7895 if (!strcmp(uri->name, "printer-uri"))
7896 {
7897 /*
7898 * Got a printer URI; see if we also have a job-id attribute...
7899 */
ef416fc2 7900
b423cd4c 7901 if ((attr = ippFindAttribute(con->request, "job-id",
7902 IPP_TAG_INTEGER)) == NULL)
7903 {
7904 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 7905 _("Got a printer-uri attribute but no job-id."));
b423cd4c 7906 return;
7907 }
ef416fc2 7908
b423cd4c 7909 jobid = attr->values[0].integer;
7910 }
ef416fc2 7911 else
ef416fc2 7912 {
b423cd4c 7913 /*
7914 * Got a job URI; parse it to get the job ID...
7915 */
ef416fc2 7916
61cf44e2
MS
7917 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7918 sizeof(scheme), username, sizeof(username), host,
b423cd4c 7919 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 7920
b423cd4c 7921 if (strncmp(resource, "/jobs/", 6))
7922 {
7923 /*
7924 * Not a valid URI!
7925 */
ef416fc2 7926
b423cd4c 7927 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 7928 _("Bad job-uri \"%s\"."),
b423cd4c 7929 uri->values[0].string.text);
7930 return;
7931 }
ef416fc2 7932
b423cd4c 7933 jobid = atoi(resource + 6);
7934 }
ef416fc2 7935
ef416fc2 7936 /*
b423cd4c 7937 * See if the job exists...
ef416fc2 7938 */
7939
b423cd4c 7940 if ((job = cupsdFindJob(jobid)) == NULL)
7941 {
7942 /*
7943 * Nope - return a "not found" error...
7944 */
7945
84315f46 7946 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
b423cd4c 7947 return;
7948 }
ef416fc2 7949
7950 /*
b423cd4c 7951 * See if the job is owned by the requesting user...
ef416fc2 7952 */
7953
b423cd4c 7954 if (!validate_user(job, con, job->username, username, sizeof(username)))
7955 {
b0f6947b
MS
7956 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
7957 cupsdFindDest(job->dest));
b423cd4c 7958 return;
7959 }
ef416fc2 7960
f99f3698
MS
7961 /*
7962 * See if the job is in a state that allows holding...
7963 */
7964
7965 if (job->state_value > IPP_JOB_STOPPED)
7966 {
7967 /*
7968 * Return a "not-possible" error...
7969 */
7970
7971 send_ipp_status(con, IPP_NOT_POSSIBLE,
7972 _("Job #%d is finished and cannot be altered."),
7973 job->id);
7974 return;
7975 }
7976
ef416fc2 7977 /*
b423cd4c 7978 * Hold the job and return...
ef416fc2 7979 */
7980
4cadd620 7981 if ((attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
b423cd4c 7982 {
4cadd620
MS
7983 if ((ippGetValueTag(attr) != IPP_TAG_KEYWORD && ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG) || ippGetCount(attr) != 1 || !ippValidateAttribute(attr))
7984 {
7985 send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Unsupported 'job-hold-until' value."));
7986 ippCopyAttribute(con->response, attr, 0);
7987 return;
7988 }
7989
7990 when = ippGetString(attr, 0, NULL);
d09495fa 7991
01ce6322 7992 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
b9faaae1 7993 "Job job-hold-until value changed by user.");
b423cd4c 7994 }
b9faaae1
MS
7995 else
7996 when = "indefinite";
ef416fc2 7997
b9faaae1
MS
7998 cupsdSetJobHoldUntil(job, when, 1);
7999 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, "Job held by \"%s\".",
8000 username);
b423cd4c 8001
8002 con->response->request.status.status_code = IPP_OK;
ef416fc2 8003}
8004
8005
61cf44e2
MS
8006/*
8007 * 'hold_new_jobs()' - Hold pending/new jobs on a printer or class.
8008 */
8009
8010static void
8011hold_new_jobs(cupsd_client_t *con, /* I - Connection */
8012 ipp_attribute_t *uri) /* I - Printer URI */
8013{
8014 http_status_t status; /* Policy status */
8015 cups_ptype_t dtype; /* Destination type (printer/class) */
8016 cupsd_printer_t *printer; /* Printer data */
8017
8018
8019 cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_new_jobs(%p[%d], %s)", con,
996acce8 8020 con->number, uri->values[0].string.text);
61cf44e2
MS
8021
8022 /*
8023 * Is the destination valid?
8024 */
8025
8026 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8027 {
8028 /*
8029 * Bad URI...
8030 */
8031
8032 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 8033 _("The printer or class does not exist."));
61cf44e2
MS
8034 return;
8035 }
8036
8037 /*
8038 * Check policy...
8039 */
8040
8041 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8042 {
8043 send_http_error(con, status, printer);
8044 return;
8045 }
8046
8047 /*
8048 * Hold pending/new jobs sent to the printer...
8049 */
8050
8051 printer->holding_new_jobs = 1;
8052
8053 cupsdSetPrinterReasons(printer, "+hold-new-jobs");
61cf44e2
MS
8054
8055 if (dtype & CUPS_PRINTER_CLASS)
8056 cupsdLogMessage(CUPSD_LOG_INFO,
8057 "Class \"%s\" now holding pending/new jobs (\"%s\").",
8058 printer->name, get_username(con));
8059 else
8060 cupsdLogMessage(CUPSD_LOG_INFO,
8061 "Printer \"%s\" now holding pending/new jobs (\"%s\").",
8062 printer->name, get_username(con));
8063
8064 /*
8065 * Everything was ok, so return OK status...
8066 */
8067
8068 con->response->request.status.status_code = IPP_OK;
8069}
8070
8071
ef416fc2 8072/*
b423cd4c 8073 * 'move_job()' - Move a job to a new destination.
ef416fc2 8074 */
8075
8076static void
b423cd4c 8077move_job(cupsd_client_t *con, /* I - Client connection */
8078 ipp_attribute_t *uri) /* I - Job URI */
ef416fc2 8079{
8080 http_status_t status; /* Policy status */
8081 ipp_attribute_t *attr; /* Current attribute */
b423cd4c 8082 int jobid; /* Job ID */
ef416fc2 8083 cupsd_job_t *job; /* Current job */
d09495fa 8084 const char *src; /* Source printer/class */
b423cd4c 8085 cups_ptype_t stype, /* Source type (printer or class) */
bc44d920 8086 dtype; /* Destination type (printer/class) */
f7deaa1a 8087 char scheme[HTTP_MAX_URI], /* Scheme portion of URI */
ef416fc2 8088 username[HTTP_MAX_URI], /* Username portion of URI */
8089 host[HTTP_MAX_URI], /* Host portion of URI */
b423cd4c 8090 resource[HTTP_MAX_URI]; /* Resource portion of URI */
ef416fc2 8091 int port; /* Port portion of URI */
b423cd4c 8092 cupsd_printer_t *sprinter, /* Source printer */
8093 *dprinter; /* Destination printer */
ef416fc2 8094
8095
996acce8 8096 cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->number,
ef416fc2 8097 uri->values[0].string.text);
8098
8099 /*
b423cd4c 8100 * Get the new printer or class...
ef416fc2 8101 */
8102
b423cd4c 8103 if ((attr = ippFindAttribute(con->request, "job-printer-uri",
8104 IPP_TAG_URI)) == NULL)
ef416fc2 8105 {
b423cd4c 8106 /*
8107 * Need job-printer-uri...
8108 */
ef416fc2 8109
b423cd4c 8110 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 8111 _("job-printer-uri attribute missing."));
b423cd4c 8112 return;
ef416fc2 8113 }
f7faf1f5 8114
f7deaa1a 8115 if (!cupsdValidateDest(attr->values[0].string.text, &dtype, &dprinter))
ef416fc2 8116 {
b423cd4c 8117 /*
8118 * Bad URI...
8119 */
ef416fc2 8120
b423cd4c 8121 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 8122 _("The printer or class does not exist."));
b423cd4c 8123 return;
ef416fc2 8124 }
8125
ef416fc2 8126 /*
b423cd4c 8127 * See if we have a job URI or a printer URI...
ef416fc2 8128 */
8129
f7deaa1a 8130 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8131 sizeof(scheme), username, sizeof(username), host,
b423cd4c 8132 sizeof(host), &port, resource, sizeof(resource));
8133
8134 if (!strcmp(uri->name, "printer-uri"))
ef416fc2 8135 {
8136 /*
b423cd4c 8137 * Got a printer URI; see if we also have a job-id attribute...
ef416fc2 8138 */
8139
b423cd4c 8140 if ((attr = ippFindAttribute(con->request, "job-id",
8141 IPP_TAG_INTEGER)) == NULL)
ef416fc2 8142 {
b423cd4c 8143 /*
8144 * Move all jobs...
8145 */
8146
f7deaa1a 8147 if ((src = cupsdValidateDest(uri->values[0].string.text, &stype,
8148 &sprinter)) == NULL)
b423cd4c 8149 {
8150 /*
8151 * Bad URI...
8152 */
8153
8154 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 8155 _("The printer or class does not exist."));
b423cd4c 8156 return;
8157 }
8158
8159 job = NULL;
8160 }
8161 else
8162 {
8163 /*
8164 * Otherwise, just move a single job...
8165 */
8166
8167 if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
8168 {
8169 /*
8170 * Nope - return a "not found" error...
8171 */
8172
8173 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 8174 _("Job #%d does not exist."), attr->values[0].integer);
b423cd4c 8175 return;
8176 }
8177 else
8178 {
8179 /*
8180 * Job found, initialize source pointers...
8181 */
8182
8183 src = NULL;
8184 sprinter = NULL;
8185 }
ef416fc2 8186 }
8187 }
8188 else
8189 {
8190 /*
b423cd4c 8191 * Got a job URI; parse it to get the job ID...
ef416fc2 8192 */
8193
b423cd4c 8194 if (strncmp(resource, "/jobs/", 6))
8195 {
8196 /*
8197 * Not a valid URI!
8198 */
8199
84315f46 8200 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
b423cd4c 8201 uri->values[0].string.text);
8202 return;
8203 }
ef416fc2 8204
ef416fc2 8205 /*
b423cd4c 8206 * See if the job exists...
ef416fc2 8207 */
8208
b423cd4c 8209 jobid = atoi(resource + 6);
ef416fc2 8210
b423cd4c 8211 if ((job = cupsdFindJob(jobid)) == NULL)
ef416fc2 8212 {
8213 /*
b423cd4c 8214 * Nope - return a "not found" error...
ef416fc2 8215 */
8216
84315f46 8217 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
b423cd4c 8218 return;
ef416fc2 8219 }
8220 else
b423cd4c 8221 {
8222 /*
8223 * Job found, initialize source pointers...
8224 */
ef416fc2 8225
b423cd4c 8226 src = NULL;
8227 sprinter = NULL;
8228 }
ef416fc2 8229 }
8230
ac884b6a
MS
8231 /*
8232 * Check the policy of the destination printer...
8233 */
8234
8235 if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con,
8236 job ? job->username : NULL)) != HTTP_OK)
8237 {
8238 send_http_error(con, status, dprinter);
8239 return;
8240 }
8241
ef416fc2 8242 /*
b423cd4c 8243 * Now move the job or jobs...
ef416fc2 8244 */
8245
b423cd4c 8246 if (job)
8247 {
8248 /*
8249 * See if the job has been completed...
8250 */
8251
8252 if (job->state_value > IPP_JOB_STOPPED)
8253 {
8254 /*
8255 * Return a "not-possible" error...
8256 */
8257
8258 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 8259 _("Job #%d is finished and cannot be altered."),
b423cd4c 8260 job->id);
8261 return;
8262 }
ef416fc2 8263
b423cd4c 8264 /*
8265 * See if the job is owned by the requesting user...
8266 */
ef416fc2 8267
b423cd4c 8268 if (!validate_user(job, con, job->username, username, sizeof(username)))
8269 {
b0f6947b
MS
8270 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8271 cupsdFindDest(job->dest));
b423cd4c 8272 return;
8273 }
ef416fc2 8274
ef416fc2 8275 /*
b423cd4c 8276 * Move the job to a different printer or class...
ef416fc2 8277 */
8278
e53920b9 8279 cupsdMoveJob(job, dprinter);
ef416fc2 8280 }
b423cd4c 8281 else
8282 {
8283 /*
8284 * Got the source printer, now look through the jobs...
8285 */
ef416fc2 8286
b423cd4c 8287 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
8288 job;
8289 job = (cupsd_job_t *)cupsArrayNext(Jobs))
8290 {
8291 /*
8292 * See if the job is pointing at the source printer or has not been
8293 * completed...
8294 */
ef416fc2 8295
88f9aafc 8296 if (_cups_strcasecmp(job->dest, src) ||
b423cd4c 8297 job->state_value > IPP_JOB_STOPPED)
8298 continue;
ef416fc2 8299
b423cd4c 8300 /*
8301 * See if the job can be moved by the requesting user...
8302 */
ef416fc2 8303
b423cd4c 8304 if (!validate_user(job, con, job->username, username, sizeof(username)))
8305 continue;
ef416fc2 8306
b423cd4c 8307 /*
8308 * Move the job to a different printer or class...
8309 */
ef416fc2 8310
e53920b9 8311 cupsdMoveJob(job, dprinter);
b423cd4c 8312 }
ef416fc2 8313 }
8314
8315 /*
b423cd4c 8316 * Start jobs if possible...
ef416fc2 8317 */
8318
b423cd4c 8319 cupsdCheckJobs();
ef416fc2 8320
8321 /*
b423cd4c 8322 * Return with "everything is OK" status...
ef416fc2 8323 */
8324
b423cd4c 8325 con->response->request.status.status_code = IPP_OK;
8326}
ef416fc2 8327
ef416fc2 8328
b423cd4c 8329/*
8330 * 'ppd_parse_line()' - Parse a PPD default line.
8331 */
ef416fc2 8332
b423cd4c 8333static int /* O - 0 on success, -1 on failure */
8334ppd_parse_line(const char *line, /* I - Line */
8335 char *option, /* O - Option name */
8336 int olen, /* I - Size of option name */
8337 char *choice, /* O - Choice name */
8338 int clen) /* I - Size of choice name */
8339{
8340 /*
8341 * Verify this is a default option line...
8342 */
ef416fc2 8343
b423cd4c 8344 if (strncmp(line, "*Default", 8))
8345 return (-1);
ef416fc2 8346
b423cd4c 8347 /*
8348 * Read the option name...
8349 */
8350
18ecb428
MS
8351 for (line += 8, olen --;
8352 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
8353 line ++)
b423cd4c 8354 if (olen > 0)
8355 {
8356 *option++ = *line;
8357 olen --;
ef416fc2 8358 }
8359
b423cd4c 8360 *option = '\0';
ef416fc2 8361
b423cd4c 8362 /*
8363 * Skip everything else up to the colon (:)...
8364 */
ef416fc2 8365
b423cd4c 8366 while (*line && *line != ':')
8367 line ++;
ef416fc2 8368
b423cd4c 8369 if (!*line)
8370 return (-1);
ef416fc2 8371
b423cd4c 8372 line ++;
ef416fc2 8373
b423cd4c 8374 /*
8375 * Now grab the option choice, skipping leading whitespace...
8376 */
ef416fc2 8377
b423cd4c 8378 while (isspace(*line & 255))
8379 line ++;
ef416fc2 8380
18ecb428
MS
8381 for (clen --;
8382 *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
8383 line ++)
b423cd4c 8384 if (clen > 0)
8385 {
8386 *choice++ = *line;
8387 clen --;
8388 }
ef416fc2 8389
b423cd4c 8390 *choice = '\0';
ef416fc2 8391
b423cd4c 8392 /*
8393 * Return with no errors...
8394 */
ef416fc2 8395
b423cd4c 8396 return (0);
8397}
ef416fc2 8398
ef416fc2 8399
b423cd4c 8400/*
8401 * 'print_job()' - Print a file to a printer or class.
8402 */
ef416fc2 8403
b423cd4c 8404static void
8405print_job(cupsd_client_t *con, /* I - Client connection */
8406 ipp_attribute_t *uri) /* I - Printer URI */
8407{
8408 ipp_attribute_t *attr; /* Current attribute */
9514a192 8409 ipp_attribute_t *doc_name; /* document-name attribute */
b423cd4c 8410 ipp_attribute_t *format; /* Document-format attribute */
f7deaa1a 8411 const char *default_format; /* document-format-default value */
b423cd4c 8412 cupsd_job_t *job; /* New job */
8413 char filename[1024]; /* Job filename */
8414 mime_type_t *filetype; /* Type of file */
8415 char super[MIME_MAX_SUPER], /* Supertype of file */
8416 type[MIME_MAX_TYPE], /* Subtype of file */
8417 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
8418 /* Textual name of mime type */
8419 cupsd_printer_t *printer; /* Printer data */
8420 struct stat fileinfo; /* File information */
8421 int kbytes; /* Size of file */
8422 int compression; /* Document compression */
ef416fc2 8423
ef416fc2 8424
996acce8 8425 cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->number,
b423cd4c 8426 uri->values[0].string.text);
ef416fc2 8427
b423cd4c 8428 /*
8429 * Validate print file attributes, for now just document-format and
8430 * compression (CUPS only supports "none" and "gzip")...
8431 */
ef416fc2 8432
b423cd4c 8433 compression = CUPS_FILE_NONE;
ef416fc2 8434
b423cd4c 8435 if ((attr = ippFindAttribute(con->request, "compression",
8436 IPP_TAG_KEYWORD)) != NULL)
8437 {
8438 if (strcmp(attr->values[0].string.text, "none")
8439#ifdef HAVE_LIBZ
8440 && strcmp(attr->values[0].string.text, "gzip")
8441#endif /* HAVE_LIBZ */
8442 )
8443 {
8444 send_ipp_status(con, IPP_ATTRIBUTES,
84315f46 8445 _("Unsupported compression \"%s\"."),
b423cd4c 8446 attr->values[0].string.text);
8447 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
8448 "compression", NULL, attr->values[0].string.text);
8449 return;
8450 }
ef416fc2 8451
b423cd4c 8452#ifdef HAVE_LIBZ
8453 if (!strcmp(attr->values[0].string.text, "gzip"))
8454 compression = CUPS_FILE_GZIP;
8455#endif /* HAVE_LIBZ */
8456 }
ef416fc2 8457
b423cd4c 8458 /*
8459 * Do we have a file to print?
8460 */
ef416fc2 8461
b423cd4c 8462 if (!con->filename)
8463 {
84315f46 8464 send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
b423cd4c 8465 return;
8466 }
ef416fc2 8467
f7deaa1a 8468 /*
8469 * Is the destination valid?
8470 */
8471
8472 if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
8473 {
8474 /*
8475 * Bad URI...
8476 */
8477
8478 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 8479 _("The printer or class does not exist."));
f7deaa1a 8480 return;
8481 }
8482
b423cd4c 8483 /*
8484 * Is it a format we support?
8485 */
ef416fc2 8486
9514a192
MS
8487 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
8488 if (doc_name)
8489 ippSetName(con->request, &doc_name, "document-name-supplied");
8490
b423cd4c 8491 if ((format = ippFindAttribute(con->request, "document-format",
8492 IPP_TAG_MIMETYPE)) != NULL)
8493 {
8494 /*
8495 * Grab format from client...
8496 */
8497
c5b24bfa 8498 if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]", super,
f7deaa1a 8499 type) != 2)
b423cd4c 8500 {
8501 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 8502 _("Bad document-format \"%s\"."),
b423cd4c 8503 format->values[0].string.text);
8504 return;
ef416fc2 8505 }
9514a192
MS
8506
8507 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, ippGetString(format, 0, NULL));
b423cd4c 8508 }
f7deaa1a 8509 else if ((default_format = cupsGetOption("document-format",
8510 printer->num_options,
8511 printer->options)) != NULL)
8512 {
8513 /*
8514 * Use default document format...
8515 */
8516
c5b24bfa 8517 if (sscanf(default_format, "%15[^/]/%255[^;]", super, type) != 2)
f7deaa1a 8518 {
8519 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 8520 _("Bad document-format \"%s\"."),
f7deaa1a 8521 default_format);
8522 return;
8523 }
8524 }
b423cd4c 8525 else
8526 {
8527 /*
f7deaa1a 8528 * Auto-type it!
b423cd4c 8529 */
8530
5a9febac
MS
8531 strlcpy(super, "application", sizeof(super));
8532 strlcpy(type, "octet-stream", sizeof(type));
b423cd4c 8533 }
ef416fc2 8534
b423cd4c 8535 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
8536 {
ef416fc2 8537 /*
b423cd4c 8538 * Auto-type the file...
ef416fc2 8539 */
8540
a0f6818e 8541 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job ???] Auto-typing file...");
b423cd4c 8542
9514a192 8543
b423cd4c 8544 filetype = mimeFileType(MimeDatabase, con->filename,
8545 doc_name ? doc_name->values[0].string.text : NULL,
8546 &compression);
8547
f7deaa1a 8548 if (!filetype)
8549 filetype = mimeType(MimeDatabase, super, type);
a0f6818e
MS
8550
8551 cupsdLogMessage(CUPSD_LOG_INFO, "[Job ???] Request file type is %s/%s.",
8552 filetype->super, filetype->type);
9514a192
MS
8553
8554 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super, filetype->type);
8555 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, mimetype);
f7deaa1a 8556 }
8557 else
8558 filetype = mimeType(MimeDatabase, super, type);
ef416fc2 8559
f7deaa1a 8560 if (filetype &&
8561 (!format ||
8562 (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
8563 {
8564 /*
8565 * Replace the document-format attribute value with the auto-typed or
8566 * default one.
8567 */
b423cd4c 8568
f7deaa1a 8569 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
8570 filetype->type);
ed486911 8571
f7deaa1a 8572 if (format)
5e6c3df7 8573 ippSetString(con->request, &format, 0, mimetype);
b423cd4c 8574 else
f7deaa1a 8575 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
8576 "document-format", NULL, mimetype);
ef416fc2 8577 }
f7deaa1a 8578 else if (!filetype)
b423cd4c 8579 {
8580 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
84315f46 8581 _("Unsupported document-format \"%s\"."),
c7017ecc
MS
8582 format ? format->values[0].string.text :
8583 "application/octet-stream");
b423cd4c 8584 cupsdLogMessage(CUPSD_LOG_INFO,
8585 "Hint: Do you have the raw file printing rules enabled?");
8586
8587 if (format)
8588 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
8589 "document-format", NULL, format->values[0].string.text);
8590
8591 return;
8592 }
8593
b423cd4c 8594 /*
8595 * Read any embedded job ticket info from PS files...
8596 */
8597
88f9aafc
MS
8598 if (!_cups_strcasecmp(filetype->super, "application") &&
8599 (!_cups_strcasecmp(filetype->type, "postscript") ||
8600 !_cups_strcasecmp(filetype->type, "pdf")))
c5571a1d 8601 read_job_ticket(con);
b423cd4c 8602
8603 /*
8604 * Create the job object...
8605 */
8606
f7deaa1a 8607 if ((job = add_job(con, printer, filetype)) == NULL)
b423cd4c 8608 return;
8609
8610 /*
8611 * Update quota data...
8612 */
8613
8614 if (stat(con->filename, &fileinfo))
8615 kbytes = 0;
8616 else
8617 kbytes = (fileinfo.st_size + 1023) / 1024;
8618
8619 cupsdUpdateQuota(printer, job->username, 0, kbytes);
8620
f16ea703
MS
8621 job->koctets += kbytes;
8622
8623 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
b423cd4c 8624 attr->values[0].integer += kbytes;
8625
ef416fc2 8626 /*
8627 * Add the job file...
8628 */
8629
8630 if (add_file(con, job, filetype, compression))
8631 return;
8632
6d8021f4
MS
8633 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
8634 if (rename(con->filename, filename))
8635 {
8636 cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to rename job document file \"%s\": %s", filename, strerror(errno));
8637
8638 send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to rename job document file."));
8639 return;
8640 }
8641
ef416fc2 8642 cupsdClearString(&con->filename);
8643
8644 /*
8645 * See if we need to add the ending sheet...
8646 */
8647
91c84a35
MS
8648 if (cupsdTimeoutJob(job))
8649 return;
ef416fc2 8650
ef416fc2 8651 /*
8652 * Log and save the job...
8653 */
8654
75bd9771
MS
8655 cupsdLogJob(job, CUPSD_LOG_INFO,
8656 "File of type %s/%s queued by \"%s\".",
8657 filetype->super, filetype->type, job->username);
8658 cupsdLogJob(job, CUPSD_LOG_DEBUG, "hold_until=%d", (int)job->hold_until);
b19ccc9e
MS
8659 cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
8660 job->dest, job->username);
ef416fc2 8661
ef416fc2 8662 /*
8663 * Start the job if possible...
8664 */
8665
8666 cupsdCheckJobs();
8667}
8668
8669
8670/*
c5571a1d 8671 * 'read_job_ticket()' - Read a job ticket embedded in a print file.
ef416fc2 8672 *
c5571a1d 8673 * This function only gets called when printing a single PDF or PostScript
ef416fc2 8674 * file using the Print-Job operation. It doesn't work for Create-Job +
8675 * Send-File, since the job attributes need to be set at job creation
c5571a1d
MS
8676 * time for banners to work. The embedded job ticket stuff is here
8677 * primarily to allow the Windows printer driver for CUPS to pass in JCL
ef416fc2 8678 * options and IPP attributes which otherwise would be lost.
8679 *
c5571a1d 8680 * The format of a job ticket is simple:
ef416fc2 8681 *
8682 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
8683 *
8684 * %cupsJobTicket: attr1=value1
8685 * %cupsJobTicket: attr2=value2
8686 * ...
8687 * %cupsJobTicket: attrN=valueN
8688 *
8689 * Job ticket lines must appear immediately after the first line that
c5571a1d
MS
8690 * specifies PostScript (%!PS-Adobe-3.0) or PDF (%PDF) format, and CUPS
8691 * stops looking for job ticket info when it finds a line that does not begin
ef416fc2 8692 * with "%cupsJobTicket:".
8693 *
8694 * The maximum length of a job ticket line, including the prefix, is
8695 * 255 characters to conform with the Adobe DSC.
8696 *
8697 * Read-only attributes are rejected with a notice to the error log in
8698 * case a malicious user tries anything. Since the job ticket is read
8699 * prior to attribute validation in print_job(), job ticket attributes
8700 * will go through the same validation as IPP attributes...
8701 */
8702
8703static void
c5571a1d 8704read_job_ticket(cupsd_client_t *con) /* I - Client connection */
ef416fc2 8705{
8706 cups_file_t *fp; /* File to read from */
8707 char line[256]; /* Line data */
8708 int num_options; /* Number of options */
8709 cups_option_t *options; /* Options */
8710 ipp_t *ticket; /* New attributes */
8711 ipp_attribute_t *attr, /* Current attribute */
8712 *attr2, /* Job attribute */
8713 *prev2; /* Previous job attribute */
8714
8715
8716 /*
8717 * First open the print file...
8718 */
8719
8720 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
8721 {
8722 cupsdLogMessage(CUPSD_LOG_ERROR,
c5571a1d 8723 "Unable to open print file for job ticket - %s",
ef416fc2 8724 strerror(errno));
8725 return;
8726 }
8727
8728 /*
8729 * Skip the first line...
8730 */
8731
8732 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
8733 {
8734 cupsdLogMessage(CUPSD_LOG_ERROR,
c5571a1d 8735 "Unable to read from print file for job ticket - %s",
ef416fc2 8736 strerror(errno));
8737 cupsFileClose(fp);
8738 return;
8739 }
8740
c5571a1d 8741 if (strncmp(line, "%!PS-Adobe-", 11) && strncmp(line, "%PDF-", 5))
ef416fc2 8742 {
8743 /*
8744 * Not a DSC-compliant file, so no job ticket info will be available...
8745 */
8746
8747 cupsFileClose(fp);
8748 return;
8749 }
8750
8751 /*
8752 * Read job ticket info from the file...
8753 */
8754
8755 num_options = 0;
8756 options = NULL;
8757
fa73b229 8758 while (cupsFileGets(fp, line, sizeof(line)))
ef416fc2 8759 {
8760 /*
8761 * Stop at the first non-ticket line...
8762 */
8763
fa73b229 8764 if (strncmp(line, "%cupsJobTicket:", 15))
ef416fc2 8765 break;
8766
8767 /*
8768 * Add the options to the option array...
8769 */
8770
8771 num_options = cupsParseOptions(line + 15, num_options, &options);
8772 }
8773
8774 /*
8775 * Done with the file; see if we have any options...
8776 */
8777
8778 cupsFileClose(fp);
8779
8780 if (num_options == 0)
8781 return;
8782
8783 /*
8784 * OK, convert the options to an attribute list, and apply them to
8785 * the request...
8786 */
8787
8788 ticket = ippNew();
8789 cupsEncodeOptions(ticket, num_options, options);
8790
8791 /*
8792 * See what the user wants to change.
8793 */
8794
fa73b229 8795 for (attr = ticket->attrs; attr; attr = attr->next)
ef416fc2 8796 {
8797 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
8798 continue;
8799
9514a192
MS
8800 if (!strncmp(attr->name, "date-time-at-", 13) ||
8801 !strcmp(attr->name, "job-impressions-completed") ||
ef416fc2 8802 !strcmp(attr->name, "job-media-sheets-completed") ||
9514a192 8803 !strncmp(attr->name, "job-k-octets", 12) ||
ef416fc2 8804 !strcmp(attr->name, "job-id") ||
9514a192
MS
8805 !strcmp(attr->name, "job-originating-host-name") ||
8806 !strcmp(attr->name, "job-originating-user-name") ||
8807 !strcmp(attr->name, "job-pages-completed") ||
8808 !strcmp(attr->name, "job-printer-uri") ||
ef416fc2 8809 !strncmp(attr->name, "job-state", 9) ||
9514a192 8810 !strcmp(attr->name, "job-uri") ||
ef416fc2 8811 !strncmp(attr->name, "time-at-", 8))
8812 continue; /* Read-only attrs */
8813
fa73b229 8814 if ((attr2 = ippFindAttribute(con->request, attr->name,
8815 IPP_TAG_ZERO)) != NULL)
ef416fc2 8816 {
8817 /*
8818 * Some other value; first free the old value...
8819 */
8820
8821 if (con->request->attrs == attr2)
8822 {
8823 con->request->attrs = attr2->next;
8824 prev2 = NULL;
8825 }
8826 else
8827 {
fa73b229 8828 for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
ef416fc2 8829 if (prev2->next == attr2)
8830 {
8831 prev2->next = attr2->next;
8832 break;
8833 }
8834 }
8835
8836 if (con->request->last == attr2)
8837 con->request->last = prev2;
8838
a2326b5b 8839 ippDeleteAttribute(NULL, attr2);
ef416fc2 8840 }
8841
8842 /*
8843 * Add new option by copying it...
8844 */
8845
a2326b5b 8846 ippCopyAttribute(con->request, attr, 0);
ef416fc2 8847 }
8848
8849 /*
8850 * Then free the attribute list and option array...
8851 */
8852
8853 ippDelete(ticket);
8854 cupsFreeOptions(num_options, options);
8855}
8856
8857
8858/*
8859 * 'reject_jobs()' - Reject print jobs to a printer.
8860 */
8861
8862static void
8863reject_jobs(cupsd_client_t *con, /* I - Client connection */
8864 ipp_attribute_t *uri) /* I - Printer or class URI */
8865{
8866 http_status_t status; /* Policy status */
bc44d920 8867 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 8868 cupsd_printer_t *printer; /* Printer data */
8869 ipp_attribute_t *attr; /* printer-state-message text */
8870
8871
8872 cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
996acce8 8873 con->number, uri->values[0].string.text);
ef416fc2 8874
8875 /*
8876 * Is the destination valid?
8877 */
8878
f7deaa1a 8879 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 8880 {
8881 /*
8882 * Bad URI...
8883 */
8884
8885 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 8886 _("The printer or class does not exist."));
ef416fc2 8887 return;
8888 }
8889
8890 /*
8891 * Check policy...
8892 */
8893
8894 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8895 {
f899b121 8896 send_http_error(con, status, printer);
ef416fc2 8897 return;
8898 }
8899
8900 /*
8901 * Reject jobs sent to the printer...
8902 */
8903
8904 printer->accepting = 0;
8905
8906 if ((attr = ippFindAttribute(con->request, "printer-state-message",
8907 IPP_TAG_TEXT)) == NULL)
5a9febac
MS
8908 strlcpy(printer->state_message, "Rejecting Jobs",
8909 sizeof(printer->state_message));
ef416fc2 8910 else
8911 strlcpy(printer->state_message, attr->values[0].string.text,
8912 sizeof(printer->state_message));
8913
f8b3a85b
MS
8914 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
8915 "No longer accepting jobs.");
8916
ef416fc2 8917 if (dtype & CUPS_PRINTER_CLASS)
8918 {
3dfe78b3 8919 cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
ef416fc2 8920
8921 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
f7deaa1a 8922 printer->name, get_username(con));
ef416fc2 8923 }
8924 else
8925 {
3dfe78b3 8926 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
ef416fc2 8927
8928 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
f7deaa1a 8929 printer->name, get_username(con));
ef416fc2 8930 }
8931
8932 /*
8933 * Everything was ok, so return OK status...
8934 */
8935
8936 con->response->request.status.status_code = IPP_OK;
8937}
8938
8939
61cf44e2
MS
8940/*
8941 * 'release_held_new_jobs()' - Release pending/new jobs on a printer or class.
8942 */
8943
8944static void
8945release_held_new_jobs(
8946 cupsd_client_t *con, /* I - Connection */
8947 ipp_attribute_t *uri) /* I - Printer URI */
8948{
8949 http_status_t status; /* Policy status */
8950 cups_ptype_t dtype; /* Destination type (printer/class) */
8951 cupsd_printer_t *printer; /* Printer data */
8952
8953
8954 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_held_new_jobs(%p[%d], %s)", con,
996acce8 8955 con->number, uri->values[0].string.text);
61cf44e2
MS
8956
8957 /*
8958 * Is the destination valid?
8959 */
8960
8961 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8962 {
8963 /*
8964 * Bad URI...
8965 */
8966
8967 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 8968 _("The printer or class does not exist."));
61cf44e2
MS
8969 return;
8970 }
8971
8972 /*
8973 * Check policy...
8974 */
8975
8976 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8977 {
8978 send_http_error(con, status, printer);
8979 return;
8980 }
8981
8982 /*
8983 * Hold pending/new jobs sent to the printer...
8984 */
8985
8986 printer->holding_new_jobs = 0;
8987
8988 cupsdSetPrinterReasons(printer, "-hold-new-jobs");
61cf44e2
MS
8989
8990 if (dtype & CUPS_PRINTER_CLASS)
8991 cupsdLogMessage(CUPSD_LOG_INFO,
8992 "Class \"%s\" now printing pending/new jobs (\"%s\").",
8993 printer->name, get_username(con));
8994 else
8995 cupsdLogMessage(CUPSD_LOG_INFO,
8996 "Printer \"%s\" now printing pending/new jobs (\"%s\").",
8997 printer->name, get_username(con));
8998
e0f489cd
MS
8999 cupsdCheckJobs();
9000
61cf44e2
MS
9001 /*
9002 * Everything was ok, so return OK status...
9003 */
9004
9005 con->response->request.status.status_code = IPP_OK;
9006}
9007
9008
ef416fc2 9009/*
9010 * 'release_job()' - Release a held print job.
9011 */
9012
9013static void
9014release_job(cupsd_client_t *con, /* I - Client connection */
9015 ipp_attribute_t *uri) /* I - Job or Printer URI */
9016{
9017 ipp_attribute_t *attr; /* Current attribute */
9018 int jobid; /* Job ID */
61cf44e2 9019 char scheme[HTTP_MAX_URI], /* Method portion of URI */
ef416fc2 9020 username[HTTP_MAX_URI], /* Username portion of URI */
9021 host[HTTP_MAX_URI], /* Host portion of URI */
9022 resource[HTTP_MAX_URI]; /* Resource portion of URI */
9023 int port; /* Port portion of URI */
9024 cupsd_job_t *job; /* Job information */
9025
9026
9027 cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
996acce8 9028 con->number, uri->values[0].string.text);
ef416fc2 9029
9030 /*
9031 * See if we have a job URI or a printer URI...
9032 */
9033
9034 if (!strcmp(uri->name, "printer-uri"))
9035 {
9036 /*
9037 * Got a printer URI; see if we also have a job-id attribute...
9038 */
9039
fa73b229 9040 if ((attr = ippFindAttribute(con->request, "job-id",
9041 IPP_TAG_INTEGER)) == NULL)
ef416fc2 9042 {
9043 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 9044 _("Got a printer-uri attribute but no job-id."));
ef416fc2 9045 return;
9046 }
9047
9048 jobid = attr->values[0].integer;
9049 }
9050 else
9051 {
9052 /*
9053 * Got a job URI; parse it to get the job ID...
9054 */
9055
61cf44e2
MS
9056 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9057 sizeof(scheme), username, sizeof(username), host,
a4d04587 9058 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 9059
9060 if (strncmp(resource, "/jobs/", 6))
9061 {
9062 /*
9063 * Not a valid URI!
9064 */
9065
84315f46 9066 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
ef416fc2 9067 uri->values[0].string.text);
9068 return;
9069 }
9070
9071 jobid = atoi(resource + 6);
9072 }
9073
9074 /*
9075 * See if the job exists...
9076 */
9077
9078 if ((job = cupsdFindJob(jobid)) == NULL)
9079 {
9080 /*
9081 * Nope - return a "not found" error...
9082 */
9083
84315f46 9084 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
ef416fc2 9085 return;
9086 }
9087
9088 /*
9089 * See if job is "held"...
9090 */
9091
bd7854cb 9092 if (job->state_value != IPP_JOB_HELD)
ef416fc2 9093 {
9094 /*
9095 * Nope - return a "not possible" error...
9096 */
9097
84315f46 9098 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held."), jobid);
ef416fc2 9099 return;
9100 }
9101
9102 /*
9103 * See if the job is owned by the requesting user...
9104 */
9105
9106 if (!validate_user(job, con, job->username, username, sizeof(username)))
9107 {
b0f6947b
MS
9108 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9109 cupsdFindDest(job->dest));
ef416fc2 9110 return;
9111 }
9112
9113 /*
9114 * Reset the job-hold-until value to "no-hold"...
9115 */
9116
fa73b229 9117 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9118 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 9119 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9120
fa73b229 9121 if (attr)
ef416fc2 9122 {
5e6c3df7
MS
9123 ippSetValueTag(job->attrs, &attr, IPP_TAG_KEYWORD);
9124 ippSetString(job->attrs, &attr, 0, "no-hold");
d09495fa 9125
01ce6322 9126 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
d09495fa 9127 "Job job-hold-until value changed by user.");
3e7fe0ca 9128 ippSetString(job->attrs, &job->reasons, 0, "none");
ef416fc2 9129 }
9130
9131 /*
9132 * Release the job and return...
9133 */
9134
9135 cupsdReleaseJob(job);
9136
01ce6322 9137 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
d09495fa 9138 "Job released by user.");
9139
75bd9771 9140 cupsdLogJob(job, CUPSD_LOG_INFO, "Released by \"%s\".", username);
ef416fc2 9141
9142 con->response->request.status.status_code = IPP_OK;
005dd1eb
MS
9143
9144 cupsdCheckJobs();
ef416fc2 9145}
9146
9147
9148/*
9149 * 'renew_subscription()' - Renew an existing subscription...
9150 */
9151
9152static void
9153renew_subscription(
9154 cupsd_client_t *con, /* I - Client connection */
9155 int sub_id) /* I - Subscription ID */
9156{
bd7854cb 9157 http_status_t status; /* Policy status */
9158 cupsd_subscription_t *sub; /* Subscription */
9159 ipp_attribute_t *lease; /* notify-lease-duration */
9160
9161
9162 cupsdLogMessage(CUPSD_LOG_DEBUG2,
9163 "renew_subscription(con=%p[%d], sub_id=%d)",
996acce8 9164 con, con->number, sub_id);
bd7854cb 9165
9166 /*
9167 * Is the subscription ID valid?
9168 */
9169
9170 if ((sub = cupsdFindSubscription(sub_id)) == NULL)
9171 {
9172 /*
9173 * Bad subscription ID...
9174 */
9175
84315f46
MS
9176 send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
9177 sub_id);
bd7854cb 9178 return;
9179 }
9180
9181 if (sub->job)
9182 {
9183 /*
9184 * Job subscriptions cannot be renewed...
9185 */
9186
9187 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 9188 _("Job subscriptions cannot be renewed."));
bd7854cb 9189 return;
9190 }
9191
9192 /*
9193 * Check policy...
9194 */
9195
9196 if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
9197 DefaultPolicyPtr,
9198 con, sub->owner)) != HTTP_OK)
9199 {
f899b121 9200 send_http_error(con, status, sub->dest);
bd7854cb 9201 return;
9202 }
9203
9204 /*
9205 * Renew the subscription...
9206 */
9207
9208 lease = ippFindAttribute(con->request, "notify-lease-duration",
9209 IPP_TAG_INTEGER);
9210
9211 sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
9212
9213 if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
9214 {
9215 cupsdLogMessage(CUPSD_LOG_INFO,
9216 "renew_subscription: Limiting notify-lease-duration to "
9217 "%d seconds.",
9218 MaxLeaseDuration);
9219 sub->lease = MaxLeaseDuration;
9220 }
9221
9222 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
9223
3dfe78b3 9224 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
bd7854cb 9225
9226 con->response->request.status.status_code = IPP_OK;
b94498cf 9227
9228 ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
9229 "notify-lease-duration", sub->lease);
ef416fc2 9230}
9231
9232
9233/*
9234 * 'restart_job()' - Restart an old print job.
9235 */
9236
9237static void
9238restart_job(cupsd_client_t *con, /* I - Client connection */
9239 ipp_attribute_t *uri) /* I - Job or Printer URI */
9240{
9241 ipp_attribute_t *attr; /* Current attribute */
9242 int jobid; /* Job ID */
238c3832 9243 cupsd_job_t *job; /* Job information */
61cf44e2 9244 char scheme[HTTP_MAX_URI], /* Method portion of URI */
ef416fc2 9245 username[HTTP_MAX_URI], /* Username portion of URI */
9246 host[HTTP_MAX_URI], /* Host portion of URI */
9247 resource[HTTP_MAX_URI]; /* Resource portion of URI */
9248 int port; /* Port portion of URI */
ef416fc2 9249
9250
9251 cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
996acce8 9252 con->number, uri->values[0].string.text);
ef416fc2 9253
9254 /*
9255 * See if we have a job URI or a printer URI...
9256 */
9257
9258 if (!strcmp(uri->name, "printer-uri"))
9259 {
9260 /*
9261 * Got a printer URI; see if we also have a job-id attribute...
9262 */
9263
fa73b229 9264 if ((attr = ippFindAttribute(con->request, "job-id",
9265 IPP_TAG_INTEGER)) == NULL)
ef416fc2 9266 {
9267 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 9268 _("Got a printer-uri attribute but no job-id."));
ef416fc2 9269 return;
9270 }
9271
9272 jobid = attr->values[0].integer;
9273 }
9274 else
9275 {
9276 /*
9277 * Got a job URI; parse it to get the job ID...
9278 */
9279
61cf44e2
MS
9280 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9281 sizeof(scheme), username, sizeof(username), host,
a4d04587 9282 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 9283
fa73b229 9284 if (strncmp(resource, "/jobs/", 6))
ef416fc2 9285 {
9286 /*
9287 * Not a valid URI!
9288 */
9289
84315f46 9290 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
ef416fc2 9291 uri->values[0].string.text);
9292 return;
9293 }
9294
9295 jobid = atoi(resource + 6);
9296 }
9297
9298 /*
9299 * See if the job exists...
9300 */
9301
9302 if ((job = cupsdFindJob(jobid)) == NULL)
9303 {
9304 /*
9305 * Nope - return a "not found" error...
9306 */
9307
84315f46 9308 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
ef416fc2 9309 return;
9310 }
9311
9312 /*
9313 * See if job is in any of the "completed" states...
9314 */
9315
bd7854cb 9316 if (job->state_value <= IPP_JOB_PROCESSING)
ef416fc2 9317 {
9318 /*
9319 * Nope - return a "not possible" error...
9320 */
9321
84315f46 9322 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete."),
ef416fc2 9323 jobid);
9324 return;
9325 }
9326
9327 /*
9328 * See if we have retained the job files...
9329 */
9330
bd7854cb 9331 cupsdLoadJob(job);
9332
07725fee 9333 if (!job->attrs || job->num_files == 0)
ef416fc2 9334 {
9335 /*
9336 * Nope - return a "not possible" error...
9337 */
9338
9339 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 9340 _("Job #%d cannot be restarted - no files."), jobid);
ef416fc2 9341 return;
9342 }
9343
9344 /*
9345 * See if the job is owned by the requesting user...
9346 */
9347
9348 if (!validate_user(job, con, job->username, username, sizeof(username)))
9349 {
b0f6947b
MS
9350 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9351 cupsdFindDest(job->dest));
ef416fc2 9352 return;
9353 }
9354
9355 /*
238c3832 9356 * See if the job-hold-until attribute is specified...
ef416fc2 9357 */
9358
238c3832
MS
9359 if ((attr = ippFindAttribute(con->request, "job-hold-until",
9360 IPP_TAG_KEYWORD)) == NULL)
9361 attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
9362
9363 if (attr && strcmp(attr->values[0].string.text, "no-hold"))
9364 {
9365 /*
9366 * Return the job to a held state...
9367 */
9368
9369 cupsdLogJob(job, CUPSD_LOG_DEBUG,
9370 "Restarted by \"%s\" with job-hold-until=%s.",
9371 username, attr->values[0].string.text);
9372 cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
9373
9374 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE,
9375 NULL, job, "Job restarted by user with job-hold-until=%s",
9376 attr->values[0].string.text);
9377 }
9378 else
9379 {
9380 /*
9381 * Restart the job...
9382 */
9383
9384 cupsdRestartJob(job);
f11a948a 9385 cupsdCheckJobs();
238c3832 9386 }
ef416fc2 9387
75bd9771 9388 cupsdLogJob(job, CUPSD_LOG_INFO, "Restarted by \"%s\".", username);
ef416fc2 9389
9390 con->response->request.status.status_code = IPP_OK;
9391}
9392
9393
9394/*
9395 * 'save_auth_info()' - Save authentication information for a job.
9396 */
9397
9398static void
f7deaa1a 9399save_auth_info(
9400 cupsd_client_t *con, /* I - Client connection */
9401 cupsd_job_t *job, /* I - Job */
9402 ipp_attribute_t *auth_info) /* I - auth-info attribute, if any */
ef416fc2 9403{
09a101d6 9404 int i; /* Looping var */
9405 char filename[1024]; /* Job authentication filename */
9406 cups_file_t *fp; /* Job authentication file */
85dda01c 9407 char line[65536]; /* Line for file */
09a101d6 9408 cupsd_printer_t *dest; /* Destination printer/class */
ef416fc2 9409
9410
9411 /*
9412 * This function saves the in-memory authentication information for
9413 * a job so that it can be used to authenticate with a remote host.
9414 * The information is stored in a file that is readable only by the
f7deaa1a 9415 * root user. The fields are Base-64 encoded, each on a separate line,
9416 * followed by random number (up to 1024) of newlines to limit the
9417 * amount of information that is exposed.
ef416fc2 9418 *
9419 * Because of the potential for exposing of authentication information,
9420 * this functionality is only enabled when running cupsd as root.
9421 *
9422 * This caching only works for the Basic and BasicDigest authentication
9423 * types. Digest authentication cannot be cached this way, and in
9424 * the future Kerberos authentication may make all of this obsolete.
9425 *
9426 * Authentication information is saved whenever an authenticated
9427 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
9428 * performed.
9429 *
9430 * This information is deleted after a job is completed or canceled,
9431 * so reprints may require subsequent re-authentication.
9432 */
9433
9434 if (RunUser)
9435 return;
9436
09a101d6 9437 if ((dest = cupsdFindDest(job->dest)) == NULL)
9438 return;
9439
ef416fc2 9440 /*
9441 * Create the authentication file and change permissions...
9442 */
9443
9444 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
9445 if ((fp = cupsFileOpen(filename, "w")) == NULL)
9446 {
9447 cupsdLogMessage(CUPSD_LOG_ERROR,
9448 "Unable to save authentication info to \"%s\" - %s",
9449 filename, strerror(errno));
9450 return;
9451 }
9452
9453 fchown(cupsFileNumber(fp), 0, 0);
9454 fchmod(cupsFileNumber(fp), 0400);
9455
94436c5a 9456 cupsFilePuts(fp, "CUPSD-AUTH-V3\n");
dcb445bc 9457
88f9aafc
MS
9458 for (i = 0;
9459 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
9460 i ++)
9461 cupsdClearString(job->auth_env + i);
9462
09a101d6 9463 if (auth_info && auth_info->num_values == dest->num_auth_info_required)
f7deaa1a 9464 {
9465 /*
09a101d6 9466 * Write 1 to 3 auth values...
f7deaa1a 9467 */
ef416fc2 9468
88f9aafc
MS
9469 for (i = 0;
9470 i < auth_info->num_values &&
9471 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
9472 i ++)
f7deaa1a 9473 {
94436c5a
MS
9474 if (strcmp(dest->auth_info_required[i], "negotiate"))
9475 {
7e86f2f6 9476 httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text, (int)strlen(auth_info->values[i].string.text));
94436c5a
MS
9477 cupsFilePutConf(fp, dest->auth_info_required[i], line);
9478 }
9479 else
9480 cupsFilePutConf(fp, dest->auth_info_required[i],
9481 auth_info->values[i].string.text);
09a101d6 9482
9483 if (!strcmp(dest->auth_info_required[i], "username"))
88f9aafc 9484 cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s",
09a101d6 9485 auth_info->values[i].string.text);
9486 else if (!strcmp(dest->auth_info_required[i], "domain"))
88f9aafc 9487 cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s",
09a101d6 9488 auth_info->values[i].string.text);
9489 else if (!strcmp(dest->auth_info_required[i], "password"))
88f9aafc
MS
9490 cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s",
9491 auth_info->values[i].string.text);
9492 else if (!strcmp(dest->auth_info_required[i], "negotiate"))
9493 cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s",
9494 auth_info->values[i].string.text);
9495 else
dcb445bc 9496 i --;
f7deaa1a 9497 }
9498 }
dcb445bc
MS
9499 else if (auth_info && auth_info->num_values == 2 &&
9500 dest->num_auth_info_required == 1 &&
9501 !strcmp(dest->auth_info_required[0], "negotiate"))
9502 {
9503 /*
9504 * Allow fallback to username+password for Kerberized queues...
9505 */
9506
7e86f2f6 9507 httpEncode64_2(line, sizeof(line), auth_info->values[0].string.text, (int)strlen(auth_info->values[0].string.text));
dcb445bc
MS
9508 cupsFilePutConf(fp, "username", line);
9509
9510 cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s",
9511 auth_info->values[0].string.text);
9512
7e86f2f6 9513 httpEncode64_2(line, sizeof(line), auth_info->values[1].string.text, (int)strlen(auth_info->values[1].string.text));
dcb445bc
MS
9514 cupsFilePutConf(fp, "password", line);
9515
9516 cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s",
9517 auth_info->values[1].string.text);
9518 }
09a101d6 9519 else if (con->username[0])
f7deaa1a 9520 {
9521 /*
9522 * Write the authenticated username...
9523 */
ef416fc2 9524
7e86f2f6 9525 httpEncode64_2(line, sizeof(line), con->username, (int)strlen(con->username));
dcb445bc 9526 cupsFilePutConf(fp, "username", line);
ef416fc2 9527
88f9aafc 9528 cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s", con->username);
09a101d6 9529
f7deaa1a 9530 /*
9531 * Write the authenticated password...
9532 */
9533
7e86f2f6 9534 httpEncode64_2(line, sizeof(line), con->password, (int)strlen(con->password));
dcb445bc 9535 cupsFilePutConf(fp, "password", line);
09a101d6 9536
88f9aafc 9537 cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s", con->password);
f7deaa1a 9538 }
ef416fc2 9539
07ed0e9a 9540#ifdef HAVE_GSSAPI
eac3a0a0 9541 if (con->gss_uid > 0)
07ed0e9a 9542 {
dcb445bc 9543 cupsFilePrintf(fp, "uid %d\n", (int)con->gss_uid);
07ed0e9a
MS
9544 cupsdSetStringf(&job->auth_uid, "AUTH_UID=%d", (int)con->gss_uid);
9545 }
9546#endif /* HAVE_GSSAPI */
9547
ef416fc2 9548 /*
9549 * Write a random number of newlines to the end of the file...
9550 */
9551
41681883 9552 for (i = (CUPS_RAND() % 1024); i >= 0; i --)
ef416fc2 9553 cupsFilePutChar(fp, '\n');
9554
9555 /*
9556 * Close the file and return...
9557 */
9558
9559 cupsFileClose(fp);
9560}
9561
9562
9563/*
9564 * 'send_document()' - Send a file to a printer or class.
9565 */
9566
9567static void
9568send_document(cupsd_client_t *con, /* I - Client connection */
9569 ipp_attribute_t *uri) /* I - Printer URI */
9570{
9571 ipp_attribute_t *attr; /* Current attribute */
a0f6818e
MS
9572 ipp_attribute_t *format; /* Request's document-format attribute */
9573 ipp_attribute_t *jformat; /* Job's document-format attribute */
f7deaa1a 9574 const char *default_format;/* document-format-default value */
ef416fc2 9575 int jobid; /* Job ID number */
9576 cupsd_job_t *job; /* Current job */
9577 char job_uri[HTTP_MAX_URI],
9578 /* Job URI */
61cf44e2 9579 scheme[HTTP_MAX_URI],
ef416fc2 9580 /* Method portion of URI */
9581 username[HTTP_MAX_URI],
9582 /* Username portion of URI */
9583 host[HTTP_MAX_URI],
9584 /* Host portion of URI */
9585 resource[HTTP_MAX_URI];
9586 /* Resource portion of URI */
9587 int port; /* Port portion of URI */
9588 mime_type_t *filetype; /* Type of file */
9589 char super[MIME_MAX_SUPER],
9590 /* Supertype of file */
9591 type[MIME_MAX_TYPE],
9592 /* Subtype of file */
9593 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
9594 /* Textual name of mime type */
9595 char filename[1024]; /* Job filename */
9596 cupsd_printer_t *printer; /* Current printer */
9597 struct stat fileinfo; /* File information */
9598 int kbytes; /* Size of file */
9599 int compression; /* Type of compression */
75bd9771 9600 int start_job; /* Start the job? */
ef416fc2 9601
9602
9603 cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
996acce8 9604 con->number, uri->values[0].string.text);
ef416fc2 9605
9606 /*
9607 * See if we have a job URI or a printer URI...
9608 */
9609
9610 if (!strcmp(uri->name, "printer-uri"))
9611 {
9612 /*
9613 * Got a printer URI; see if we also have a job-id attribute...
9614 */
9615
fa73b229 9616 if ((attr = ippFindAttribute(con->request, "job-id",
9617 IPP_TAG_INTEGER)) == NULL)
ef416fc2 9618 {
9619 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 9620 _("Got a printer-uri attribute but no job-id."));
ef416fc2 9621 return;
9622 }
9623
9624 jobid = attr->values[0].integer;
9625 }
9626 else
9627 {
9628 /*
9629 * Got a job URI; parse it to get the job ID...
9630 */
9631
61cf44e2
MS
9632 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9633 sizeof(scheme), username, sizeof(username), host,
a4d04587 9634 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 9635
9636 if (strncmp(resource, "/jobs/", 6))
9637 {
9638 /*
9639 * Not a valid URI!
9640 */
9641
84315f46 9642 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
ef416fc2 9643 uri->values[0].string.text);
9644 return;
9645 }
9646
9647 jobid = atoi(resource + 6);
9648 }
9649
9650 /*
9651 * See if the job exists...
9652 */
9653
9654 if ((job = cupsdFindJob(jobid)) == NULL)
9655 {
9656 /*
9657 * Nope - return a "not found" error...
9658 */
9659
84315f46 9660 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
ef416fc2 9661 return;
9662 }
9663
f301802f 9664 printer = cupsdFindDest(job->dest);
9665
ef416fc2 9666 /*
9667 * See if the job is owned by the requesting user...
9668 */
9669
9670 if (!validate_user(job, con, job->username, username, sizeof(username)))
9671 {
b0f6947b
MS
9672 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9673 cupsdFindDest(job->dest));
ef416fc2 9674 return;
9675 }
9676
9677 /*
9678 * OK, see if the client is sending the document compressed - CUPS
9679 * only supports "none" and "gzip".
9680 */
9681
9682 compression = CUPS_FILE_NONE;
9683
fa73b229 9684 if ((attr = ippFindAttribute(con->request, "compression",
9685 IPP_TAG_KEYWORD)) != NULL)
ef416fc2 9686 {
9687 if (strcmp(attr->values[0].string.text, "none")
9688#ifdef HAVE_LIBZ
9689 && strcmp(attr->values[0].string.text, "gzip")
9690#endif /* HAVE_LIBZ */
9691 )
9692 {
84315f46 9693 send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\"."),
ef416fc2 9694 attr->values[0].string.text);
9695 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
9696 "compression", NULL, attr->values[0].string.text);
9697 return;
9698 }
9699
9700#ifdef HAVE_LIBZ
9701 if (!strcmp(attr->values[0].string.text, "gzip"))
9702 compression = CUPS_FILE_GZIP;
9703#endif /* HAVE_LIBZ */
9704 }
9705
9706 /*
9707 * Do we have a file to print?
9708 */
9709
83e08001
MS
9710 if ((attr = ippFindAttribute(con->request, "last-document",
9711 IPP_TAG_BOOLEAN)) == NULL)
9712 {
9713 send_ipp_status(con, IPP_BAD_REQUEST,
9714 _("Missing last-document attribute in request."));
9715 return;
9716 }
9717
ef416fc2 9718 if (!con->filename)
9719 {
6d2f911b
MS
9720 /*
9721 * Check for an empty request with "last-document" set to true, which is
9722 * used to close an "open" job by RFC 2911, section 3.3.2.
9723 */
9724
83e08001 9725 if (job->num_files > 0 && attr->values[0].boolean)
6d2f911b
MS
9726 goto last_document;
9727
84315f46 9728 send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
ef416fc2 9729 return;
9730 }
9731
9732 /*
9733 * Is it a format we support?
9734 */
9735
9514a192
MS
9736 cupsdLoadJob(job);
9737
ef416fc2 9738 if ((format = ippFindAttribute(con->request, "document-format",
9739 IPP_TAG_MIMETYPE)) != NULL)
9740 {
9741 /*
9742 * Grab format from client...
9743 */
9744
c5b24bfa 9745 if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]",
bc44d920 9746 super, type) != 2)
ef416fc2 9747 {
84315f46 9748 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"."),
ef416fc2 9749 format->values[0].string.text);
9750 return;
9751 }
9514a192
MS
9752
9753 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, ippGetString(format, 0, NULL));
ef416fc2 9754 }
f7deaa1a 9755 else if ((default_format = cupsGetOption("document-format",
9756 printer->num_options,
9757 printer->options)) != NULL)
9758 {
9759 /*
9760 * Use default document format...
9761 */
9762
c5b24bfa 9763 if (sscanf(default_format, "%15[^/]/%255[^;]", super, type) != 2)
f7deaa1a 9764 {
9765 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 9766 _("Bad document-format-default \"%s\"."), default_format);
f7deaa1a 9767 return;
9768 }
9769 }
ef416fc2 9770 else
9771 {
9772 /*
9773 * No document format attribute? Auto-type it!
9774 */
9775
5a9febac
MS
9776 strlcpy(super, "application", sizeof(super));
9777 strlcpy(type, "octet-stream", sizeof(type));
ef416fc2 9778 }
9779
fa73b229 9780 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
ef416fc2 9781 {
9782 /*
9783 * Auto-type the file...
9784 */
9785
bd7854cb 9786 ipp_attribute_t *doc_name; /* document-name attribute */
9787
9788
75bd9771 9789 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Auto-typing file...");
ef416fc2 9790
bd7854cb 9791 doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
9792 filetype = mimeFileType(MimeDatabase, con->filename,
9793 doc_name ? doc_name->values[0].string.text : NULL,
9794 &compression);
ef416fc2 9795
f7deaa1a 9796 if (!filetype)
f7faf1f5 9797 filetype = mimeType(MimeDatabase, super, type);
a0f6818e 9798
0268488e
MS
9799 if (filetype)
9800 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.",
9801 filetype->super, filetype->type);
9514a192
MS
9802
9803 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super, filetype->type);
9804 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, mimetype);
ed486911 9805 }
f7faf1f5 9806 else
9807 filetype = mimeType(MimeDatabase, super, type);
9808
49d87452 9809 if (filetype)
f7deaa1a 9810 {
9811 /*
9812 * Replace the document-format attribute value with the auto-typed or
9813 * default one.
9814 */
9815
9816 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9817 filetype->type);
9818
49d87452
MS
9819 if ((jformat = ippFindAttribute(job->attrs, "document-format",
9820 IPP_TAG_MIMETYPE)) != NULL)
5e6c3df7 9821 ippSetString(job->attrs, &jformat, 0, mimetype);
f7deaa1a 9822 else
a0f6818e 9823 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
f7deaa1a 9824 "document-format", NULL, mimetype);
9825 }
9826 else if (!filetype)
ef416fc2 9827 {
9828 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
84315f46 9829 _("Unsupported document-format \"%s/%s\"."), super, type);
ef416fc2 9830 cupsdLogMessage(CUPSD_LOG_INFO,
9831 "Hint: Do you have the raw file printing rules enabled?");
9832
9833 if (format)
9834 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9835 "document-format", NULL, format->values[0].string.text);
9836
9837 return;
9838 }
9839
80ca4592 9840 if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
9841 {
9842 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
9843 filetype->type);
9844
9845 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
84315f46 9846 _("Unsupported document-format \"%s\"."), mimetype);
80ca4592 9847
9848 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
9849 "document-format", NULL, mimetype);
9850
9851 return;
9852 }
9853
ef416fc2 9854 /*
9855 * Add the file to the job...
9856 */
9857
9858 if (add_file(con, job, filetype, compression))
9859 return;
9860
9514a192
MS
9861 if ((attr = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME)) != NULL)
9862 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
9863
ef416fc2 9864 if (stat(con->filename, &fileinfo))
9865 kbytes = 0;
9866 else
9867 kbytes = (fileinfo.st_size + 1023) / 1024;
9868
9869 cupsdUpdateQuota(printer, job->username, 0, kbytes);
9870
f16ea703
MS
9871 job->koctets += kbytes;
9872
9873 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
ef416fc2 9874 attr->values[0].integer += kbytes;
9875
6d8021f4
MS
9876 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
9877 if (rename(con->filename, filename))
9878 {
9879 cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to rename job document file \"%s\": %s", filename, strerror(errno));
9880
9881 send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to rename job document file."));
9882 return;
9883 }
ef416fc2 9884
9885 cupsdClearString(&con->filename);
9886
75bd9771
MS
9887 cupsdLogJob(job, CUPSD_LOG_INFO, "File of type %s/%s queued by \"%s\".",
9888 filetype->super, filetype->type, job->username);
ef416fc2 9889
9890 /*
9891 * Start the job if this is the last document...
9892 */
9893
6d2f911b
MS
9894 last_document:
9895
fa73b229 9896 if ((attr = ippFindAttribute(con->request, "last-document",
9897 IPP_TAG_BOOLEAN)) != NULL &&
ef416fc2 9898 attr->values[0].boolean)
9899 {
9900 /*
9901 * See if we need to add the ending sheet...
9902 */
9903
91c84a35
MS
9904 if (cupsdTimeoutJob(job))
9905 return;
ef416fc2 9906
bd7854cb 9907 if (job->state_value == IPP_JOB_STOPPED)
9908 {
ef416fc2 9909 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 9910 job->state_value = IPP_JOB_PENDING;
12f89d24
MS
9911
9912 ippSetString(job->attrs, &job->reasons, 0, "none");
bd7854cb 9913 }
9914 else if (job->state_value == IPP_JOB_HELD)
ef416fc2 9915 {
fa73b229 9916 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9917 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 9918 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9919
fa73b229 9920 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
bd7854cb 9921 {
ef416fc2 9922 job->state->values[0].integer = IPP_JOB_PENDING;
bd7854cb 9923 job->state_value = IPP_JOB_PENDING;
12f89d24
MS
9924
9925 ippSetString(job->attrs, &job->reasons, 0, "none");
bd7854cb 9926 }
12f89d24
MS
9927 else
9928 ippSetString(job->attrs, &job->reasons, 0, "job-hold-until-specified");
ef416fc2 9929 }
9930
3dfe78b3
MS
9931 job->dirty = 1;
9932 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 9933
75bd9771 9934 start_job = 1;
ef416fc2 9935 }
9936 else
9937 {
fa73b229 9938 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9939 IPP_TAG_KEYWORD)) == NULL)
ef416fc2 9940 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9941
fa73b229 9942 if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
ef416fc2 9943 {
9944 job->state->values[0].integer = IPP_JOB_HELD;
bd7854cb 9945 job->state_value = IPP_JOB_HELD;
dfd5680b 9946 job->hold_until = time(NULL) + MultipleOperationTimeout;
3dfe78b3 9947
12f89d24
MS
9948 ippSetString(job->attrs, &job->reasons, 0, "job-incoming");
9949
9950 job->dirty = 1;
3dfe78b3 9951 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 9952 }
75bd9771
MS
9953
9954 start_job = 0;
ef416fc2 9955 }
9956
9957 /*
9958 * Fill in the response info...
9959 */
9960
83e08001 9961 httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
f2534050 9962 con->clientname, con->clientport, "/jobs/%d", jobid);
ef416fc2 9963 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
9964 job_uri);
9965
9966 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
9967
9968 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
75bd9771 9969 job->state_value);
12f89d24
MS
9970 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons",
9971 NULL, job->reasons->values[0].string.text);
ef416fc2 9972
9973 con->response->request.status.status_code = IPP_OK;
75bd9771
MS
9974
9975 /*
9976 * Start the job if necessary...
9977 */
9978
9979 if (start_job)
9980 cupsdCheckJobs();
ef416fc2 9981}
9982
9983
9984/*
9985 * 'send_http_error()' - Send a HTTP error back to the IPP client.
9986 */
9987
9988static void
f899b121 9989send_http_error(
9990 cupsd_client_t *con, /* I - Client connection */
9991 http_status_t status, /* I - HTTP status code */
9992 cupsd_printer_t *printer) /* I - Printer, if any */
ef416fc2 9993{
9f5eb9be
MS
9994 ipp_attribute_t *uri; /* Request URI, if any */
9995
9996
9997 if ((uri = ippFindAttribute(con->request, "printer-uri",
9998 IPP_TAG_URI)) == NULL)
9999 uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI);
10000
10001 cupsdLogMessage(status == HTTP_FORBIDDEN ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
21f36711 10002 "[Client %d] Returning HTTP %s for %s (%s) from %s",
996acce8 10003 con->number, httpStatus(status),
321d8d57
MS
10004 con->request ?
10005 ippOpString(con->request->request.op.operation_id) :
10006 "no operation-id",
9f5eb9be 10007 uri ? uri->values[0].string.text : "no URI",
996acce8 10008 con->http->hostname);
ef416fc2 10009
5f64df29
MS
10010 if (printer)
10011 {
db0bd74a 10012 int auth_type; /* Type of authentication required */
2fb76298
MS
10013
10014
5f64df29
MS
10015 auth_type = CUPSD_AUTH_NONE;
10016
10017 if (status == HTTP_UNAUTHORIZED &&
10018 printer->num_auth_info_required > 0 &&
10019 !strcmp(printer->auth_info_required[0], "negotiate") &&
10020 con->request &&
10021 (con->request->request.op.operation_id == IPP_PRINT_JOB ||
10022 con->request->request.op.operation_id == IPP_CREATE_JOB ||
10023 con->request->request.op.operation_id == CUPS_AUTHENTICATE_JOB))
10024 {
10025 /*
10026 * Creating and authenticating jobs requires Kerberos...
10027 */
10028
10029 auth_type = CUPSD_AUTH_NEGOTIATE;
10030 }
db0bd74a 10031 else
5f64df29
MS
10032 {
10033 /*
10034 * Use policy/location-defined authentication requirements...
10035 */
10036
10037 char resource[HTTP_MAX_URI]; /* Resource portion of URI */
10038 cupsd_location_t *auth; /* Pointer to authentication element */
10039
10040
10041 if (printer->type & CUPS_PRINTER_CLASS)
10042 snprintf(resource, sizeof(resource), "/classes/%s", printer->name);
10043 else
10044 snprintf(resource, sizeof(resource), "/printers/%s", printer->name);
10045
10046 if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
10047 auth->type == CUPSD_AUTH_NONE)
10048 auth = cupsdFindPolicyOp(printer->op_policy_ptr,
10049 con->request ?
10050 con->request->request.op.operation_id :
10051 IPP_PRINT_JOB);
10052
10053 if (auth)
10054 {
10055 if (auth->type == CUPSD_AUTH_DEFAULT)
dcb445bc 10056 auth_type = cupsdDefaultAuthType();
5f64df29
MS
10057 else
10058 auth_type = auth->type;
10059 }
10060 }
2fb76298 10061
db0bd74a 10062 cupsdSendError(con, status, auth_type);
2fb76298 10063 }
f899b121 10064 else
5bd77a73 10065 cupsdSendError(con, status, CUPSD_AUTH_NONE);
ef416fc2 10066
10067 ippDelete(con->response);
10068 con->response = NULL;
10069
10070 return;
10071}
10072
10073
10074/*
10075 * 'send_ipp_status()' - Send a status back to the IPP client.
10076 */
10077
10078static void
10079send_ipp_status(cupsd_client_t *con, /* I - Client connection */
1f0275e3
MS
10080 ipp_status_t status, /* I - IPP status code */
10081 const char *message,/* I - Status message */
10082 ...) /* I - Additional args as needed */
ef416fc2 10083{
10084 va_list ap; /* Pointer to additional args */
10085 char formatted[1024]; /* Formatted errror message */
10086
10087
3d8365b8 10088 va_start(ap, message);
10089 vsnprintf(formatted, sizeof(formatted),
10090 _cupsLangString(con->language, message), ap);
10091 va_end(ap);
ef416fc2 10092
3d8365b8 10093 cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
10094 ippOpString(con->request->request.op.operation_id),
10095 ippErrorString(status), formatted);
ef416fc2 10096
10097 con->response->request.status.status_code = status;
10098
fa73b229 10099 if (ippFindAttribute(con->response, "attributes-charset",
10100 IPP_TAG_ZERO) == NULL)
ef416fc2 10101 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
f8b3a85b 10102 "attributes-charset", NULL, "utf-8");
ef416fc2 10103
10104 if (ippFindAttribute(con->response, "attributes-natural-language",
10105 IPP_TAG_ZERO) == NULL)
10106 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
10107 "attributes-natural-language", NULL, DefaultLanguage);
10108
3d8365b8 10109 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
10110 "status-message", NULL, formatted);
ef416fc2 10111}
10112
10113
10114/*
10115 * 'set_default()' - Set the default destination...
10116 */
10117
10118static void
10119set_default(cupsd_client_t *con, /* I - Client connection */
10120 ipp_attribute_t *uri) /* I - Printer URI */
10121{
10122 http_status_t status; /* Policy status */
bc44d920 10123 cups_ptype_t dtype; /* Destination type (printer/class) */
dd1abb6b
MS
10124 cupsd_printer_t *printer, /* Printer */
10125 *oldprinter; /* Old default printer */
ef416fc2 10126
10127
10128 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
996acce8 10129 con->number, uri->values[0].string.text);
ef416fc2 10130
10131 /*
10132 * Is the destination valid?
10133 */
10134
f7deaa1a 10135 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 10136 {
10137 /*
10138 * Bad URI...
10139 */
10140
10141 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 10142 _("The printer or class does not exist."));
ef416fc2 10143 return;
10144 }
10145
10146 /*
10147 * Check policy...
10148 */
10149
10150 if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
10151 {
f899b121 10152 send_http_error(con, status, NULL);
ef416fc2 10153 return;
10154 }
10155
10156 /*
10157 * Set it as the default...
10158 */
10159
dd1abb6b 10160 oldprinter = DefaultPrinter;
ef416fc2 10161 DefaultPrinter = printer;
10162
dd1abb6b
MS
10163 if (oldprinter)
10164 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, oldprinter, NULL,
10165 "%s is no longer the default printer.", oldprinter->name);
10166
10167 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
10168 "%s is now the default printer.", printer->name);
10169
3dfe78b3 10170 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS | CUPSD_DIRTY_CLASSES |
a2326b5b 10171 CUPSD_DIRTY_PRINTCAP);
ef416fc2 10172
10173 cupsdLogMessage(CUPSD_LOG_INFO,
f7deaa1a 10174 "Default destination set to \"%s\" by \"%s\".",
10175 printer->name, get_username(con));
ef416fc2 10176
10177 /*
10178 * Everything was ok, so return OK status...
10179 */
10180
10181 con->response->request.status.status_code = IPP_OK;
10182}
10183
10184
10185/*
10186 * 'set_job_attrs()' - Set job attributes.
10187 */
10188
10189static void
10190set_job_attrs(cupsd_client_t *con, /* I - Client connection */
10191 ipp_attribute_t *uri) /* I - Job URI */
10192{
10193 ipp_attribute_t *attr, /* Current attribute */
10194 *attr2; /* Job attribute */
10195 int jobid; /* Job ID */
10196 cupsd_job_t *job; /* Current job */
61cf44e2 10197 char scheme[HTTP_MAX_URI],
ef416fc2 10198 /* Method portion of URI */
10199 username[HTTP_MAX_URI],
10200 /* Username portion of URI */
10201 host[HTTP_MAX_URI],
10202 /* Host portion of URI */
10203 resource[HTTP_MAX_URI];
10204 /* Resource portion of URI */
10205 int port; /* Port portion of URI */
d09495fa 10206 int event; /* Events? */
005dd1eb 10207 int check_jobs; /* Check jobs? */
ef416fc2 10208
10209
10210 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
996acce8 10211 con->number, uri->values[0].string.text);
ef416fc2 10212
10213 /*
10214 * Start with "everything is OK" status...
10215 */
10216
10217 con->response->request.status.status_code = IPP_OK;
10218
10219 /*
10220 * See if we have a job URI or a printer URI...
10221 */
10222
fa73b229 10223 if (!strcmp(uri->name, "printer-uri"))
ef416fc2 10224 {
10225 /*
10226 * Got a printer URI; see if we also have a job-id attribute...
10227 */
10228
fa73b229 10229 if ((attr = ippFindAttribute(con->request, "job-id",
10230 IPP_TAG_INTEGER)) == NULL)
ef416fc2 10231 {
10232 send_ipp_status(con, IPP_BAD_REQUEST,
84315f46 10233 _("Got a printer-uri attribute but no job-id."));
ef416fc2 10234 return;
10235 }
10236
10237 jobid = attr->values[0].integer;
10238 }
10239 else
10240 {
10241 /*
10242 * Got a job URI; parse it to get the job ID...
10243 */
10244
61cf44e2
MS
10245 httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
10246 sizeof(scheme), username, sizeof(username), host,
a4d04587 10247 sizeof(host), &port, resource, sizeof(resource));
ef416fc2 10248
fa73b229 10249 if (strncmp(resource, "/jobs/", 6))
ef416fc2 10250 {
10251 /*
10252 * Not a valid URI!
10253 */
10254
84315f46 10255 send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
ef416fc2 10256 uri->values[0].string.text);
10257 return;
10258 }
10259
10260 jobid = atoi(resource + 6);
10261 }
10262
10263 /*
10264 * See if the job exists...
10265 */
10266
10267 if ((job = cupsdFindJob(jobid)) == NULL)
10268 {
10269 /*
10270 * Nope - return a "not found" error...
10271 */
10272
84315f46 10273 send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
ef416fc2 10274 return;
10275 }
10276
10277 /*
10278 * See if the job has been completed...
10279 */
10280
bd7854cb 10281 if (job->state_value > IPP_JOB_STOPPED)
ef416fc2 10282 {
10283 /*
10284 * Return a "not-possible" error...
10285 */
10286
10287 send_ipp_status(con, IPP_NOT_POSSIBLE,
84315f46 10288 _("Job #%d is finished and cannot be altered."), jobid);
ef416fc2 10289 return;
10290 }
10291
10292 /*
10293 * See if the job is owned by the requesting user...
10294 */
10295
10296 if (!validate_user(job, con, job->username, username, sizeof(username)))
10297 {
b0f6947b
MS
10298 send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
10299 cupsdFindDest(job->dest));
ef416fc2 10300 return;
10301 }
10302
10303 /*
10304 * See what the user wants to change.
10305 */
10306
bd7854cb 10307 cupsdLoadJob(job);
10308
005dd1eb
MS
10309 check_jobs = 0;
10310 event = 0;
d09495fa 10311
fa73b229 10312 for (attr = con->request->attrs; attr; attr = attr->next)
ef416fc2 10313 {
10314 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
10315 continue;
10316
10317 if (!strcmp(attr->name, "attributes-charset") ||
10318 !strcmp(attr->name, "attributes-natural-language") ||
9514a192
MS
10319 !strncmp(attr->name, "date-time-at-", 13) ||
10320 !strncmp(attr->name, "document-compression", 20) ||
10321 !strncmp(attr->name, "document-format", 15) ||
ef416fc2 10322 !strcmp(attr->name, "job-detailed-status-messages") ||
10323 !strcmp(attr->name, "job-document-access-errors") ||
10324 !strcmp(attr->name, "job-id") ||
5d6412a9 10325 !strcmp(attr->name, "job-impressions-completed") ||
9514a192
MS
10326 !strcmp(attr->name, "job-k-octets-completed") ||
10327 !strcmp(attr->name, "job-media-sheets-completed") ||
ef416fc2 10328 !strcmp(attr->name, "job-originating-host-name") ||
10329 !strcmp(attr->name, "job-originating-user-name") ||
9514a192 10330 !strcmp(attr->name, "job-pages-completed") ||
ef416fc2 10331 !strcmp(attr->name, "job-printer-up-time") ||
10332 !strcmp(attr->name, "job-printer-uri") ||
10333 !strcmp(attr->name, "job-sheets") ||
10334 !strcmp(attr->name, "job-state-message") ||
10335 !strcmp(attr->name, "job-state-reasons") ||
10336 !strcmp(attr->name, "job-uri") ||
10337 !strcmp(attr->name, "number-of-documents") ||
10338 !strcmp(attr->name, "number-of-intervening-jobs") ||
10339 !strcmp(attr->name, "output-device-assigned") ||
ef416fc2 10340 !strncmp(attr->name, "time-at-", 8))
10341 {
10342 /*
10343 * Read-only attrs!
10344 */
10345
10346 send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
10347 _("%s cannot be changed."), attr->name);
10348
a2326b5b
MS
10349 attr2 = ippCopyAttribute(con->response, attr, 0);
10350 ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
ef416fc2 10351 continue;
10352 }
10353
4cadd620
MS
10354 if (!ippValidateAttribute(attr))
10355 {
10356 send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Bad '%s' value."), attr->name);
10357 ippCopyAttribute(con->response, attr, 0);
10358 return;
10359 }
10360
10361 if (!strcmp(attr->name, "job-hold-until"))
10362 {
10363 const char *when = ippGetString(attr, 0, NULL);
10364 /* job-hold-until value */
10365
10366 if ((ippGetValueTag(attr) != IPP_TAG_KEYWORD && ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG) || ippGetCount(attr) != 1)
10367 {
10368 send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Unsupported 'job-hold-until' value."));
10369 ippCopyAttribute(con->response, attr, 0);
10370 return;
10371 }
10372
10373 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s", when);
10374 cupsdSetJobHoldUntil(job, when, 0);
10375
10376 if (!strcmp(when, "no-hold"))
10377 {
10378 cupsdReleaseJob(job);
10379 check_jobs = 1;
10380 }
10381 else
10382 cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, "Job held by \"%s\".", username);
10383
10384 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
10385 }
10386 else if (!strcmp(attr->name, "job-priority"))
ef416fc2 10387 {
10388 /*
10389 * Change the job priority...
10390 */
10391
10392 if (attr->value_tag != IPP_TAG_INTEGER)
10393 {
84315f46 10394 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value."));
ef416fc2 10395
a2326b5b
MS
10396 attr2 = ippCopyAttribute(con->response, attr, 0);
10397 ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
ef416fc2 10398 }
bd7854cb 10399 else if (job->state_value >= IPP_JOB_PROCESSING)
ef416fc2 10400 {
10401 send_ipp_status(con, IPP_NOT_POSSIBLE,
10402 _("Job is completed and cannot be changed."));
10403 return;
10404 }
10405 else if (con->response->request.status.status_code == IPP_OK)
d09495fa 10406 {
005dd1eb
MS
10407 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-priority to %d",
10408 attr->values[0].integer);
ef416fc2 10409 cupsdSetJobPriority(job, attr->values[0].integer);
005dd1eb
MS
10410
10411 check_jobs = 1;
10412 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED |
10413 CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED;
d09495fa 10414 }
ef416fc2 10415 }
10416 else if (!strcmp(attr->name, "job-state"))
10417 {
10418 /*
10419 * Change the job state...
10420 */
10421
10422 if (attr->value_tag != IPP_TAG_ENUM)
10423 {
84315f46 10424 send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value."));
ef416fc2 10425
a2326b5b
MS
10426 attr2 = ippCopyAttribute(con->response, attr, 0);
10427 ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
ef416fc2 10428 }
10429 else
10430 {
10431 switch (attr->values[0].integer)
10432 {
10433 case IPP_JOB_PENDING :
10434 case IPP_JOB_HELD :
bd7854cb 10435 if (job->state_value > IPP_JOB_HELD)
ef416fc2 10436 {
10437 send_ipp_status(con, IPP_NOT_POSSIBLE,
10438 _("Job state cannot be changed."));
10439 return;
10440 }
10441 else if (con->response->request.status.status_code == IPP_OK)
bd7854cb 10442 {
005dd1eb
MS
10443 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
10444 attr->values[0].integer);
7e86f2f6 10445 cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer, CUPSD_JOB_DEFAULT, "Job state changed by \"%s\"", username);
005dd1eb 10446 check_jobs = 1;
bd7854cb 10447 }
ef416fc2 10448 break;
10449
10450 case IPP_JOB_PROCESSING :
10451 case IPP_JOB_STOPPED :
bd7854cb 10452 if (job->state_value != attr->values[0].integer)
ef416fc2 10453 {
10454 send_ipp_status(con, IPP_NOT_POSSIBLE,
10455 _("Job state cannot be changed."));
10456 return;
10457 }
10458 break;
10459
d09495fa 10460 case IPP_JOB_CANCELED :
ef416fc2 10461 case IPP_JOB_ABORTED :
10462 case IPP_JOB_COMPLETED :
bd7854cb 10463 if (job->state_value > IPP_JOB_PROCESSING)
ef416fc2 10464 {
10465 send_ipp_status(con, IPP_NOT_POSSIBLE,
10466 _("Job state cannot be changed."));
10467 return;
10468 }
10469 else if (con->response->request.status.status_code == IPP_OK)
005dd1eb
MS
10470 {
10471 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
10472 attr->values[0].integer);
b9faaae1
MS
10473 cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer,
10474 CUPSD_JOB_DEFAULT,
10475 "Job state changed by \"%s\"", username);
005dd1eb
MS
10476 check_jobs = 1;
10477 }
ef416fc2 10478 break;
10479 }
10480 }
10481 }
10482 else if (con->response->request.status.status_code != IPP_OK)
10483 continue;
fa73b229 10484 else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10485 IPP_TAG_ZERO)) != NULL)
ef416fc2 10486 {
10487 /*
10488 * Some other value; first free the old value...
10489 */
10490
10491 if (job->attrs->prev)
10492 job->attrs->prev->next = attr2->next;
10493 else
10494 job->attrs->attrs = attr2->next;
10495
10496 if (job->attrs->last == attr2)
10497 job->attrs->last = job->attrs->prev;
10498
a2326b5b 10499 ippDeleteAttribute(NULL, attr2);
ef416fc2 10500
10501 /*
10502 * Then copy the attribute...
10503 */
10504
a2326b5b 10505 ippCopyAttribute(job->attrs, attr, 0);
ef416fc2 10506 }
10507 else if (attr->value_tag == IPP_TAG_DELETEATTR)
10508 {
10509 /*
10510 * Delete the attribute...
10511 */
10512
10513 if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10514 IPP_TAG_ZERO)) != NULL)
10515 {
10516 if (job->attrs->prev)
10517 job->attrs->prev->next = attr2->next;
10518 else
10519 job->attrs->attrs = attr2->next;
10520
10521 if (attr2 == job->attrs->last)
10522 job->attrs->last = job->attrs->prev;
10523
a2326b5b 10524 ippDeleteAttribute(NULL, attr2);
d09495fa 10525
10526 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
ef416fc2 10527 }
10528 }
10529 else
10530 {
10531 /*
10532 * Add new option by copying it...
10533 */
10534
a2326b5b 10535 ippCopyAttribute(job->attrs, attr, 0);
d09495fa 10536
10537 event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
ef416fc2 10538 }
10539 }
10540
10541 /*
10542 * Save the job...
10543 */
10544
3dfe78b3
MS
10545 job->dirty = 1;
10546 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 10547
d09495fa 10548 /*
10549 * Send events as needed...
10550 */
10551
d9bca400 10552 if (event & CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED)
01ce6322
MS
10553 cupsdAddEvent(CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED,
10554 cupsdFindDest(job->dest), job,
d9bca400
MS
10555 "Job priority changed by user.");
10556
d09495fa 10557 if (event & CUPSD_EVENT_JOB_STATE)
01ce6322 10558 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
d09495fa 10559 job->state_value == IPP_JOB_HELD ?
10560 "Job held by user." : "Job restarted by user.");
10561
10562 if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
01ce6322 10563 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
d09495fa 10564 "Job options changed by user.");
10565
ef416fc2 10566 /*
10567 * Start jobs if possible...
10568 */
10569
005dd1eb
MS
10570 if (check_jobs)
10571 cupsdCheckJobs();
ef416fc2 10572}
10573
10574
c168a833
MS
10575/*
10576 * 'set_printer_attrs()' - Set printer attributes.
10577 */
10578
10579static void
10580set_printer_attrs(cupsd_client_t *con, /* I - Client connection */
10581 ipp_attribute_t *uri) /* I - Printer */
10582{
10583 http_status_t status; /* Policy status */
10584 cups_ptype_t dtype; /* Destination type (printer/class) */
10585 cupsd_printer_t *printer; /* Printer/class */
10586 ipp_attribute_t *attr; /* Printer attribute */
bf3816c7 10587 int changed = 0; /* Was anything changed? */
c168a833
MS
10588
10589
10590 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_attrs(%p[%d], %s)", con,
996acce8 10591 con->number, uri->values[0].string.text);
c168a833
MS
10592
10593 /*
10594 * Is the destination valid?
10595 */
10596
10597 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10598 {
10599 /*
10600 * Bad URI...
10601 */
10602
10603 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 10604 _("The printer or class does not exist."));
c168a833
MS
10605 return;
10606 }
10607
10608 /*
10609 * Check policy...
10610 */
10611
10612 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10613 {
10614 send_http_error(con, status, printer);
10615 return;
10616 }
10617
10618 /*
10619 * Return a list of attributes that can be set via Set-Printer-Attributes.
10620 */
10621
10622 if ((attr = ippFindAttribute(con->request, "printer-location",
10623 IPP_TAG_TEXT)) != NULL)
10624 {
10625 cupsdSetString(&printer->location, attr->values[0].string.text);
10626 changed = 1;
10627 }
10628
9b4bd602
MS
10629 if ((attr = ippFindAttribute(con->request, "printer-geo-location", IPP_TAG_URI)) != NULL && !strncmp(attr->values[0].string.text, "geo:", 4))
10630 {
10631 cupsdSetString(&printer->geo_location, attr->values[0].string.text);
10632 changed = 1;
10633 }
10634
10635 if ((attr = ippFindAttribute(con->request, "printer-organization", IPP_TAG_TEXT)) != NULL)
10636 {
10637 cupsdSetString(&printer->organization, attr->values[0].string.text);
10638 changed = 1;
10639 }
10640
10641 if ((attr = ippFindAttribute(con->request, "printer-organizational-unit", IPP_TAG_TEXT)) != NULL)
10642 {
10643 cupsdSetString(&printer->organizational_unit, attr->values[0].string.text);
10644 changed = 1;
10645 }
10646
c168a833
MS
10647 if ((attr = ippFindAttribute(con->request, "printer-info",
10648 IPP_TAG_TEXT)) != NULL)
10649 {
10650 cupsdSetString(&printer->info, attr->values[0].string.text);
10651 changed = 1;
10652 }
10653
10654 /*
10655 * Update the printer attributes and return...
10656 */
10657
10658 if (changed)
10659 {
9b4bd602
MS
10660 printer->config_time = time(NULL);
10661
c168a833
MS
10662 cupsdSetPrinterAttrs(printer);
10663 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
10664
10665 cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL,
10666 "Printer \"%s\" description or location changed by \"%s\".",
10667 printer->name, get_username(con));
10668
10669 cupsdLogMessage(CUPSD_LOG_INFO,
10670 "Printer \"%s\" description or location changed by \"%s\".",
10671 printer->name, get_username(con));
10672 }
10673
10674 con->response->request.status.status_code = IPP_OK;
10675}
10676
10677
b423cd4c 10678/*
10679 * 'set_printer_defaults()' - Set printer default options from a request.
10680 */
10681
f0b589f7 10682static int /* O - 1 on success, 0 on failure */
b423cd4c 10683set_printer_defaults(
10684 cupsd_client_t *con, /* I - Client connection */
10685 cupsd_printer_t *printer) /* I - Printer */
10686{
10687 int i; /* Looping var */
10688 ipp_attribute_t *attr; /* Current attribute */
7e86f2f6 10689 size_t namelen; /* Length of attribute name */
b423cd4c 10690 char name[256], /* New attribute name */
10691 value[256]; /* String version of integer attrs */
10692
10693
10694 for (attr = con->request->attrs; attr; attr = attr->next)
10695 {
10696 /*
10697 * Skip non-printer attributes...
10698 */
10699
10700 if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
10701 continue;
10702
10703 cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
10704
10705 if (!strcmp(attr->name, "job-sheets-default"))
10706 {
10707 /*
10708 * Only allow keywords and names...
10709 */
10710
10711 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10712 continue;
10713
10714 /*
10715 * Only allow job-sheets-default to be set when running without a
10716 * system high classification level...
10717 */
10718
10719 if (Classification)
10720 continue;
10721
10722 cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
10723
10724 if (attr->num_values > 1)
10725 cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
10726 else
10727 cupsdSetString(&printer->job_sheets[1], "none");
10728 }
10729 else if (!strcmp(attr->name, "requesting-user-name-allowed"))
10730 {
10d09e33 10731 cupsdFreeStrings(&(printer->users));
b423cd4c 10732
10733 printer->deny_users = 0;
10734
10735 if (attr->value_tag == IPP_TAG_NAME &&
10736 (attr->num_values > 1 ||
10737 strcmp(attr->values[0].string.text, "all")))
10738 {
10739 for (i = 0; i < attr->num_values; i ++)
10d09e33 10740 cupsdAddString(&(printer->users), attr->values[i].string.text);
b423cd4c 10741 }
10742 }
10743 else if (!strcmp(attr->name, "requesting-user-name-denied"))
10744 {
10d09e33 10745 cupsdFreeStrings(&(printer->users));
b423cd4c 10746
10747 printer->deny_users = 1;
10748
10749 if (attr->value_tag == IPP_TAG_NAME &&
10750 (attr->num_values > 1 ||
10751 strcmp(attr->values[0].string.text, "none")))
10752 {
10753 for (i = 0; i < attr->num_values; i ++)
10d09e33 10754 cupsdAddString(&(printer->users), attr->values[i].string.text);
b423cd4c 10755 }
10756 }
10757 else if (!strcmp(attr->name, "job-quota-period"))
10758 {
10759 if (attr->value_tag != IPP_TAG_INTEGER)
10760 continue;
10761
10762 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
10763 attr->values[0].integer);
10764 cupsdFreeQuotas(printer);
10765
10766 printer->quota_period = attr->values[0].integer;
10767 }
10768 else if (!strcmp(attr->name, "job-k-limit"))
10769 {
10770 if (attr->value_tag != IPP_TAG_INTEGER)
10771 continue;
10772
10773 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
10774 attr->values[0].integer);
10775 cupsdFreeQuotas(printer);
10776
10777 printer->k_limit = attr->values[0].integer;
10778 }
10779 else if (!strcmp(attr->name, "job-page-limit"))
10780 {
10781 if (attr->value_tag != IPP_TAG_INTEGER)
10782 continue;
10783
10784 cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
10785 attr->values[0].integer);
10786 cupsdFreeQuotas(printer);
10787
10788 printer->page_limit = attr->values[0].integer;
10789 }
10790 else if (!strcmp(attr->name, "printer-op-policy"))
10791 {
10792 cupsd_policy_t *p; /* Policy */
10793
10794
10795 if (attr->value_tag != IPP_TAG_NAME)
10796 continue;
10797
10798 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
10799 {
10800 cupsdLogMessage(CUPSD_LOG_DEBUG,
10801 "Setting printer-op-policy to \"%s\"...",
10802 attr->values[0].string.text);
10803 cupsdSetString(&printer->op_policy, attr->values[0].string.text);
10804 printer->op_policy_ptr = p;
10805 }
10806 else
10807 {
10808 send_ipp_status(con, IPP_NOT_POSSIBLE,
10809 _("Unknown printer-op-policy \"%s\"."),
10810 attr->values[0].string.text);
f0b589f7 10811 return (0);
b423cd4c 10812 }
10813 }
10814 else if (!strcmp(attr->name, "printer-error-policy"))
10815 {
10816 if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
10817 continue;
10818
f11a948a 10819 if (strcmp(attr->values[0].string.text, "retry-current-job") &&
a2326b5b 10820 ((printer->type & CUPS_PRINTER_CLASS) ||
f11a948a
MS
10821 (strcmp(attr->values[0].string.text, "abort-job") &&
10822 strcmp(attr->values[0].string.text, "retry-job") &&
10823 strcmp(attr->values[0].string.text, "stop-printer"))))
b423cd4c 10824 {
10825 send_ipp_status(con, IPP_NOT_POSSIBLE,
10826 _("Unknown printer-error-policy \"%s\"."),
10827 attr->values[0].string.text);
f0b589f7 10828 return (0);
b423cd4c 10829 }
10830
10831 cupsdLogMessage(CUPSD_LOG_DEBUG,
10832 "Setting printer-error-policy to \"%s\"...",
10833 attr->values[0].string.text);
10834 cupsdSetString(&printer->error_policy, attr->values[0].string.text);
10835 }
b423cd4c 10836
10837 /*
10838 * Skip any other non-default attributes...
10839 */
10840
10841 namelen = strlen(attr->name);
10842 if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
10843 namelen > (sizeof(name) - 1) || attr->num_values != 1)
10844 continue;
10845
10846 /*
10847 * OK, anything else must be a user-defined default...
10848 */
10849
10850 strlcpy(name, attr->name, sizeof(name));
10851 name[namelen - 8] = '\0'; /* Strip "-default" */
10852
10853 switch (attr->value_tag)
10854 {
10855 case IPP_TAG_DELETEATTR :
10856 printer->num_options = cupsRemoveOption(name,
10857 printer->num_options,
10858 &(printer->options));
10859 cupsdLogMessage(CUPSD_LOG_DEBUG,
10860 "Deleting %s", attr->name);
10861 break;
10862
10863 case IPP_TAG_NAME :
5a9febac 10864 case IPP_TAG_TEXT :
b423cd4c 10865 case IPP_TAG_KEYWORD :
10866 case IPP_TAG_URI :
10867 printer->num_options = cupsAddOption(name,
10868 attr->values[0].string.text,
10869 printer->num_options,
10870 &(printer->options));
10871 cupsdLogMessage(CUPSD_LOG_DEBUG,
10872 "Setting %s to \"%s\"...", attr->name,
10873 attr->values[0].string.text);
10874 break;
10875
10876 case IPP_TAG_BOOLEAN :
10877 printer->num_options = cupsAddOption(name,
10878 attr->values[0].boolean ?
10879 "true" : "false",
10880 printer->num_options,
10881 &(printer->options));
10882 cupsdLogMessage(CUPSD_LOG_DEBUG,
10883 "Setting %s to %s...", attr->name,
10884 attr->values[0].boolean ? "true" : "false");
10885 break;
10886
10887 case IPP_TAG_INTEGER :
10888 case IPP_TAG_ENUM :
10889 sprintf(value, "%d", attr->values[0].integer);
10890 printer->num_options = cupsAddOption(name, value,
10891 printer->num_options,
10892 &(printer->options));
10893 cupsdLogMessage(CUPSD_LOG_DEBUG,
10894 "Setting %s to %s...", attr->name, value);
10895 break;
10896
10897 case IPP_TAG_RANGE :
10898 sprintf(value, "%d-%d", attr->values[0].range.lower,
10899 attr->values[0].range.upper);
10900 printer->num_options = cupsAddOption(name, value,
10901 printer->num_options,
10902 &(printer->options));
10903 cupsdLogMessage(CUPSD_LOG_DEBUG,
10904 "Setting %s to %s...", attr->name, value);
10905 break;
10906
10907 case IPP_TAG_RESOLUTION :
10908 sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
10909 attr->values[0].resolution.yres,
10910 attr->values[0].resolution.units == IPP_RES_PER_INCH ?
3e7fe0ca 10911 "dpi" : "dpcm");
b423cd4c 10912 printer->num_options = cupsAddOption(name, value,
10913 printer->num_options,
10914 &(printer->options));
10915 cupsdLogMessage(CUPSD_LOG_DEBUG,
10916 "Setting %s to %s...", attr->name, value);
10917 break;
10918
10919 default :
10920 /* Do nothing for other values */
10921 break;
10922 }
10923 }
f0b589f7
MS
10924
10925 return (1);
b423cd4c 10926}
10927
10928
ef416fc2 10929/*
10930 * 'start_printer()' - Start a printer.
10931 */
10932
10933static void
10934start_printer(cupsd_client_t *con, /* I - Client connection */
10935 ipp_attribute_t *uri) /* I - Printer URI */
10936{
10d09e33 10937 int i; /* Temporary variable */
ef416fc2 10938 http_status_t status; /* Policy status */
bc44d920 10939 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 10940 cupsd_printer_t *printer; /* Printer data */
10941
10942
10943 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
996acce8 10944 con->number, uri->values[0].string.text);
ef416fc2 10945
10946 /*
10947 * Is the destination valid?
10948 */
10949
f7deaa1a 10950 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 10951 {
10952 /*
10953 * Bad URI...
10954 */
10955
10956 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 10957 _("The printer or class does not exist."));
ef416fc2 10958 return;
10959 }
10960
10961 /*
10962 * Check policy...
10963 */
10964
10965 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10966 {
f899b121 10967 send_http_error(con, status, printer);
ef416fc2 10968 return;
10969 }
10970
10971 /*
10972 * Start the printer...
10973 */
10974
10975 printer->state_message[0] = '\0';
10976
10977 cupsdStartPrinter(printer, 1);
10978
10979 if (dtype & CUPS_PRINTER_CLASS)
f7deaa1a 10980 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".",
10981 printer->name, get_username(con));
e00b005a 10982 else
f7deaa1a 10983 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".",
10984 printer->name, get_username(con));
ef416fc2 10985
10986 cupsdCheckJobs();
10987
10d09e33
MS
10988 /*
10989 * Check quotas...
10990 */
10991
10992 if ((i = check_quotas(con, printer)) < 0)
10993 {
10994 send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
10995 return;
10996 }
10997 else if (i == 0)
10998 {
10999 send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print."));
11000 return;
11001 }
11002
ef416fc2 11003 /*
11004 * Everything was ok, so return OK status...
11005 */
11006
11007 con->response->request.status.status_code = IPP_OK;
11008}
11009
11010
11011/*
11012 * 'stop_printer()' - Stop a printer.
11013 */
11014
11015static void
11016stop_printer(cupsd_client_t *con, /* I - Client connection */
11017 ipp_attribute_t *uri) /* I - Printer URI */
11018{
11019 http_status_t status; /* Policy status */
bc44d920 11020 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 11021 cupsd_printer_t *printer; /* Printer data */
11022 ipp_attribute_t *attr; /* printer-state-message attribute */
11023
11024
11025 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
996acce8 11026 con->number, uri->values[0].string.text);
ef416fc2 11027
11028 /*
11029 * Is the destination valid?
11030 */
11031
f7deaa1a 11032 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 11033 {
11034 /*
11035 * Bad URI...
11036 */
11037
11038 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 11039 _("The printer or class does not exist."));
ef416fc2 11040 return;
11041 }
11042
11043 /*
11044 * Check policy...
11045 */
11046
11047 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11048 {
f899b121 11049 send_http_error(con, status, printer);
ef416fc2 11050 return;
11051 }
11052
11053 /*
11054 * Stop the printer...
11055 */
11056
11057 if ((attr = ippFindAttribute(con->request, "printer-state-message",
11058 IPP_TAG_TEXT)) == NULL)
5a9febac 11059 strlcpy(printer->state_message, "Paused", sizeof(printer->state_message));
ef416fc2 11060 else
11061 {
11062 strlcpy(printer->state_message, attr->values[0].string.text,
11063 sizeof(printer->state_message));
11064 }
11065
11066 cupsdStopPrinter(printer, 1);
11067
11068 if (dtype & CUPS_PRINTER_CLASS)
f7deaa1a 11069 cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".",
11070 printer->name, get_username(con));
ef416fc2 11071 else
f7deaa1a 11072 cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".",
11073 printer->name, get_username(con));
ef416fc2 11074
11075 /*
11076 * Everything was ok, so return OK status...
11077 */
11078
11079 con->response->request.status.status_code = IPP_OK;
11080}
11081
11082
89d46774 11083/*
11084 * 'url_encode_attr()' - URL-encode a string attribute.
11085 */
11086
11087static void
11088url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
11089 char *buffer,/* I - String buffer */
07623986 11090 size_t bufsize)/* I - Size of buffer */
89d46774 11091{
11092 int i; /* Looping var */
11093 char *bufptr, /* Pointer into buffer */
b94498cf 11094 *bufend; /* End of buffer */
89d46774 11095
11096
11097 strlcpy(buffer, attr->name, bufsize);
11098 bufptr = buffer + strlen(buffer);
11099 bufend = buffer + bufsize - 1;
11100
11101 for (i = 0; i < attr->num_values; i ++)
11102 {
11103 if (bufptr >= bufend)
11104 break;
11105
11106 if (i)
11107 *bufptr++ = ',';
11108 else
11109 *bufptr++ = '=';
11110
11111 if (bufptr >= bufend)
11112 break;
11113
11114 *bufptr++ = '\'';
11115
07623986 11116 bufptr = url_encode_string(attr->values[i].string.text, bufptr, (size_t)(bufend - bufptr + 1));
89d46774 11117
11118 if (bufptr >= bufend)
11119 break;
11120
11121 *bufptr++ = '\'';
11122 }
11123
11124 *bufptr = '\0';
11125}
11126
11127
b94498cf 11128/*
11129 * 'url_encode_string()' - URL-encode a string.
11130 */
11131
11132static char * /* O - End of string */
11133url_encode_string(const char *s, /* I - String */
11134 char *buffer, /* I - String buffer */
07623986 11135 size_t bufsize) /* I - Size of buffer */
b94498cf 11136{
11137 char *bufptr, /* Pointer into buffer */
11138 *bufend; /* End of buffer */
11139 static const char *hex = "0123456789ABCDEF";
11140 /* Hex digits */
11141
11142
11143 bufptr = buffer;
11144 bufend = buffer + bufsize - 1;
11145
11146 while (*s && bufptr < bufend)
11147 {
f701418f 11148 if (*s == ' ' || *s == '%' || *s == '+')
b94498cf 11149 {
11150 if (bufptr >= (bufend - 2))
11151 break;
11152
11153 *bufptr++ = '%';
11154 *bufptr++ = hex[(*s >> 4) & 15];
11155 *bufptr++ = hex[*s & 15];
11156
11157 s ++;
11158 }
11159 else if (*s == '\'' || *s == '\\')
11160 {
11161 if (bufptr >= (bufend - 1))
11162 break;
11163
11164 *bufptr++ = '\\';
11165 *bufptr++ = *s++;
11166 }
11167 else
11168 *bufptr++ = *s++;
11169 }
11170
11171 *bufptr = '\0';
11172
11173 return (bufptr);
11174}
11175
11176
ef416fc2 11177/*
11178 * 'user_allowed()' - See if a user is allowed to print to a queue.
11179 */
11180
11181static int /* O - 0 if not allowed, 1 if allowed */
11182user_allowed(cupsd_printer_t *p, /* I - Printer or class */
11183 const char *username) /* I - Username */
11184{
ef416fc2 11185 struct passwd *pw; /* User password data */
5bd77a73 11186 char baseuser[256], /* Base username */
10d09e33
MS
11187 *baseptr, /* Pointer to "@" in base username */
11188 *name; /* Current user name */
ef416fc2 11189
11190
10d09e33 11191 if (cupsArrayCount(p->users) == 0)
ef416fc2 11192 return (1);
11193
11194 if (!strcmp(username, "root"))
11195 return (1);
11196
5bd77a73
MS
11197 if (strchr(username, '@'))
11198 {
11199 /*
11200 * Strip @REALM for username check...
11201 */
11202
11203 strlcpy(baseuser, username, sizeof(baseuser));
11204
11205 if ((baseptr = strchr(baseuser, '@')) != NULL)
11206 *baseptr = '\0';
11207
11208 username = baseuser;
11209 }
11210
ef416fc2 11211 pw = getpwnam(username);
11212 endpwent();
11213
10d09e33
MS
11214 for (name = (char *)cupsArrayFirst(p->users);
11215 name;
11216 name = (char *)cupsArrayNext(p->users))
ef416fc2 11217 {
10d09e33 11218 if (name[0] == '@')
ef416fc2 11219 {
11220 /*
11221 * Check group membership...
11222 */
11223
10d09e33 11224 if (cupsdCheckGroup(username, pw, name + 1))
ef416fc2 11225 break;
11226 }
10d09e33 11227 else if (name[0] == '#')
8922323b
MS
11228 {
11229 /*
11230 * Check UUID...
11231 */
11232
10d09e33 11233 if (cupsdCheckGroup(username, pw, name))
8922323b
MS
11234 break;
11235 }
88f9aafc 11236 else if (!_cups_strcasecmp(username, name))
ef416fc2 11237 break;
11238 }
11239
10d09e33 11240 return ((name != NULL) != p->deny_users);
ef416fc2 11241}
11242
11243
11244/*
11245 * 'validate_job()' - Validate printer options and destination.
11246 */
11247
11248static void
11249validate_job(cupsd_client_t *con, /* I - Client connection */
11250 ipp_attribute_t *uri) /* I - Printer URI */
11251{
11252 http_status_t status; /* Policy status */
7d5824d6
MS
11253 ipp_attribute_t *attr; /* Current attribute */
11254#ifdef HAVE_SSL
11255 ipp_attribute_t *auth_info; /* auth-info attribute */
11256#endif /* HAVE_SSL */
5a9febac
MS
11257 ipp_attribute_t *format, /* Document-format attribute */
11258 *name; /* Job-name attribute */
bc44d920 11259 cups_ptype_t dtype; /* Destination type (printer/class) */
ef416fc2 11260 char super[MIME_MAX_SUPER],
11261 /* Supertype of file */
11262 type[MIME_MAX_TYPE];
11263 /* Subtype of file */
11264 cupsd_printer_t *printer; /* Printer */
11265
11266
11267 cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
996acce8 11268 con->number, uri->values[0].string.text);
ef416fc2 11269
11270 /*
11271 * OK, see if the client is sending the document compressed - CUPS
11272 * doesn't support compression yet...
11273 */
11274
fa73b229 11275 if ((attr = ippFindAttribute(con->request, "compression",
c7017ecc 11276 IPP_TAG_KEYWORD)) != NULL)
ef416fc2 11277 {
c7017ecc
MS
11278 if (strcmp(attr->values[0].string.text, "none")
11279#ifdef HAVE_LIBZ
11280 && strcmp(attr->values[0].string.text, "gzip")
11281#endif /* HAVE_LIBZ */
11282 )
11283 {
11284 send_ipp_status(con, IPP_ATTRIBUTES,
5a9febac 11285 _("Unsupported 'compression' value \"%s\"."),
c7017ecc
MS
11286 attr->values[0].string.text);
11287 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
11288 "compression", NULL, attr->values[0].string.text);
11289 return;
11290 }
ef416fc2 11291 }
11292
11293 /*
11294 * Is it a format we support?
11295 */
11296
11297 if ((format = ippFindAttribute(con->request, "document-format",
11298 IPP_TAG_MIMETYPE)) != NULL)
11299 {
c5b24bfa 11300 if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]",
bc44d920 11301 super, type) != 2)
ef416fc2 11302 {
5a9febac
MS
11303 send_ipp_status(con, IPP_BAD_REQUEST,
11304 _("Bad 'document-format' value \"%s\"."),
ef416fc2 11305 format->values[0].string.text);
11306 return;
11307 }
11308
fa73b229 11309 if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
11310 !mimeType(MimeDatabase, super, type))
ef416fc2 11311 {
11312 cupsdLogMessage(CUPSD_LOG_INFO,
11313 "Hint: Do you have the raw file printing rules enabled?");
11314 send_ipp_status(con, IPP_DOCUMENT_FORMAT,
5a9febac 11315 _("Unsupported 'document-format' value \"%s\"."),
ef416fc2 11316 format->values[0].string.text);
11317 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
11318 "document-format", NULL, format->values[0].string.text);
11319 return;
11320 }
11321 }
11322
4cadd620
MS
11323 /*
11324 * Is the job-hold-until value valid?
11325 */
11326
11327 if ((attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_ZERO)) != NULL && ((ippGetValueTag(attr) != IPP_TAG_KEYWORD && ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG) || ippGetCount(attr) != 1 || !ippValidateAttribute(attr)))
11328 {
11329 send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Unsupported 'job-hold-until' value."));
11330 ippCopyAttribute(con->response, attr, 0);
11331 return;
11332 }
11333
5a9febac
MS
11334 /*
11335 * Is the job-name valid?
11336 */
11337
11338 if ((name = ippFindAttribute(con->request, "job-name", IPP_TAG_ZERO)) != NULL)
11339 {
5a9febac 11340 if ((name->value_tag != IPP_TAG_NAME && name->value_tag != IPP_TAG_NAMELANG) ||
4cadd620 11341 name->num_values != 1 || !ippValidateAttribute(name))
5a9febac
MS
11342 {
11343 if (StrictConformance)
11344 {
4cadd620 11345 send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Unsupported 'job-name' value."));
5a9febac
MS
11346 ippCopyAttribute(con->response, name, 0);
11347 return;
11348 }
11349 else
11350 {
4cadd620 11351 cupsdLogMessage(CUPSD_LOG_WARN, "Unsupported 'job-name' value, deleting from request.");
5a9febac
MS
11352 ippDeleteAttribute(con->request, name);
11353 }
11354 }
11355 }
11356
ef416fc2 11357 /*
11358 * Is the destination valid?
11359 */
11360
f7deaa1a 11361 if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
ef416fc2 11362 {
11363 /*
11364 * Bad URI...
11365 */
11366
11367 send_ipp_status(con, IPP_NOT_FOUND,
84315f46 11368 _("The printer or class does not exist."));
ef416fc2 11369 return;
11370 }
11371
11372 /*
11373 * Check policy...
11374 */
11375
7d5824d6 11376#ifdef HAVE_SSL
c7017ecc 11377 auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
7d5824d6 11378#endif /* HAVE_SSL */
c7017ecc 11379
ef416fc2 11380 if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11381 {
f899b121 11382 send_http_error(con, status, printer);
ef416fc2 11383 return;
11384 }
c7017ecc
MS
11385 else if (printer->num_auth_info_required == 1 &&
11386 !strcmp(printer->auth_info_required[0], "negotiate") &&
11387 !con->username[0])
11388 {
11389 send_http_error(con, HTTP_UNAUTHORIZED, printer);
11390 return;
11391 }
11392#ifdef HAVE_SSL
996acce8
MS
11393 else if (auth_info && !con->http->tls &&
11394 !httpAddrLocalhost(con->http->hostaddr))
c7017ecc
MS
11395 {
11396 /*
11397 * Require encryption of auth-info over non-local connections...
11398 */
11399
11400 send_http_error(con, HTTP_UPGRADE_REQUIRED, printer);
11401 return;
11402 }
11403#endif /* HAVE_SSL */
ef416fc2 11404
11405 /*
11406 * Everything was ok, so return OK status...
11407 */
11408
11409 con->response->request.status.status_code = IPP_OK;
11410}
11411
11412
11413/*
11414 * 'validate_name()' - Make sure the printer name only contains valid chars.
11415 */
11416
bc44d920 11417static int /* O - 0 if name is no good, 1 if good */
ef416fc2 11418validate_name(const char *name) /* I - Name to check */
11419{
11420 const char *ptr; /* Pointer into name */
11421
11422
11423 /*
11424 * Scan the whole name...
11425 */
11426
11427 for (ptr = name; *ptr; ptr ++)
f7deaa1a 11428 if ((*ptr > 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
ef416fc2 11429 return (0);
11430
11431 /*
11432 * All the characters are good; validate the length, too...
11433 */
11434
11435 return ((ptr - name) < 128);
11436}
11437
11438
11439/*
11440 * 'validate_user()' - Validate the user for the request.
11441 */
11442
11443static int /* O - 1 if permitted, 0 otherwise */
11444validate_user(cupsd_job_t *job, /* I - Job */
11445 cupsd_client_t *con, /* I - Client connection */
11446 const char *owner, /* I - Owner of job/resource */
11447 char *username, /* O - Authenticated username */
07623986 11448 size_t userlen) /* I - Length of username */
ef416fc2 11449{
ef416fc2 11450 cupsd_printer_t *printer; /* Printer for job */
11451
11452
07623986 11453 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 11454
11455 /*
11456 * Validate input...
11457 */
11458
11459 if (!con || !owner || !username || userlen <= 0)
11460 return (0);
11461
11462 /*
11463 * Get the best authenticated username that is available.
11464 */
11465
e00b005a 11466 strlcpy(username, get_username(con), userlen);
ef416fc2 11467
11468 /*
11469 * Check the username against the owner...
11470 */
11471
80ca4592 11472 printer = cupsdFindDest(job->dest);
ef416fc2 11473
11474 return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
11475 con, owner) == HTTP_OK);
11476}