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