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