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