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