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