]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/ipp.c
Add support for cached authentication on a per-job basis. The
[thirdparty/cups.git] / scheduler / ipp.c
CommitLineData
e31bfb6e 1/*
b2e10895 2 * "$Id$"
e31bfb6e 3 *
4 * IPP routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
c9d3f842 6 * Copyright 1997-2005 by Easy Software Products, all rights reserved.
e31bfb6e 7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
8784b6a6 17 * 44141 Airport View Drive, Suite 204
1e0c2f84 18 * Hollywood, Maryland 20636 USA
e31bfb6e 19 *
edfd3c3d 20 * Voice: (301) 373-9600
e31bfb6e 21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
7ebf3a09 26 * ProcessIPPRequest() - Process an incoming IPP request...
27 * accept_jobs() - Accept print jobs to a printer.
28 * add_class() - Add a class to the system.
a3e17a89 29 * add_file() - Add a file to a job.
7ebf3a09 30 * add_job_state_reasons() - Add the "job-state-reasons" attribute based
31 * upon the job and printer state...
fd0624de 32 * add_job_subscriptions() - Add any subcriptions for a job.
7ebf3a09 33 * add_printer() - Add a printer to the system.
34 * add_printer_state_reasons() - Add the "printer-state-reasons" attribute
35 * based upon the printer state...
36 * add_queued_job_count() - Add the "queued-job-count" attribute for
48e211f3 37 * authenticate_job() - Set job authentication info.
7ebf3a09 38 * cancel_all_jobs() - Cancel all print jobs.
39 * cancel_job() - Cancel a print job.
a4b3db80 40 * cancel_subscription() - Cancel a subscription.
b521f3fc 41 * check_quotas() - Check quotas for a printer and user.
962e5a9f 42 * copy_attribute() - Copy a single attribute.
7ebf3a09 43 * copy_attrs() - Copy attributes from one request to another.
e903a8f6 44 * copy_banner() - Copy a banner file to the requests directory
45 * for the specified job.
7ebf3a09 46 * copy_file() - Copy a PPD file or interface script...
0e4f2c25 47 * copy_model() - Copy a PPD model file, substituting default
48 * values as needed...
49 * create_job() - Print a file to a printer or class.
a4b3db80 50 * create_subscription() - Create a notification subscription.
7ebf3a09 51 * delete_printer() - Remove a printer or class from the system.
52 * get_default() - Get the default destination.
53 * get_devices() - Get the list of available devices on the
54 * local system.
55 * get_jobs() - Get a list of jobs for the specified printer.
56 * get_job_attrs() - Get job attributes.
a4b3db80 57 * get_notifications() - Get events for a subscription.
7ebf3a09 58 * get_ppds() - Get the list of PPD files on the local
59 * system.
60 * get_printer_attrs() - Get printer attributes.
61 * get_printers() - Get a list of printers.
a4b3db80 62 * get_subscription_attrs() - Get subscription attributes.
63 * get_subscriptions() - Get subscriptions.
7ebf3a09 64 * hold_job() - Hold a print job.
753453e4 65 * move_job() - Move a job to a new destination.
0e4f2c25 66 * ppd_add_default() - Add a PPD default choice.
67 * ppd_parse_line() - Parse a PPD default line.
7ebf3a09 68 * print_job() - Print a file to a printer or class.
4e43f59e 69 * read_ps_line() - Read a line from a PS file...
56e9c951 70 * read_ps_job_ticket() - Reads a job ticket embedded in a PS file.
7ebf3a09 71 * reject_jobs() - Reject print jobs to a printer.
f63a2256 72 * release_job() - Release a held print job.
73 * restart_job() - Restart an old print job.
48e211f3 74 * save_auth_info() - Save authentication information for a job.
7ebf3a09 75 * send_document() - Send a file to a printer or class.
76 * send_ipp_error() - Send an error status back to the IPP client.
77 * set_default() - Set the default destination...
9cbd98eb 78 * set_job_attrs() - Set job attributes.
7ebf3a09 79 * start_printer() - Start a printer.
80 * stop_printer() - Stop a printer.
bf9da908 81 * user_allowed() - See if a user is allowed to print to a queue.
7ebf3a09 82 * validate_job() - Validate printer options and destination.
a3901bc4 83 * validate_name() - Make sure the printer name only contains
84 * valid chars.
ed3e11d8 85 * validate_user() - Validate the user for the request.
e31bfb6e 86 */
87
88/*
89 * Include necessary headers...
90 */
91
92#include "cupsd.h"
0e4f2c25 93
b4f2ed46 94#ifdef HAVE_LIBPAPER
95# include <paper.h>
96#endif /* HAVE_LIBPAPER */
97
0e4f2c25 98
99/*
100 * PPD default choice structure...
101 */
102
103typedef struct
104{
105 char option[PPD_MAX_NAME]; /* Main keyword (option name) */
106 char choice[PPD_MAX_NAME]; /* Option keyword (choice name) */
107} ppd_default_t;
108
e31bfb6e 109
e31bfb6e 110/*
111 * Local functions...
112 */
113
f3d580b9 114static void accept_jobs(client_t *con, ipp_attribute_t *uri);
c7fa9d06 115static void add_class(client_t *con, ipp_attribute_t *uri);
d59a189c 116static int add_file(client_t *con, job_t *job, mime_type_t *filetype,
117 int compression);
7ebf3a09 118static void add_job_state_reasons(client_t *con, job_t *job);
fd0624de 119static void add_job_subscriptions(client_t *con, job_t *job);
c7fa9d06 120static void add_printer(client_t *con, ipp_attribute_t *uri);
7ebf3a09 121static void add_printer_state_reasons(client_t *con, printer_t *p);
122static void add_queued_job_count(client_t *con, printer_t *p);
48e211f3 123static void authenticate_job(client_t *con, ipp_attribute_t *uri);
f3d580b9 124static void cancel_all_jobs(client_t *con, ipp_attribute_t *uri);
1d2c70a6 125static void cancel_job(client_t *con, ipp_attribute_t *uri);
a4b3db80 126static void cancel_subscription(client_t *con, int id);
b521f3fc 127static int check_quotas(client_t *con, printer_t *p);
8fc34542 128static ipp_attribute_t *copy_attribute(ipp_t *to, ipp_attribute_t *attr,
129 int quickcopy);
4979ce3d 130static void copy_attrs(ipp_t *to, ipp_t *from, ipp_attribute_t *req,
9ef400ff 131 ipp_tag_t group, int quickcopy);
b521f3fc 132static int copy_banner(client_t *con, job_t *job, const char *name);
5d99df62 133static int copy_file(const char *from, const char *to);
a04b77d5 134static int copy_model(client_t *con, const char *from, const char *to);
4979ce3d 135static void create_job(client_t *con, ipp_attribute_t *uri);
a4b3db80 136static void create_subscription(client_t *con, ipp_attribute_t *uri);
c7fa9d06 137static void delete_printer(client_t *con, ipp_attribute_t *uri);
1d2c70a6 138static void get_default(client_t *con);
bd84e0d1 139static void get_devices(client_t *con);
1d2c70a6 140static void get_jobs(client_t *con, ipp_attribute_t *uri);
141static void get_job_attrs(client_t *con, ipp_attribute_t *uri);
a4b3db80 142static void get_notifications(client_t *con, int id);
bd84e0d1 143static void get_ppds(client_t *con);
cbbfcc63 144static void get_printers(client_t *con, int type);
1d2c70a6 145static void get_printer_attrs(client_t *con, ipp_attribute_t *uri);
a4b3db80 146static void get_subscription_attrs(client_t *con, int sub_id);
147static void get_subscriptions(client_t *con, ipp_attribute_t *uri);
bd84e0d1 148static void hold_job(client_t *con, ipp_attribute_t *uri);
a3e17a89 149static void move_job(client_t *con, ipp_attribute_t *uri);
0e4f2c25 150static int ppd_add_default(const char *option, const char *choice,
151 int num_defaults, ppd_default_t **defaults);
152static int ppd_parse_line(const char *line, char *option, int olen,
153 char *choice, int clen);
1d2c70a6 154static void print_job(client_t *con, ipp_attribute_t *uri);
56e9c951 155static void read_ps_job_ticket(client_t *con);
f3d580b9 156static void reject_jobs(client_t *con, ipp_attribute_t *uri);
f63a2256 157static void release_job(client_t *con, ipp_attribute_t *uri);
a4b3db80 158static void renew_subscription(client_t *con, int sub_id);
bd84e0d1 159static void restart_job(client_t *con, ipp_attribute_t *uri);
48e211f3 160static void save_auth_info(client_t *con, int job_id);
bd84e0d1 161static void send_document(client_t *con, ipp_attribute_t *uri);
e31bfb6e 162static void send_ipp_error(client_t *con, ipp_status_t status);
3270670b 163static void set_default(client_t *con, ipp_attribute_t *uri);
9cbd98eb 164static void set_job_attrs(client_t *con, ipp_attribute_t *uri);
f3d580b9 165static void start_printer(client_t *con, ipp_attribute_t *uri);
166static void stop_printer(client_t *con, ipp_attribute_t *uri);
bf9da908 167static int user_allowed(printer_t *p, const char *username);
1d2c70a6 168static void validate_job(client_t *con, ipp_attribute_t *uri);
a3901bc4 169static int validate_name(const char *name);
bd5510a5 170static int validate_user(job_t *job, client_t *con,
171 const char *owner, char *username,
ed3e11d8 172 int userlen);
e31bfb6e 173
174
175/*
176 * 'ProcessIPPRequest()' - Process an incoming IPP request...
177 */
178
6e3d4276 179int /* O - 1 on success, 0 on failure */
e31bfb6e 180ProcessIPPRequest(client_t *con) /* I - Client connection */
181{
182 ipp_tag_t group; /* Current group tag */
183 ipp_attribute_t *attr; /* Current attribute */
184 ipp_attribute_t *charset; /* Character set attribute */
185 ipp_attribute_t *language; /* Language attribute */
186 ipp_attribute_t *uri; /* Printer URI attribute */
c8f336b5 187 ipp_attribute_t *username; /* requesting-user-name attr */
a4b3db80 188 int sub_id; /* Subscription ID */
e31bfb6e 189
190
b2e10895 191 LogMessage(L_DEBUG2, "ProcessIPPRequest(%p[%d]): operation_id = %04x",
192 con, con->http.fd, con->request->request.op.operation_id);
e31bfb6e 193
194 /*
195 * First build an empty response message for this request...
196 */
197
198 con->response = ippNew();
199
0a3ac972 200 con->response->request.status.version[0] = con->request->request.op.version[0];
201 con->response->request.status.version[1] = con->request->request.op.version[1];
202 con->response->request.status.request_id = con->request->request.op.request_id;
e31bfb6e 203
204 /*
205 * Then validate the request header and required attributes...
206 */
207
0a3ac972 208 if (con->request->request.any.version[0] != 1)
e31bfb6e 209 {
210 /*
211 * Return an error, since we only support IPP 1.x.
212 */
213
1e437ea6 214 LogMessage(L_ERROR, "ProcessIPPRequest: bad request version (%d.%d)!",
0a3ac972 215 con->request->request.any.version[0],
216 con->request->request.any.version[1]);
dd41ddbb 217
a0db0702 218 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
219 "%04X %s Bad request version (%d.%d)",
220 IPP_VERSION_NOT_SUPPORTED, con->http.hostname,
221 con->request->request.any.version[0],
222 con->request->request.any.version[1]);
223
e31bfb6e 224 send_ipp_error(con, IPP_VERSION_NOT_SUPPORTED);
e31bfb6e 225 }
1e437ea6 226 else if (con->request->attrs == NULL)
227 {
228 LogMessage(L_ERROR, "ProcessIPPRequest: no attributes in request!");
a0db0702 229
230 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
231 "%04X %s No attributes in request",
232 IPP_BAD_REQUEST, con->http.hostname);
233
1e437ea6 234 send_ipp_error(con, IPP_BAD_REQUEST);
235 }
e31bfb6e 236 else
e31bfb6e 237 {
238 /*
27b6a264 239 * Make sure that the attributes are provided in the correct order and
240 * don't repeat groups...
e31bfb6e 241 */
242
27b6a264 243 for (attr = con->request->attrs, group = attr->group_tag;
244 attr != NULL;
245 attr = attr->next)
246 if (attr->group_tag < group)
247 {
248 /*
249 * Out of order; return an error...
250 */
251
5ea8888e 252 LogMessage(L_ERROR, "ProcessIPPRequest: attribute groups are out of order!");
a0db0702 253
254 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
255 "%04X %s Attribute groups are out of order",
256 IPP_BAD_REQUEST, con->http.hostname);
257
27b6a264 258 send_ipp_error(con, IPP_BAD_REQUEST);
f3d580b9 259 break;
27b6a264 260 }
261 else
262 group = attr->group_tag;
f3d580b9 263
27b6a264 264 if (attr == NULL)
265 {
266 /*
267 * Then make sure that the first three attributes are:
268 *
269 * attributes-charset
270 * attributes-natural-language
f63a2256 271 * printer-uri/job-uri
27b6a264 272 */
e31bfb6e 273
27b6a264 274 attr = con->request->attrs;
a0db0702 275 if (attr && !strcmp(attr->name, "attributes-charset") &&
27b6a264 276 attr->value_tag == IPP_TAG_CHARSET)
277 charset = attr;
278 else
279 charset = NULL;
280
17b95e13 281 if (attr)
282 attr = attr->next;
a0db0702 283
284 if (attr && !strcmp(attr->name, "attributes-natural-language") &&
27b6a264 285 attr->value_tag == IPP_TAG_LANGUAGE)
286 language = attr;
287 else
288 language = NULL;
289
17b95e13 290 if ((attr = ippFindAttribute(con->request, "printer-uri", IPP_TAG_URI)) != NULL)
27b6a264 291 uri = attr;
17b95e13 292 else if ((attr = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) != NULL)
27b6a264 293 uri = attr;
294 else
295 uri = NULL;
296
17b95e13 297 if (charset)
298 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
299 "attributes-charset", NULL, charset->values[0].string.text);
300 else
301 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
302 "attributes-charset", NULL, DefaultCharset);
b9e6b836 303
17b95e13 304 if (language)
305 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
306 "attributes-natural-language", NULL,
307 language->values[0].string.text);
308 else
309 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
310 "attributes-natural-language", NULL, DefaultLanguage);
b9e6b836 311
27b6a264 312 if (charset == NULL || language == NULL ||
a148912d 313 (uri == NULL &&
0a3ac972 314 con->request->request.op.operation_id != CUPS_GET_DEFAULT &&
315 con->request->request.op.operation_id != CUPS_GET_PRINTERS &&
316 con->request->request.op.operation_id != CUPS_GET_CLASSES &&
317 con->request->request.op.operation_id != CUPS_GET_DEVICES &&
318 con->request->request.op.operation_id != CUPS_GET_PPDS))
27b6a264 319 {
320 /*
321 * Return an error, since attributes-charset,
322 * attributes-natural-language, and printer-uri/job-uri are required
323 * for all operations.
324 */
325
a0db0702 326 if (!charset)
327 {
5ea8888e 328 LogMessage(L_ERROR, "ProcessIPPRequest: missing attributes-charset attribute!");
bd84e0d1 329
a0db0702 330 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
331 "%04X %s Missing attributes-charset attribute",
332 IPP_BAD_REQUEST, con->http.hostname);
333 }
334
335 if (!language)
336 {
5ea8888e 337 LogMessage(L_ERROR, "ProcessIPPRequest: missing attributes-natural-language attribute!");
bd84e0d1 338
a0db0702 339 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
340 "%04X %s Missing attributes-natural-language attribute",
341 IPP_BAD_REQUEST, con->http.hostname);
342 }
343
344 if (!uri)
345 {
5ea8888e 346 LogMessage(L_ERROR, "ProcessIPPRequest: missing printer-uri or job-uri attribute!");
bd84e0d1 347
a0db0702 348 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
349 "%04X %s Missing printer-uri or job-uri attribute",
350 IPP_BAD_REQUEST, con->http.hostname);
351 }
352
b2e10895 353 LogMessage(L_DEBUG, "Request attributes follow...");
354
355 for (attr = con->request->attrs; attr != NULL; attr = attr->next)
356 LogMessage(L_DEBUG, "attr \"%s\": group_tag = %x, value_tag = %x",
357 attr->name ? attr->name : "(null)", attr->group_tag,
358 attr->value_tag);
359
360 LogMessage(L_DEBUG, "End of attributes...");
361
27b6a264 362 send_ipp_error(con, IPP_BAD_REQUEST);
363 }
364 else
365 {
27b6a264 366 /*
c8f336b5 367 * OK, all the checks pass so far; make sure requesting-user-name is
368 * not "root" from a remote host...
369 */
370
a4b3db80 371 if ((username = ippFindAttribute(con->request, "requesting-user-name",
372 IPP_TAG_NAME)) != NULL)
c8f336b5 373 {
374 /*
375 * Check for root user...
376 */
377
378 if (strcmp(username->values[0].string.text, "root") == 0 &&
99de6da0 379 strcasecmp(con->http.hostname, "localhost") != 0 &&
c8f336b5 380 strcmp(con->username, "root") != 0)
381 {
382 /*
383 * Remote unauthenticated user masquerading as local root...
384 */
385
8e504b4d 386 SetString(&(username->values[0].string.text), RemoteRoot);
c8f336b5 387 }
388 }
389
a4b3db80 390 if ((attr = ippFindAttribute(con->request, "notify-subscription-id",
391 IPP_TAG_INTEGER)) != NULL)
392 sub_id = attr->values[0].integer;
393 else
394 sub_id = 0;
395
c8f336b5 396 /*
397 * Then try processing the operation...
27b6a264 398 */
399
c4dda638 400 if (uri)
401 LogMessage(L_DEBUG2, "ProcessIPPRequest: URI=\"%s\"",
402 uri->values[0].string.text);
403
0a3ac972 404 switch (con->request->request.op.operation_id)
27b6a264 405 {
406 case IPP_PRINT_JOB :
407 print_job(con, uri);
408 break;
409
410 case IPP_VALIDATE_JOB :
411 validate_job(con, uri);
412 break;
413
bd84e0d1 414 case IPP_CREATE_JOB :
415 create_job(con, uri);
416 break;
417
418 case IPP_SEND_DOCUMENT :
419 send_document(con, uri);
420 break;
421
27b6a264 422 case IPP_CANCEL_JOB :
423 cancel_job(con, uri);
424 break;
425
426 case IPP_GET_JOB_ATTRIBUTES :
427 get_job_attrs(con, uri);
428 break;
429
430 case IPP_GET_JOBS :
431 get_jobs(con, uri);
432 break;
433
434 case IPP_GET_PRINTER_ATTRIBUTES :
435 get_printer_attrs(con, uri);
436 break;
437
bd84e0d1 438 case IPP_HOLD_JOB :
439 hold_job(con, uri);
440 break;
441
f63a2256 442 case IPP_RELEASE_JOB :
443 release_job(con, uri);
444 break;
445
bd84e0d1 446 case IPP_RESTART_JOB :
447 restart_job(con, uri);
448 break;
449
27b6a264 450 case IPP_PAUSE_PRINTER :
451 stop_printer(con, uri);
452 break;
453
454 case IPP_RESUME_PRINTER :
455 start_printer(con, uri);
456 break;
457
458 case IPP_PURGE_JOBS :
459 cancel_all_jobs(con, uri);
460 break;
461
9cbd98eb 462 case IPP_SET_JOB_ATTRIBUTES :
463 set_job_attrs(con, uri);
464 break;
465
27b6a264 466 case CUPS_GET_DEFAULT :
467 get_default(con);
468 break;
469
470 case CUPS_GET_PRINTERS :
cbbfcc63 471 get_printers(con, 0);
472 break;
473
474 case CUPS_GET_CLASSES :
475 get_printers(con, CUPS_PRINTER_CLASS);
27b6a264 476 break;
e31bfb6e 477
27b6a264 478 case CUPS_ADD_PRINTER :
c7fa9d06 479 add_printer(con, uri);
27b6a264 480 break;
e31bfb6e 481
27b6a264 482 case CUPS_DELETE_PRINTER :
c7fa9d06 483 delete_printer(con, uri);
27b6a264 484 break;
e31bfb6e 485
27b6a264 486 case CUPS_ADD_CLASS :
c7fa9d06 487 add_class(con, uri);
27b6a264 488 break;
e31bfb6e 489
27b6a264 490 case CUPS_DELETE_CLASS :
c7fa9d06 491 delete_printer(con, uri);
27b6a264 492 break;
e31bfb6e 493
27b6a264 494 case CUPS_ACCEPT_JOBS :
b5cb0608 495 case IPP_ENABLE_PRINTER :
27b6a264 496 accept_jobs(con, uri);
497 break;
f3d580b9 498
27b6a264 499 case CUPS_REJECT_JOBS :
b5cb0608 500 case IPP_DISABLE_PRINTER :
27b6a264 501 reject_jobs(con, uri);
502 break;
f3d580b9 503
3270670b 504 case CUPS_SET_DEFAULT :
505 set_default(con, uri);
506 break;
507
bd84e0d1 508 case CUPS_GET_DEVICES :
509 get_devices(con);
510 break;
511
512 case CUPS_GET_PPDS :
513 get_ppds(con);
514 break;
515
a3e17a89 516 case CUPS_MOVE_JOB :
517 move_job(con, uri);
518 break;
519
48e211f3 520 case CUPS_AUTHENTICATE_JOB :
521 authenticate_job(con, uri);
522 break;
523
a4b3db80 524 case IPP_CREATE_PRINTER_SUBSCRIPTION :
525 case IPP_CREATE_JOB_SUBSCRIPTION :
526 create_subscription(con, uri);
527 break;
528
529 case IPP_GET_SUBSCRIPTION_ATTRIBUTES :
530 get_subscription_attrs(con, sub_id);
531 break;
532
533 case IPP_GET_SUBSCRIPTIONS :
534 get_subscriptions(con, uri);
535 break;
536
537 case IPP_RENEW_SUBSCRIPTION :
538 renew_subscription(con, sub_id);
539 break;
540
541 case IPP_CANCEL_SUBSCRIPTION :
542 cancel_subscription(con, sub_id);
543 break;
544
545 case IPP_GET_NOTIFICATIONS :
546 get_notifications(con, sub_id);
547 break;
548
27b6a264 549 default :
a0db0702 550 cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
551 "%04X %s Operation %04X (%s) not supported",
552 IPP_OPERATION_NOT_SUPPORTED, con->http.hostname,
553 con->request->request.op.operation_id,
554 ippOpString(con->request->request.op.operation_id));
555
27b6a264 556 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
a0db0702 557 break;
27b6a264 558 }
559 }
560 }
e31bfb6e 561 }
562
1d5ef583 563 if (con->response)
6e3d4276 564 {
1d5ef583 565 /*
566 * Sending data from the scheduler...
567 */
dd63ebe2 568
1d5ef583 569 LogMessage(L_DEBUG, "ProcessIPPRequest: %d status_code=%x (%s)",
570 con->http.fd, con->response->request.status.status_code,
571 ippErrorString(con->response->request.status.status_code));
572
573 if (SendHeader(con, HTTP_OK, "application/ipp"))
dd63ebe2 574 {
1d5ef583 575 if (con->http.version == HTTP_1_1)
576 {
577 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
6e3d4276 578
1d5ef583 579 httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n");
580 }
581 else
582 {
583 con->http.data_encoding = HTTP_ENCODE_LENGTH;
584 con->http.data_remaining = ippLength(con->response);
f937f199 585
1d5ef583 586 httpPrintf(HTTP(con), "Content-Length: %d\r\n\r\n",
587 con->http.data_remaining);
588 }
1d2c70a6 589
1d5ef583 590 LogMessage(L_DEBUG2, "ProcessIPPRequest: Adding fd %d to OutputSet...",
591 con->http.fd);
6e3d4276 592
1d5ef583 593 FD_SET(con->http.fd, OutputSet);
6e3d4276 594
1d5ef583 595 /*
596 * Tell the caller the response header was sent successfully...
597 */
598
599 return (1);
600 }
601 else
602 {
603 /*
604 * Tell the caller the response header could not be sent...
605 */
606
607 return (0);
608 }
6e3d4276 609 }
610 else
611 {
612 /*
1d5ef583 613 * Sending data from a subprocess like cups-deviced; tell the caller
614 * everything is A-OK so far...
6e3d4276 615 */
18fe941f 616
1d5ef583 617 return (1);
6e3d4276 618 }
e31bfb6e 619}
620
621
f3d580b9 622/*
623 * 'accept_jobs()' - Accept print jobs to a printer.
624 */
625
626static void
627accept_jobs(client_t *con, /* I - Client connection */
628 ipp_attribute_t *uri) /* I - Printer or class URI */
629{
a4b3db80 630 cups_ptype_t dtype; /* Destination type (printer or class) */
631 char method[HTTP_MAX_URI], /* Method portion of URI */
632 username[HTTP_MAX_URI], /* Username portion of URI */
633 host[HTTP_MAX_URI], /* Host portion of URI */
634 resource[HTTP_MAX_URI]; /* Resource portion of URI */
635 int port; /* Port portion of URI */
636 const char *name; /* Printer name */
637 printer_t *printer; /* Printer data */
f3d580b9 638
639
b2e10895 640 LogMessage(L_DEBUG2, "accept_jobs(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 641 uri->values[0].string.text);
f3d580b9 642
643 /*
644 * Was this operation called from the correct URI?
645 */
646
647 if (strncmp(con->uri, "/admin/", 7) != 0)
648 {
5ea8888e 649 LogMessage(L_ERROR, "accept_jobs: admin request on bad resource \'%s\'!",
1124e9ec 650 con->uri);
f3d580b9 651 send_ipp_error(con, IPP_NOT_AUTHORIZED);
652 return;
653 }
654
655 /*
656 * Is the destination valid?
657 */
658
659 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
660
bd5510a5 661 if ((name = ValidateDest(host, resource, &dtype, &printer)) == NULL)
f3d580b9 662 {
663 /*
664 * Bad URI...
665 */
666
5ea8888e 667 LogMessage(L_ERROR, "accept_jobs: resource name \'%s\' no good!", resource);
f3d580b9 668 send_ipp_error(con, IPP_NOT_FOUND);
669 return;
670 }
671
672 /*
bd5510a5 673 * Check policy...
f3d580b9 674 */
675
99baf768 676 if (!cupsdCheckPolicy(printer->op_policy_ptr, con, NULL))
bd5510a5 677 {
678 LogMessage(L_ERROR, "accept_jobs: not authorized!");
679 send_ipp_error(con, IPP_NOT_AUTHORIZED);
680 return;
681 }
682
683 /*
684 * Accept jobs sent to the printer...
685 */
17532ea0 686
f3d580b9 687 printer->accepting = 1;
688 printer->state_message[0] = '\0';
689
c8a55d2c 690 AddPrinterHistory(printer);
691
997cf8b0 692 if (dtype & CUPS_PRINTER_CLASS)
5d99df62 693 SaveAllClasses();
694 else
695 SaveAllPrinters();
696
5ea8888e 697 LogMessage(L_INFO, "Printer \'%s\' now accepting jobs (\'%s\').", name,
cc0561c6 698 con->username);
699
f3d580b9 700 /*
701 * Everything was ok, so return OK status...
702 */
703
0a3ac972 704 con->response->request.status.status_code = IPP_OK;
f3d580b9 705}
706
707
1d2c70a6 708/*
709 * 'add_class()' - Add a class to the system.
710 */
711
e31bfb6e 712static void
c7fa9d06 713add_class(client_t *con, /* I - Client connection */
714 ipp_attribute_t *uri) /* I - URI of class */
e31bfb6e 715{
a4b3db80 716 int i; /* Looping var */
717 char method[HTTP_MAX_URI], /* Method portion of URI */
718 username[HTTP_MAX_URI], /* Username portion of URI */
719 host[HTTP_MAX_URI], /* Host portion of URI */
720 resource[HTTP_MAX_URI]; /* Resource portion of URI */
721 int port; /* Port portion of URI */
722 printer_t *pclass, /* Class */
723 *member; /* Member printer/class */
724 cups_ptype_t dtype; /* Destination type */
725 const char *dest; /* Printer or class name */
726 ipp_attribute_t *attr; /* Printer attribute */
727 int modify; /* Non-zero if we just modified */
3270670b 728
729
b2e10895 730 LogMessage(L_DEBUG2, "add_class(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 731 uri->values[0].string.text);
732
f3d580b9 733 /*
734 * Was this operation called from the correct URI?
735 */
736
737 if (strncmp(con->uri, "/admin/", 7) != 0)
738 {
5ea8888e 739 LogMessage(L_ERROR, "add_class: admin request on bad resource \'%s\'!",
1124e9ec 740 con->uri);
f3d580b9 741 send_ipp_error(con, IPP_NOT_AUTHORIZED);
742 return;
743 }
744
3270670b 745 /*
746 * Do we have a valid URI?
747 */
748
749 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
750
c305e5e4 751 if (strncmp(resource, "/classes/", 9) != 0 || strlen(resource) == 9)
3270670b 752 {
753 /*
754 * No, return an error...
755 */
756
757 send_ipp_error(con, IPP_BAD_REQUEST);
758 return;
759 }
760
a3901bc4 761 /*
762 * Do we have a valid printer name?
763 */
764
765 if (!validate_name(resource + 9))
766 {
767 /*
768 * No, return an error...
769 */
770
771 send_ipp_error(con, IPP_BAD_REQUEST);
772 return;
773 }
774
bd5510a5 775 /*
776 * Check policy...
777 */
778
99baf768 779 if (!cupsdCheckPolicy(DefaultPolicyPtr, con, NULL))
bd5510a5 780 {
781 LogMessage(L_ERROR, "add_class: not authorized!");
782 send_ipp_error(con, IPP_NOT_AUTHORIZED);
783 return;
784 }
785
3270670b 786 /*
787 * See if the class already exists; if not, create a new class...
788 */
789
790 if ((pclass = FindClass(resource + 9)) == NULL)
4621b86f 791 {
792 /*
793 * Class doesn't exist; see if we have a printer of the same name...
794 */
795
e29fa28a 796 if ((pclass = FindPrinter(resource + 9)) != NULL &&
797 !(pclass->type & CUPS_PRINTER_REMOTE))
4621b86f 798 {
799 /*
800 * Yes, return an error...
801 */
802
803 send_ipp_error(con, IPP_NOT_POSSIBLE);
804 return;
805 }
4621b86f 806
e29fa28a 807 /*
808 * No, add the pclass...
809 */
810
811 pclass = AddClass(resource + 9);
d6f1ff9a 812 modify = 0;
4621b86f 813 }
d6f1ff9a 814 else if (pclass->type & CUPS_PRINTER_IMPLICIT)
815 {
816 /*
753453e4 817 * Rename the implicit class to "AnyClass" or remove it...
d6f1ff9a 818 */
819
753453e4 820 if (ImplicitAnyClasses)
821 {
a9a95097 822 SetStringf(&pclass->name, "Any%s", resource + 9);
753453e4 823 SortPrinters();
824 }
825 else
9b2fe6bd 826 DeletePrinter(pclass, 1);
d6f1ff9a 827
828 /*
829 * Add the class as a new local class...
830 */
831
753453e4 832 pclass = AddClass(resource + 9);
d6f1ff9a 833 modify = 0;
834 }
835 else if (pclass->type & CUPS_PRINTER_REMOTE)
836 {
837 /*
2bdd1992 838 * Rename the remote class to "Class"...
d6f1ff9a 839 */
840
2bdd1992 841 DeletePrinterFilters(pclass);
a9a95097 842 SetStringf(&pclass->name, "%s@%s", resource + 9, pclass->hostname);
2360839b 843 SetPrinterAttrs(pclass);
d6f1ff9a 844 SortPrinters();
845
846 /*
847 * Add the class as a new local class...
848 */
849
753453e4 850 pclass = AddClass(resource + 9);
d6f1ff9a 851 modify = 0;
852 }
853 else
854 modify = 1;
3270670b 855
856 /*
857 * Look for attributes and copy them over as needed...
858 */
859
860 if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
36992080 861 SetString(&pclass->location, attr->values[0].string.text);
970017a4 862
3270670b 863 if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
36992080 864 SetString(&pclass->info, attr->values[0].string.text);
970017a4 865
4621b86f 866 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
867 {
5ea8888e 868 LogMessage(L_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
4621b86f 869 pclass->name, attr->values[0].boolean, pclass->accepting);
870
871 pclass->accepting = attr->values[0].boolean;
c8a55d2c 872 AddPrinterHistory(pclass);
4621b86f 873 }
25392f52 874
875 if ((attr = ippFindAttribute(con->request, "printer-is-shared", IPP_TAG_BOOLEAN)) != NULL)
876 {
877 LogMessage(L_INFO, "Setting %s printer-is-shared to %d (was %d.)",
878 pclass->name, attr->values[0].boolean, pclass->shared);
879
880 pclass->shared = attr->values[0].boolean;
881 }
882
4621b86f 883 if ((attr = ippFindAttribute(con->request, "printer-state", IPP_TAG_ENUM)) != NULL)
884 {
c8a55d2c 885 if (attr->values[0].integer != IPP_PRINTER_IDLE &&
886 attr->values[0].integer == IPP_PRINTER_STOPPED)
4621b86f 887 {
c8a55d2c 888 LogMessage(L_ERROR, "Attempt to set %s printer-state to bad value %d!",
889 pclass->name, attr->values[0].integer);
890 send_ipp_error(con, IPP_BAD_REQUEST);
891 return;
4621b86f 892 }
893
c8a55d2c 894 LogMessage(L_INFO, "Setting %s printer-state to %d (was %d.)", pclass->name,
895 attr->values[0].integer, pclass->state);
896
1e0c2f84 897 SetPrinterState(pclass, (ipp_pstate_t)(attr->values[0].integer), 0);
4621b86f 898 }
899 if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL)
c8a55d2c 900 {
def978d5 901 strlcpy(pclass->state_message, attr->values[0].string.text,
902 sizeof(pclass->state_message));
c8a55d2c 903 AddPrinterHistory(pclass);
904 }
753453e4 905 if ((attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_ZERO)) != NULL &&
36992080 906 !Classification)
a3e17a89 907 {
36992080 908 SetString(&pclass->job_sheets[0], attr->values[0].string.text);
a3e17a89 909 if (attr->num_values > 1)
36992080 910 SetString(&pclass->job_sheets[1], attr->values[1].string.text);
a3e17a89 911 else
36992080 912 SetString(&pclass->job_sheets[1], "none");
a3e17a89 913 }
04d756fc 914 if ((attr = ippFindAttribute(con->request, "requesting-user-name-allowed",
915 IPP_TAG_ZERO)) != NULL)
916 {
917 FreePrinterUsers(pclass);
918
919 pclass->deny_users = 0;
01a426e9 920 if (attr->value_tag == IPP_TAG_NAME &&
921 (attr->num_values > 1 ||
922 strcmp(attr->values[0].string.text, "all") != 0))
04d756fc 923 for (i = 0; i < attr->num_values; i ++)
924 AddPrinterUser(pclass, attr->values[i].string.text);
925 }
926 else if ((attr = ippFindAttribute(con->request, "requesting-user-name-denied",
927 IPP_TAG_ZERO)) != NULL)
928 {
929 FreePrinterUsers(pclass);
930
931 pclass->deny_users = 1;
01a426e9 932 if (attr->value_tag == IPP_TAG_NAME &&
933 (attr->num_values > 1 ||
934 strcmp(attr->values[0].string.text, "none") != 0))
04d756fc 935 for (i = 0; i < attr->num_values; i ++)
936 AddPrinterUser(pclass, attr->values[i].string.text);
937 }
938 if ((attr = ippFindAttribute(con->request, "job-quota-period",
939 IPP_TAG_INTEGER)) != NULL)
940 {
320115a7 941 LogMessage(L_DEBUG, "add_class: Setting job-quota-period to %d...",
942 attr->values[0].integer);
04d756fc 943 FreeQuotas(pclass);
944 pclass->quota_period = attr->values[0].integer;
945 }
946 if ((attr = ippFindAttribute(con->request, "job-k-limit",
947 IPP_TAG_INTEGER)) != NULL)
948 {
320115a7 949 LogMessage(L_DEBUG, "add_class: Setting job-k-limit to %d...",
950 attr->values[0].integer);
04d756fc 951 FreeQuotas(pclass);
952 pclass->k_limit = attr->values[0].integer;
953 }
954 if ((attr = ippFindAttribute(con->request, "job-page-limit",
955 IPP_TAG_INTEGER)) != NULL)
956 {
320115a7 957 LogMessage(L_DEBUG, "add_class: Setting job-page-limit to %d...",
958 attr->values[0].integer);
04d756fc 959 FreeQuotas(pclass);
960 pclass->page_limit = attr->values[0].integer;
961 }
8e504b4d 962 if ((attr = ippFindAttribute(con->request, "printer-op-policy", IPP_TAG_TEXT)) != NULL)
963 {
99baf768 964 cupsd_policy_t *p; /* Policy */
8e504b4d 965
3270670b 966
99baf768 967 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
8e504b4d 968 {
969 LogMessage(L_DEBUG, "add_class: Setting printer-op-policy to \"%s\"...",
970 attr->values[0].string.text);
971 SetString(&pclass->op_policy, attr->values[0].string.text);
972 pclass->op_policy_ptr = p;
973 }
974 else
975 {
976 LogMessage(L_ERROR, "add_class: Unknown printer-op-policy \"%s\"...",
977 attr->values[0].string.text);
978 send_ipp_error(con, IPP_NOT_POSSIBLE);
979 return;
980 }
981 }
982 if ((attr = ippFindAttribute(con->request, "printer-error-policy", IPP_TAG_TEXT)) != NULL)
983 {
0f9c1053 984 if (strcmp(attr->values[0].string.text, "abort-job") &&
985 strcmp(attr->values[0].string.text, "retry-job") &&
986 strcmp(attr->values[0].string.text, "stop-printer"))
987 {
988 LogMessage(L_ERROR, "add_class: Unknown printer-error-policy \"%s\"...",
989 attr->values[0].string.text);
990 send_ipp_error(con, IPP_NOT_POSSIBLE);
991 return;
992 }
993
8e504b4d 994 LogMessage(L_DEBUG, "add_class: Setting printer-error-policy to \"%s\"...",
995 attr->values[0].string.text);
996 SetString(&pclass->error_policy, attr->values[0].string.text);
997 }
3270670b 998 if ((attr = ippFindAttribute(con->request, "member-uris", IPP_TAG_URI)) != NULL)
999 {
1000 /*
1001 * Clear the printer array as needed...
1002 */
1003
1004 if (pclass->num_printers > 0)
1005 {
1006 free(pclass->printers);
1007 pclass->num_printers = 0;
1008 }
1009
1010 /*
1011 * Add each printer or class that is listed...
1012 */
1013
1014 for (i = 0; i < attr->num_values; i ++)
1015 {
1016 /*
1017 * Search for the printer or class URI...
1018 */
1019
1020 httpSeparate(attr->values[i].string.text, method, username, host,
1021 &port, resource);
1022
bd5510a5 1023 if ((dest = ValidateDest(host, resource, &dtype, &member)) == NULL)
3270670b 1024 {
1025 /*
1026 * Bad URI...
1027 */
1028
5ea8888e 1029 LogMessage(L_ERROR, "add_class: resource name \'%s\' no good!", resource);
3270670b 1030 send_ipp_error(con, IPP_NOT_FOUND);
1031 return;
1032 }
1033
1034 /*
1035 * Add it to the class...
1036 */
1037
bd5510a5 1038 AddPrinterToClass(pclass, member);
3270670b 1039 }
1040 }
1041
3270670b 1042 /*
1043 * Update the printer class attributes and return...
1044 */
1045
1046 SetPrinterAttrs(pclass);
cc0561c6 1047 SaveAllClasses();
47afc1d2 1048 CheckJobs();
cc0561c6 1049
3a74d6bf 1050 WritePrintcap();
1051
d6f1ff9a 1052 if (modify)
42f94780 1053 {
1054 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, pclass, NULL,
1055 "Class \'%s\' modified by \'%s\'.", pclass->name,
1056 con->username);
1057
d6f1ff9a 1058 LogMessage(L_INFO, "Class \'%s\' modified by \'%s\'.", pclass->name,
1059 con->username);
42f94780 1060 }
d6f1ff9a 1061 else
068e9059 1062 {
1063 AddPrinterHistory(pclass);
1064
42f94780 1065 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, pclass, NULL,
1066 "New class \'%s\' added by \'%s\'.", pclass->name,
1067 con->username);
1068
d6f1ff9a 1069 LogMessage(L_INFO, "New class \'%s\' added by \'%s\'.", pclass->name,
1070 con->username);
068e9059 1071 }
3270670b 1072
0a3ac972 1073 con->response->request.status.status_code = IPP_OK;
e31bfb6e 1074}
1075
1076
a3e17a89 1077/*
1078 * 'add_file()' - Add a file to a job.
1079 */
1080
b521f3fc 1081static int /* O - 0 on success, -1 on error */
a3e17a89 1082add_file(client_t *con, /* I - Connection to client */
1083 job_t *job, /* I - Job to add to */
d59a189c 1084 mime_type_t *filetype, /* I - Type of file */
1085 int compression) /* I - Compression */
a3e17a89 1086{
1087 mime_type_t **filetypes; /* New filetypes array... */
d59a189c 1088 int *compressions; /* New compressions array... */
a3e17a89 1089
1090
d59a189c 1091 LogMessage(L_DEBUG2, "add_file(con=%p[%d], job=%d, filetype=%s/%s, compression=%d)\n",
1092 con, con->http.fd, job->id, filetype->super, filetype->type,
1093 compression);
dbb05cda 1094
a3e17a89 1095 /*
1096 * Add the file to the job...
1097 */
1098
1099 if (job->num_files == 0)
d59a189c 1100 {
1101 compressions = (int *)malloc(sizeof(int));
1102 filetypes = (mime_type_t **)malloc(sizeof(mime_type_t *));
1103 }
a3e17a89 1104 else
d59a189c 1105 {
1106 compressions = (int *)realloc(job->compressions,
1107 (job->num_files + 1) * sizeof(int));
1108 filetypes = (mime_type_t **)realloc(job->filetypes,
1109 (job->num_files + 1) *
1110 sizeof(mime_type_t *));
1111 }
a3e17a89 1112
d59a189c 1113 if (compressions == NULL || filetypes == NULL)
a3e17a89 1114 {
d65c1374 1115 CancelJob(job->id, 1);
a3e17a89 1116 LogMessage(L_ERROR, "add_file: unable to allocate memory for file types!");
1117 send_ipp_error(con, IPP_INTERNAL_ERROR);
1118 return (-1);
1119 }
1120
d59a189c 1121 job->compressions = compressions;
1122 job->compressions[job->num_files] = compression;
1123 job->filetypes = filetypes;
1124 job->filetypes[job->num_files] = filetype;
a3e17a89 1125
1126 job->num_files ++;
1127
1128 return (0);
1129}
1130
1131
7ebf3a09 1132/*
1133 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
1134 * upon the job and printer state...
1135 */
1136
1137static void
1138add_job_state_reasons(client_t *con, /* I - Client connection */
1139 job_t *job) /* I - Job info */
1140{
1141 printer_t *dest; /* Destination printer */
1142
1143
ceaef43a 1144 LogMessage(L_DEBUG2, "add_job_state_reasons(%p[%d], %d)\n", con, con->http.fd,
1145 job ? job->id : 0);
dbb05cda 1146
ceaef43a 1147 switch (job ? job->state->values[0].integer : IPP_JOB_CANCELLED)
7ebf3a09 1148 {
1149 case IPP_JOB_PENDING :
1150 if (job->dtype & CUPS_PRINTER_CLASS)
1151 dest = FindClass(job->dest);
1152 else
1153 dest = FindPrinter(job->dest);
1154
1155 if (dest != NULL && dest->state == IPP_PRINTER_STOPPED)
1156 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1157 "job-state-reasons", NULL, "printer-stopped");
1158 else
1159 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1160 "job-state-reasons", NULL, "none");
1161 break;
1162
1163 case IPP_JOB_HELD :
05ca02bc 1164 if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
1165 ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
1166 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1167 "job-state-reasons", NULL, "job-hold-until-specified");
1168 else
1169 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1170 "job-state-reasons", NULL, "job-incoming");
7ebf3a09 1171 break;
1172
1173 case IPP_JOB_PROCESSING :
1174 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1175 "job-state-reasons", NULL, "job-printing");
1176 break;
1177
1178 case IPP_JOB_STOPPED :
1179 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1180 "job-state-reasons", NULL, "job-stopped");
1181 break;
1182
1183 case IPP_JOB_CANCELLED :
1184 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1185 "job-state-reasons", NULL, "job-canceled-by-user");
1186 break;
1187
1188 case IPP_JOB_ABORTED :
1189 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1190 "job-state-reasons", NULL, "aborted-by-system");
1191 break;
1192
1193 case IPP_JOB_COMPLETED :
1194 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1195 "job-state-reasons", NULL, "job-completed-successfully");
1196 break;
1197 }
1198}
1199
1200
fd0624de 1201/*
1202 * 'add_job_subscriptions()' - Add any subcriptions for a job.
1203 */
1204
1205static void
1206add_job_subscriptions(client_t *con, /* I - Client connection */
1207 job_t *job) /* I - Newly created job */
1208{
1209 int i; /* Looping var */
fd0624de 1210 ipp_attribute_t *prev, /* Previous attribute */
277a6a9b 1211 *next, /* Next attribute */
fd0624de 1212 *attr; /* Current attribute */
1213 cupsd_subscription_t *sub; /* Subscription object */
1214 const char *recipient, /* notify-recipient-uri */
1215 *pullmethod; /* notify-pull-method */
1216 ipp_attribute_t *user_data; /* notify-user-data */
1217 int interval; /* notify-time-interval */
1218 unsigned mask; /* notify-events */
1219
1220
1221 /*
1222 * Find the first subscription group attribute; return if we have
1223 * none...
1224 */
1225
42f94780 1226 for (attr = job->attrs->attrs, prev = NULL; attr; prev = attr, attr = attr->next)
fd0624de 1227 if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
1228 break;
1229
1230 if (!attr)
1231 return;
1232
1233 /*
277a6a9b 1234 * Process the subscription attributes in the request...
fd0624de 1235 */
1236
1237 while (attr)
1238 {
1239 recipient = NULL;
1240 pullmethod = NULL;
1241 user_data = NULL;
1242 interval = 0;
1243 mask = CUPSD_EVENT_NONE;
1244
1245 while (attr && attr->group_tag != IPP_TAG_ZERO)
1246 {
1247 if (!strcmp(attr->name, "notify-recipient") &&
1248 attr->value_tag == IPP_TAG_URI)
1249 recipient = attr->values[0].string.text;
1250 else if (!strcmp(attr->name, "notify-pull-method") &&
1251 attr->value_tag == IPP_TAG_KEYWORD)
1252 pullmethod = attr->values[0].string.text;
1253 else if (!strcmp(attr->name, "notify-charset") &&
1254 attr->value_tag == IPP_TAG_CHARSET &&
1255 strcmp(attr->values[0].string.text, "utf-8"))
1256 {
1257 send_ipp_error(con, IPP_CHARSET);
1258 return;
1259 }
1260 else if (!strcmp(attr->name, "notify-natural-language") &&
1261 attr->value_tag == IPP_TAG_LANGUAGE &&
1262 strcmp(attr->values[0].string.text, DefaultLanguage))
1263 {
1264 send_ipp_error(con, IPP_CHARSET);
1265 return;
1266 }
1267 else if (!strcmp(attr->name, "notify-user-data") &&
1268 attr->value_tag == IPP_TAG_STRING)
1269 {
1270 if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
1271 {
1272 send_ipp_error(con, IPP_REQUEST_VALUE);
1273 return;
1274 }
1275
1276 user_data = attr;
1277 }
1278 else if (!strcmp(attr->name, "notify-events") &&
1279 attr->value_tag == IPP_TAG_KEYWORD)
1280 {
1281 for (i = 0; i < attr->num_values; i ++)
1282 mask |= cupsdEventValue(attr->values[i].string.text);
1283 }
1284 else if (!strcmp(attr->name, "notify-lease-time"))
1285 {
1286 send_ipp_error(con, IPP_BAD_REQUEST);
1287 return;
1288 }
1289 else if (!strcmp(attr->name, "notify-time-interval") &&
1290 attr->value_tag == IPP_TAG_INTEGER)
1291 interval = attr->values[0].integer;
1292
1293 attr = attr->next;
1294 }
1295
1296 if (!recipient && !pullmethod)
1297 break;
1298
277a6a9b 1299 if (mask == CUPSD_EVENT_NONE)
1300 mask = CUPSD_EVENT_JOB_COMPLETED;
1301
fd0624de 1302 sub = cupsdAddSubscription(mask, FindDest(job->dest), job, recipient);
1303
277a6a9b 1304 sub->interval = interval;
1305
fd0624de 1306 SetString(&sub->owner, job->username);
1307
1308 if (user_data)
1309 {
1310 sub->user_data_len = user_data->values[0].unknown.length;
1311 memcpy(sub->user_data, user_data->values[0].unknown.data,
1312 sub->user_data_len);
1313 }
1314
1315 if (attr)
1316 attr = attr->next;
1317 }
1318
1319 /*
277a6a9b 1320 * Remove all of the subscription attributes from the job request...
fd0624de 1321 */
1322
277a6a9b 1323 for (attr = job->attrs->attrs, prev = NULL; attr; attr = next)
1324 {
1325 next = attr->next;
1326
1327 if (attr->group_tag == IPP_TAG_SUBSCRIPTION ||
1328 attr->group_tag == IPP_TAG_ZERO)
1329 {
1330 /*
1331 * Free and remove this attribute...
1332 */
1333
1334 _ipp_free_attr(attr);
1335
1336 if (prev)
1337 prev->next = next;
1338 else
1339 job->attrs->attrs = next;
1340 }
1341 else
1342 prev = attr;
1343 }
1344
1345 job->attrs->last = prev;
1346 job->attrs->current = prev;
fd0624de 1347}
1348
1349
1d2c70a6 1350/*
1351 * 'add_printer()' - Add a printer to the system.
1352 */
1353
e31bfb6e 1354static void
3270670b 1355add_printer(client_t *con, /* I - Client connection */
1356 ipp_attribute_t *uri) /* I - URI of printer */
e31bfb6e 1357{
a4b3db80 1358 int i; /* Looping var */
1359 char method[HTTP_MAX_URI], /* Method portion of URI */
1360 username[HTTP_MAX_URI], /* Username portion of URI */
1361 host[HTTP_MAX_URI], /* Host portion of URI */
1362 resource[HTTP_MAX_URI]; /* Resource portion of URI */
1363 int port; /* Port portion of URI */
1364 printer_t *printer; /* Printer/class */
1365 ipp_attribute_t *attr; /* Printer attribute */
1366 cups_file_t *fp; /* Script/PPD file */
1367 char line[1024]; /* Line from file... */
1368 char srcfile[1024], /* Source Script/PPD file */
1369 dstfile[1024]; /* Destination Script/PPD file */
1370 int modify; /* Non-zero if we are modifying */
c7fa9d06 1371
1372
b2e10895 1373 LogMessage(L_DEBUG2, "add_printer(%p[%d], %s)\n", con, con->http.fd,
bd917997 1374 uri->values[0].string.text);
1375
f3d580b9 1376 /*
1377 * Was this operation called from the correct URI?
1378 */
1379
1380 if (strncmp(con->uri, "/admin/", 7) != 0)
1381 {
5ea8888e 1382 LogMessage(L_ERROR, "add_printer: admin request on bad resource \'%s\'!",
1124e9ec 1383 con->uri);
f3d580b9 1384 send_ipp_error(con, IPP_NOT_AUTHORIZED);
1385 return;
1386 }
1387
c7fa9d06 1388 /*
1389 * Do we have a valid URI?
1390 */
1391
1392 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1393
c305e5e4 1394 if (strncmp(resource, "/printers/", 10) != 0 || strlen(resource) == 10)
c7fa9d06 1395 {
1396 /*
1397 * No, return an error...
1398 */
1399
9c4b5e2e 1400 LogMessage(L_ERROR, "add_printer: bad printer URI \"%s\"!",
1401 uri->values[0].string.text);
c7fa9d06 1402 send_ipp_error(con, IPP_BAD_REQUEST);
1403 return;
1404 }
1405
a3901bc4 1406 /*
1407 * Do we have a valid printer name?
1408 */
1409
1410 if (!validate_name(resource + 10))
1411 {
1412 /*
1413 * No, return an error...
1414 */
1415
1416 send_ipp_error(con, IPP_BAD_REQUEST);
1417 return;
1418 }
1419
bd5510a5 1420 /*
1421 * Check policy...
1422 */
1423
99baf768 1424 if (!cupsdCheckPolicy(DefaultPolicyPtr, con, NULL))
bd5510a5 1425 {
1426 LogMessage(L_ERROR, "add_printer: not authorized!");
1427 send_ipp_error(con, IPP_NOT_AUTHORIZED);
1428 return;
1429 }
1430
c7fa9d06 1431 /*
1432 * See if the printer already exists; if not, create a new printer...
1433 */
1434
1435 if ((printer = FindPrinter(resource + 10)) == NULL)
4621b86f 1436 {
1437 /*
1438 * Printer doesn't exist; see if we have a class of the same name...
1439 */
1440
e29fa28a 1441 if ((printer = FindClass(resource + 10)) != NULL &&
1442 !(printer->type & CUPS_PRINTER_REMOTE))
4621b86f 1443 {
1444 /*
1445 * Yes, return an error...
1446 */
1447
9c4b5e2e 1448 LogMessage(L_ERROR, "add_printer: \"%s\" already exists as a class!",
1449 resource + 10);
4621b86f 1450 send_ipp_error(con, IPP_NOT_POSSIBLE);
1451 return;
1452 }
4621b86f 1453
e29fa28a 1454 /*
1455 * No, add the printer...
1456 */
1457
1458 printer = AddPrinter(resource + 10);
d6f1ff9a 1459 modify = 0;
1460 }
1461 else if (printer->type & CUPS_PRINTER_IMPLICIT)
1462 {
1463 /*
753453e4 1464 * Rename the implicit printer to "AnyPrinter" or delete it...
d6f1ff9a 1465 */
1466
753453e4 1467 if (ImplicitAnyClasses)
1468 {
a9a95097 1469 SetStringf(&printer->name, "Any%s", resource + 10);
753453e4 1470 SortPrinters();
1471 }
1472 else
9b2fe6bd 1473 DeletePrinter(printer, 1);
d6f1ff9a 1474
1475 /*
1476 * Add the printer as a new local printer...
1477 */
1478
1479 printer = AddPrinter(resource + 10);
1480 modify = 0;
1481 }
1482 else if (printer->type & CUPS_PRINTER_REMOTE)
1483 {
1484 /*
1485 * Rename the remote printer to "Printer@server"...
1486 */
1487
2bdd1992 1488 DeletePrinterFilters(printer);
a9a95097 1489 SetStringf(&printer->name, "%s@%s", resource + 10, printer->hostname);
2360839b 1490 SetPrinterAttrs(printer);
d6f1ff9a 1491 SortPrinters();
1492
1493 /*
1494 * Add the printer as a new local printer...
1495 */
1496
1497 printer = AddPrinter(resource + 10);
1498 modify = 0;
4621b86f 1499 }
d6f1ff9a 1500 else
1501 modify = 1;
c7fa9d06 1502
1503 /*
1504 * Look for attributes and copy them over as needed...
1505 */
1506
1507 if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
36992080 1508 SetString(&printer->location, attr->values[0].string.text);
970017a4 1509
c7fa9d06 1510 if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
36992080 1511 SetString(&printer->info, attr->values[0].string.text);
970017a4 1512
c7fa9d06 1513 if ((attr = ippFindAttribute(con->request, "device-uri", IPP_TAG_URI)) != NULL)
997edb40 1514 {
753453e4 1515 /*
1516 * Do we have a valid device URI?
1517 */
1518
1519 httpSeparate(attr->values[0].string.text, method, username, host,
1520 &port, resource);
753453e4 1521
cbc2da6a 1522 if (!strcmp(method, "file"))
f63a430f 1523 {
1524 /*
1525 * See if the administrator has enabled file devices...
1526 */
1527
edd85093 1528 if (!FileDevice && strcmp(resource, "/dev/null"))
f63a430f 1529 {
1530 /*
edd85093 1531 * File devices are disabled and the URL is not file:/dev/null...
f63a430f 1532 */
1533
1534 LogMessage(L_ERROR, "add_printer: File device URIs have been disabled! "
1535 "To enable, see the FileDevice directive in cupsd.conf.");
1536 send_ipp_error(con, IPP_NOT_POSSIBLE);
1537 return;
1538 }
1539 }
1540 else
753453e4 1541 {
1542 /*
cbc2da6a 1543 * See if the backend exists and is executable...
753453e4 1544 */
1545
cbc2da6a 1546 snprintf(srcfile, sizeof(srcfile), "%s/backend/%s", ServerBin, method);
1547 if (access(srcfile, X_OK))
753453e4 1548 {
1549 /*
1550 * Could not find device in list!
1551 */
1552
1553 LogMessage(L_ERROR, "add_printer: bad device-uri attribute \'%s\'!",
1554 attr->values[0].string.text);
1555 send_ipp_error(con, IPP_NOT_POSSIBLE);
1556 return;
1557 }
1558 }
1559
5ea8888e 1560 LogMessage(L_INFO, "Setting %s device-uri to \"%s\" (was \"%s\".)",
2cb3f80d 1561 printer->name,
1562 cupsdSanitizeURI(attr->values[0].string.text, line, sizeof(line)),
1563 cupsdSanitizeURI(printer->device_uri, resource, sizeof(resource)));
997edb40 1564
36992080 1565 SetString(&printer->device_uri, attr->values[0].string.text);
997edb40 1566 }
970017a4 1567
a9bef0b2 1568 if ((attr = ippFindAttribute(con->request, "port-monitor", IPP_TAG_KEYWORD)) != NULL)
1569 {
1570 ipp_attribute_t *supported; /* port-monitor-supported attribute */
1571
1572
1573 supported = ippFindAttribute(printer->attrs, "port-monitor-supported",
1574 IPP_TAG_KEYWORD);
1575 for (i = 0; i < supported->num_values; i ++)
1576 if (!strcmp(supported->values[i].string.text,
1577 attr->values[0].string.text))
1578 break;
1579
1580 if (i >= supported->num_values)
1581 {
1582 LogMessage(L_ERROR, "add_printer: bad port-monitor attribute \'%s\'!",
1583 attr->values[0].string.text);
1584 send_ipp_error(con, IPP_NOT_POSSIBLE);
1585 return;
1586 }
1587
1588 LogMessage(L_INFO, "Setting %s port-monitor to \"%s\" (was \"%s\".)",
1589 printer->name, attr->values[0].string.text,
1590 printer->port_monitor);
1591
1592 if (strcmp(attr->values[0].string.text, "none"))
1593 SetString(&printer->port_monitor, attr->values[0].string.text);
1594 else
1595 ClearString(&printer->port_monitor);
1596 }
1597
251c0599 1598 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
997edb40 1599 {
5ea8888e 1600 LogMessage(L_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
997edb40 1601 printer->name, attr->values[0].boolean, printer->accepting);
1602
251c0599 1603 printer->accepting = attr->values[0].boolean;
c8a55d2c 1604 AddPrinterHistory(printer);
997edb40 1605 }
25392f52 1606
1607 if ((attr = ippFindAttribute(con->request, "printer-is-shared", IPP_TAG_BOOLEAN)) != NULL)
1608 {
1609 LogMessage(L_INFO, "Setting %s printer-is-shared to %d (was %d.)",
1610 printer->name, attr->values[0].boolean, printer->shared);
1611
1612 printer->shared = attr->values[0].boolean;
1613 }
1614
251c0599 1615 if ((attr = ippFindAttribute(con->request, "printer-state", IPP_TAG_ENUM)) != NULL)
1616 {
c8a55d2c 1617 if (attr->values[0].integer != IPP_PRINTER_IDLE &&
1618 attr->values[0].integer == IPP_PRINTER_STOPPED)
8f137344 1619 {
c8a55d2c 1620 LogMessage(L_ERROR, "Attempt to set %s printer-state to bad value %d!",
1621 printer->name, attr->values[0].integer);
1622 send_ipp_error(con, IPP_BAD_REQUEST);
1623 return;
8f137344 1624 }
1625
c8a55d2c 1626 LogMessage(L_INFO, "Setting %s printer-state to %d (was %d.)", printer->name,
1627 attr->values[0].integer, printer->state);
1628
1e0c2f84 1629 SetPrinterState(printer, (ipp_pstate_t)(attr->values[0].integer), 0);
251c0599 1630 }
1631 if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL)
c8a55d2c 1632 {
def978d5 1633 strlcpy(printer->state_message, attr->values[0].string.text,
1634 sizeof(printer->state_message));
c8a55d2c 1635 AddPrinterHistory(printer);
1636 }
753453e4 1637 if ((attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_ZERO)) != NULL &&
9d18dc1f 1638 !Classification)
a3e17a89 1639 {
36992080 1640 SetString(&printer->job_sheets[0], attr->values[0].string.text);
a3e17a89 1641 if (attr->num_values > 1)
36992080 1642 SetString(&printer->job_sheets[1], attr->values[1].string.text);
a3e17a89 1643 else
36992080 1644 SetString(&printer->job_sheets[1], "none");
a3e17a89 1645 }
04d756fc 1646 if ((attr = ippFindAttribute(con->request, "requesting-user-name-allowed",
1647 IPP_TAG_ZERO)) != NULL)
1648 {
1649 FreePrinterUsers(printer);
1650
1651 printer->deny_users = 0;
753453e4 1652 if (attr->value_tag == IPP_TAG_NAME &&
1653 (attr->num_values > 1 ||
1654 strcmp(attr->values[0].string.text, "all") != 0))
04d756fc 1655 for (i = 0; i < attr->num_values; i ++)
1656 AddPrinterUser(printer, attr->values[i].string.text);
1657 }
1658 else if ((attr = ippFindAttribute(con->request, "requesting-user-name-denied",
1659 IPP_TAG_ZERO)) != NULL)
1660 {
1661 FreePrinterUsers(printer);
1662
1663 printer->deny_users = 1;
753453e4 1664 if (attr->value_tag == IPP_TAG_NAME &&
1665 (attr->num_values > 1 ||
1666 strcmp(attr->values[0].string.text, "none") != 0))
04d756fc 1667 for (i = 0; i < attr->num_values; i ++)
1668 AddPrinterUser(printer, attr->values[i].string.text);
1669 }
1670 if ((attr = ippFindAttribute(con->request, "job-quota-period",
1671 IPP_TAG_INTEGER)) != NULL)
1672 {
320115a7 1673 LogMessage(L_DEBUG, "add_printer: Setting job-quota-period to %d...",
1674 attr->values[0].integer);
04d756fc 1675 FreeQuotas(printer);
1676 printer->quota_period = attr->values[0].integer;
1677 }
1678 if ((attr = ippFindAttribute(con->request, "job-k-limit",
1679 IPP_TAG_INTEGER)) != NULL)
1680 {
320115a7 1681 LogMessage(L_DEBUG, "add_printer: Setting job-k-limit to %d...",
1682 attr->values[0].integer);
04d756fc 1683 FreeQuotas(printer);
1684 printer->k_limit = attr->values[0].integer;
1685 }
1686 if ((attr = ippFindAttribute(con->request, "job-page-limit",
1687 IPP_TAG_INTEGER)) != NULL)
1688 {
320115a7 1689 LogMessage(L_DEBUG, "add_printer: Setting job-page-limit to %d...",
1690 attr->values[0].integer);
04d756fc 1691 FreeQuotas(printer);
1692 printer->page_limit = attr->values[0].integer;
1693 }
8e504b4d 1694 if ((attr = ippFindAttribute(con->request, "printer-op-policy", IPP_TAG_TEXT)) != NULL)
1695 {
99baf768 1696 cupsd_policy_t *p; /* Policy */
8e504b4d 1697
1698
99baf768 1699 if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
8e504b4d 1700 {
1701 LogMessage(L_DEBUG, "add_printer: Setting printer-op-policy to \"%s\"...",
1702 attr->values[0].string.text);
1703 SetString(&printer->op_policy, attr->values[0].string.text);
1704 printer->op_policy_ptr = p;
1705 }
1706 else
1707 {
1708 LogMessage(L_ERROR, "add_printer: Unknown printer-op-policy \"%s\"...",
1709 attr->values[0].string.text);
1710 send_ipp_error(con, IPP_NOT_POSSIBLE);
1711 return;
1712 }
1713 }
1714 if ((attr = ippFindAttribute(con->request, "printer-error-policy", IPP_TAG_TEXT)) != NULL)
1715 {
0f9c1053 1716 if (strcmp(attr->values[0].string.text, "abort-job") &&
1717 strcmp(attr->values[0].string.text, "retry-job") &&
1718 strcmp(attr->values[0].string.text, "stop-printer"))
1719 {
1720 LogMessage(L_ERROR, "add_printer: Unknown printer-error-policy \"%s\"...",
1721 attr->values[0].string.text);
1722 send_ipp_error(con, IPP_NOT_POSSIBLE);
1723 return;
1724 }
1725
8e504b4d 1726 LogMessage(L_DEBUG, "add_printer: Setting printer-error-policy to \"%s\"...",
1727 attr->values[0].string.text);
1728 SetString(&printer->error_policy, attr->values[0].string.text);
1729 }
c7fa9d06 1730
1731 /*
1732 * See if we have all required attributes...
1733 */
1734
36992080 1735 if (!printer->device_uri)
1736 SetString(&printer->device_uri, "file:/dev/null");
c7fa9d06 1737
1738 /*
1739 * See if we have an interface script or PPD file attached to the request...
1740 */
1741
fc757c63 1742 if (con->filename)
753453e4 1743 {
0e4f2c25 1744 strlcpy(srcfile, con->filename, sizeof(srcfile));
d4c438d4 1745
0e4f2c25 1746 if ((fp = cupsFileOpen(srcfile, "rb")) != NULL)
1747 {
1748 /*
1749 * Yes; get the first line from it...
1750 */
8496650b 1751
0e4f2c25 1752 line[0] = '\0';
1753 cupsFileGets(fp, line, sizeof(line));
1754 cupsFileClose(fp);
753453e4 1755
0e4f2c25 1756 /*
1757 * Then see what kind of file it is...
1758 */
753453e4 1759
0e4f2c25 1760 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
1761 printer->name);
c7fa9d06 1762
0e4f2c25 1763 if (strncmp(line, "*PPD-Adobe", 10) == 0)
1764 {
1765 /*
1766 * The new file is a PPD file, so remove any old interface script
1767 * that might be lying around...
1768 */
c7fa9d06 1769
0e4f2c25 1770 unlink(dstfile);
1771 }
1772 else
1773 {
1774 /*
1775 * This must be an interface script, so move the file over to the
1776 * interfaces directory and make it executable...
1777 */
1778
1779 if (copy_file(srcfile, dstfile))
1780 {
1781 LogMessage(L_ERROR, "add_printer: Unable to copy interface script from %s to %s - %s!",
1782 srcfile, dstfile, strerror(errno));
1783 send_ipp_error(con, IPP_INTERNAL_ERROR);
1784 return;
1785 }
1786 else
1787 {
1788 LogMessage(L_DEBUG, "add_printer: Copied interface script successfully!");
1789 chmod(dstfile, 0755);
1790 }
1791 }
1792
1793 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
1794 printer->name);
1795
a04b77d5 1796 if (!strncmp(line, "*PPD-Adobe", 10))
0e4f2c25 1797 {
1798 /*
1799 * The new file is a PPD file, so move the file over to the
1800 * ppd directory and make it readable by all...
1801 */
c7fa9d06 1802
0e4f2c25 1803 if (copy_file(srcfile, dstfile))
1804 {
1805 LogMessage(L_ERROR, "add_printer: Unable to copy PPD file from %s to %s - %s!",
1806 srcfile, dstfile, strerror(errno));
1807 send_ipp_error(con, IPP_INTERNAL_ERROR);
1808 return;
1809 }
1810 else
1811 {
1812 LogMessage(L_DEBUG, "add_printer: Copied PPD file successfully!");
1813 chmod(dstfile, 0644);
1814 }
1815 }
1816 else
1817 {
1818 /*
1819 * This must be an interface script, so remove any old PPD file that
1820 * may be lying around...
1821 */
c7fa9d06 1822
0e4f2c25 1823 unlink(dstfile);
1824 }
1825 }
1826 }
1827 else if ((attr = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME)) != NULL)
1828 {
a04b77d5 1829 if (!strcmp(attr->values[0].string.text, "raw"))
c7fa9d06 1830 {
1831 /*
0e4f2c25 1832 * Raw driver, remove any existing PPD or interface script files.
c7fa9d06 1833 */
1834
0e4f2c25 1835 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
1836 printer->name);
1837 unlink(dstfile);
1838
1839 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
1840 printer->name);
d4c438d4 1841 unlink(dstfile);
c7fa9d06 1842 }
1843 else
1844 {
1845 /*
0e4f2c25 1846 * PPD model file...
c7fa9d06 1847 */
1848
0e4f2c25 1849 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
1850 printer->name);
1851 unlink(dstfile);
c7fa9d06 1852
0e4f2c25 1853 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
1854 printer->name);
c7fa9d06 1855
a04b77d5 1856 if (copy_model(con, attr->values[0].string.text, dstfile))
3b41df31 1857 {
bd917997 1858 LogMessage(L_ERROR, "add_printer: Unable to copy PPD file from %s to %s - %s!",
a04b77d5 1859 attr->values[0].string.text, dstfile, strerror(errno));
1b918e3a 1860 send_ipp_error(con, IPP_INTERNAL_ERROR);
3b41df31 1861 return;
1862 }
1863 else
8496650b 1864 {
1865 LogMessage(L_DEBUG, "add_printer: Copied PPD file successfully!");
d4c438d4 1866 chmod(dstfile, 0644);
8496650b 1867 }
c7fa9d06 1868 }
c7fa9d06 1869 }
1870
4621b86f 1871 /*
1872 * Make this printer the default if there is none...
1873 */
1874
1875 if (DefaultPrinter == NULL)
1876 DefaultPrinter = printer;
1877
c7fa9d06 1878 /*
1879 * Update the printer attributes and return...
1880 */
1881
3270670b 1882 SetPrinterAttrs(printer);
cc0561c6 1883 SaveAllPrinters();
ccd04319 1884
1885 if (printer->job != NULL)
1886 {
aa25b9e1 1887 job_t *job;
1888
ccd04319 1889 /*
1890 * Stop the current job and then restart it below...
1891 */
1892
aa25b9e1 1893 job = (job_t *)printer->job;
1894
1895 StopJob(job->id, 1);
1896 job->state->values[0].integer = IPP_JOB_PENDING;
ccd04319 1897 }
1898
47afc1d2 1899 CheckJobs();
cc0561c6 1900
3a74d6bf 1901 WritePrintcap();
1902
d6f1ff9a 1903 if (modify)
42f94780 1904 {
1905 cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, printer, NULL,
1906 "Printer \'%s\' modified by \'%s\'.", printer->name,
1907 con->username);
1908
d6f1ff9a 1909 LogMessage(L_INFO, "Printer \'%s\' modified by \'%s\'.", printer->name,
1910 con->username);
42f94780 1911 }
d6f1ff9a 1912 else
068e9059 1913 {
1914 AddPrinterHistory(printer);
1915
42f94780 1916 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, printer, NULL,
1917 "New printer \'%s\' added by \'%s\'.", printer->name,
1918 con->username);
1919
d6f1ff9a 1920 LogMessage(L_INFO, "New printer \'%s\' added by \'%s\'.", printer->name,
1921 con->username);
068e9059 1922 }
c7fa9d06 1923
0a3ac972 1924 con->response->request.status.status_code = IPP_OK;
e31bfb6e 1925}
1926
1927
7ebf3a09 1928/*
1929 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
1930 * based upon the printer state...
1931 */
1932
1933static void
a4b3db80 1934add_printer_state_reasons(
1935 client_t *con, /* I - Client connection */
1936 printer_t *p) /* I - Printer info */
7ebf3a09 1937{
b2e10895 1938 LogMessage(L_DEBUG2, "add_printer_state_reasons(%p[%d], %p[%s])\n",
1939 con, con->http.fd, p, p->name);
dbb05cda 1940
62bcac22 1941 if (p->num_reasons == 0)
1942 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1943 "printer-state-reasons", NULL,
1944 p->state == IPP_PRINTER_STOPPED ? "paused" : "none");
1945 else
1946 ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1947 "printer-state-reasons", p->num_reasons, NULL,
1948 (const char * const *)p->reasons);
7ebf3a09 1949}
1950
1951
1952/*
1953 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
1954 * the specified printer or class.
1955 */
1956
1957static void
1958add_queued_job_count(client_t *con, /* I - Client connection */
1959 printer_t *p) /* I - Printer or class */
1960{
7ebf3a09 1961 int count; /* Number of jobs on destination */
1962
1963
b2e10895 1964 LogMessage(L_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])\n",
1965 con, con->http.fd, p, p->name);
dbb05cda 1966
7b1b1c6e 1967 count = GetPrinterJobCount(p->name);
7ebf3a09 1968
1969 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1970 "queued-job-count", count);
1971}
1972
1973
48e211f3 1974/*
1975 * 'authenticate_job()' - Set job authentication info.
1976 */
1977
1978static void
1979authenticate_job(client_t *con, /* I - Client connection */
1980 ipp_attribute_t *uri) /* I - Job URI */
1981{
1982 ipp_attribute_t *attr; /* Job-id attribute */
1983 int jobid; /* Job ID */
1984 job_t *job; /* Current job */
1985 char method[HTTP_MAX_URI],
1986 /* Method portion of URI */
1987 username[HTTP_MAX_URI],
1988 /* Username portion of URI */
1989 host[HTTP_MAX_URI],
1990 /* Host portion of URI */
1991 resource[HTTP_MAX_URI];
1992 /* Resource portion of URI */
1993 int port; /* Port portion of URI */
1994
1995
1996 LogMessage(L_DEBUG2, "authenticate_job(%p[%d], %s)\n", con, con->http.fd,
1997 uri->values[0].string.text);
1998
1999 /*
2000 * Start with "everything is OK" status...
2001 */
2002
2003 con->response->request.status.status_code = IPP_OK;
2004
2005 /*
2006 * See if we have a job URI or a printer URI...
2007 */
2008
2009 if (!strcmp(uri->name, "printer-uri"))
2010 {
2011 /*
2012 * Got a printer URI; see if we also have a job-id attribute...
2013 */
2014
2015 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
2016 {
2017 LogMessage(L_ERROR, "authenticate_job: got a printer-uri attribute but no job-id!");
2018 send_ipp_error(con, IPP_BAD_REQUEST);
2019 return;
2020 }
2021
2022 jobid = attr->values[0].integer;
2023 }
2024 else
2025 {
2026 /*
2027 * Got a job URI; parse it to get the job ID...
2028 */
2029
2030 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
2031
2032 if (strncmp(resource, "/jobs/", 6))
2033 {
2034 /*
2035 * Not a valid URI!
2036 */
2037
2038 LogMessage(L_ERROR, "authenticate_job: bad job-uri attribute \'%s\'!\n",
2039 uri->values[0].string.text);
2040 send_ipp_error(con, IPP_BAD_REQUEST);
2041 return;
2042 }
2043
2044 jobid = atoi(resource + 6);
2045 }
2046
2047 /*
2048 * See if the job exists...
2049 */
2050
2051 if ((job = FindJob(jobid)) == NULL)
2052 {
2053 /*
2054 * Nope - return a "not found" error...
2055 */
2056
2057 LogMessage(L_ERROR, "authenticate_job: job #%d doesn't exist!", jobid);
2058 send_ipp_error(con, IPP_NOT_FOUND);
2059 return;
2060 }
2061
2062 /*
2063 * See if the job has been completed...
2064 */
2065
2066 if (job->state->values[0].integer != IPP_JOB_HELD)
2067 {
2068 /*
2069 * Return a "not-possible" error...
2070 */
2071
2072 LogMessage(L_ERROR, "authenticate_job: job #%d is not held for authentication!", jobid);
2073 send_ipp_error(con, IPP_NOT_POSSIBLE);
2074 return;
2075 }
2076
2077 /*
2078 * See if we have already authenticated...
2079 */
2080
2081 if (!con->username[0])
2082 {
2083 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2084 return;
2085 }
2086
2087 /*
2088 * See if the job is owned by the requesting user...
2089 */
2090
2091 if (!validate_user(job, con, job->username, username, sizeof(username)))
2092 {
2093 LogMessage(L_ERROR, "authenticate_job: \"%s\" not authorized to authenticate job id %d owned by \"%s\"!",
2094 username, jobid, job->username);
2095 send_ipp_error(con, IPP_FORBIDDEN);
2096 return;
2097 }
2098
2099 /*
2100 * Save the authentication information for this job...
2101 */
2102
2103 save_auth_info(con, job->id);
2104
2105 /*
2106 * Reset the job-hold-until value to "no-hold"...
2107 */
2108
2109 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
2110 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
2111
2112 if (attr != NULL)
2113 {
2114 attr->value_tag = IPP_TAG_KEYWORD;
2115 SetString(&(attr->values[0].string.text), "no-hold");
2116 }
2117
2118 /*
2119 * Release the job and return...
2120 */
2121
2122 ReleaseJob(jobid);
2123
2124 LogMessage(L_INFO, "Job %d was authenticated by \'%s\'.", jobid,
2125 con->username);
2126}
2127
2128
f3d580b9 2129/*
2130 * 'cancel_all_jobs()' - Cancel all print jobs.
2131 */
2132
2133static void
2134cancel_all_jobs(client_t *con, /* I - Client connection */
2135 ipp_attribute_t *uri) /* I - Job or Printer URI */
2136{
a4b3db80 2137 const char *dest; /* Destination */
2138 cups_ptype_t dtype; /* Destination type */
2139 char method[HTTP_MAX_URI], /* Method portion of URI */
2140 userpass[HTTP_MAX_URI], /* Username portion of URI */
2141 host[HTTP_MAX_URI], /* Host portion of URI */
2142 resource[HTTP_MAX_URI]; /* Resource portion of URI */
2143 int port; /* Port portion of URI */
2144 ipp_attribute_t *attr; /* Attribute in request */
2145 const char *username; /* Username */
2146 int purge; /* Purge? */
2147 printer_t *printer; /* Printer */
f3d580b9 2148
2149
b2e10895 2150 LogMessage(L_DEBUG2, "cancel_all_jobs(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 2151 uri->values[0].string.text);
f3d580b9 2152
2153 /*
2154 * Was this operation called from the correct URI?
2155 */
2156
3f5dfefd 2157 if (strncmp(con->uri, "/admin/", 7) &&
2158 strncmp(con->uri, "/jobs/", 7))
f3d580b9 2159 {
b2e10895 2160 LogMessage(L_ERROR, "cancel_all_jobs: admin request on bad resource \'%s\'!",
1124e9ec 2161 con->uri);
f3d580b9 2162 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2163 return;
2164 }
2165
2166 /*
2167 * See if we have a printer URI...
2168 */
2169
3f5dfefd 2170 if (strcmp(uri->name, "printer-uri"))
f3d580b9 2171 {
5ea8888e 2172 LogMessage(L_ERROR, "cancel_all_jobs: bad %s attribute \'%s\'!",
bd84e0d1 2173 uri->name, uri->values[0].string.text);
f3d580b9 2174 send_ipp_error(con, IPP_BAD_REQUEST);
2175 return;
2176 }
2177
dd9e85de 2178 /*
2179 * Get the username (if any) for the jobs we want to cancel (only if
2180 * "my-jobs" is specified...
2181 */
2182
2183 if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL &&
2184 attr->values[0].boolean)
2185 {
2186 if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
2187 username = attr->values[0].string.text;
2188 else
2189 {
2190 LogMessage(L_ERROR, "cancel_all_jobs: missing requesting-user-name attribute!");
2191 send_ipp_error(con, IPP_BAD_REQUEST);
2192 return;
2193 }
2194 }
2195 else
2196 username = NULL;
2197
3f5dfefd 2198 if ((!username ||
2199 (username && con->username[0] && strcmp(username, con->username))) &&
2200 strncmp(con->uri, "/admin/", 7))
2201 {
2202 LogMessage(L_ERROR, "cancel_all_jobs: only administrators can cancel "
2203 "other users\' jobs!");
2204 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2205 return;
2206 }
2207
dd9e85de 2208 /*
2209 * Look for the "purge-jobs" attribute...
2210 */
2211
2212 if ((attr = ippFindAttribute(con->request, "purge-jobs", IPP_TAG_BOOLEAN)) != NULL)
2213 purge = attr->values[0].boolean;
2214 else
2215 purge = 1;
2216
f3d580b9 2217 /*
2218 * And if the destination is valid...
2219 */
2220
dd9e85de 2221 httpSeparate(uri->values[0].string.text, method, userpass, host, &port,
f3d580b9 2222 resource);
2223
bd5510a5 2224 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
f3d580b9 2225 {
2226 /*
50b9556e 2227 * Bad URI?
f3d580b9 2228 */
2229
50b9556e 2230 if (strcmp(resource, "/printers/") != 0)
2231 {
2232 LogMessage(L_ERROR, "cancel_all_jobs: resource name \'%s\' no good!", resource);
2233 send_ipp_error(con, IPP_NOT_FOUND);
2234 return;
2235 }
f3d580b9 2236
bd5510a5 2237 /*
2238 * Check policy...
2239 */
2240
99baf768 2241 if (!cupsdCheckPolicy(DefaultPolicyPtr, con, NULL))
bd5510a5 2242 {
2243 LogMessage(L_ERROR, "cancel_all_jobs: not authorized!");
2244 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2245 return;
2246 }
2247
50b9556e 2248 /*
2249 * Cancel all jobs on all printers...
2250 */
f3d580b9 2251
dd9e85de 2252 CancelJobs(NULL, username, purge);
2253
2254 LogMessage(L_INFO, "All jobs were %s by \'%s\'.",
2255 purge ? "purged" : "cancelled", con->username);
50b9556e 2256 }
2257 else
2258 {
bd5510a5 2259 /*
2260 * Check policy...
2261 */
2262
99baf768 2263 if (!cupsdCheckPolicy(printer->op_policy_ptr, con, NULL))
bd5510a5 2264 {
2265 LogMessage(L_ERROR, "cancel_all_jobs: not authorized!");
2266 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2267 return;
2268 }
2269
50b9556e 2270 /*
2271 * Cancel all of the jobs on the named printer...
2272 */
2273
dd9e85de 2274 CancelJobs(dest, username, purge);
2275
2276 LogMessage(L_INFO, "All jobs on \'%s\' were %s by \'%s\'.", dest,
2277 purge ? "purged" : "cancelled", con->username);
50b9556e 2278 }
f3d580b9 2279
0a3ac972 2280 con->response->request.status.status_code = IPP_OK;
f3d580b9 2281}
2282
2283
1d2c70a6 2284/*
2285 * 'cancel_job()' - Cancel a print job.
2286 */
2287
e31bfb6e 2288static void
1d2c70a6 2289cancel_job(client_t *con, /* I - Client connection */
2290 ipp_attribute_t *uri) /* I - Job or Printer URI */
e31bfb6e 2291{
a4b3db80 2292 ipp_attribute_t *attr; /* Current attribute */
2293 int jobid; /* Job ID */
2294 char method[HTTP_MAX_URI], /* Method portion of URI */
2295 username[HTTP_MAX_URI], /* Username portion of URI */
2296 host[HTTP_MAX_URI], /* Host portion of URI */
2297 resource[HTTP_MAX_URI]; /* Resource portion of URI */
2298 int port; /* Port portion of URI */
2299 job_t *job; /* Job information */
2300 const char *dest; /* Destination */
2301 cups_ptype_t dtype; /* Destination type (printer or class) */
2302 printer_t *printer; /* Printer data */
1d2c70a6 2303
2304
b2e10895 2305 LogMessage(L_DEBUG2, "cancel_job(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 2306 uri->values[0].string.text);
1d2c70a6 2307
2aeb2b1d 2308 /*
2309 * Verify that the POST operation was done to a valid URI.
2310 */
2311
2312 if (strncmp(con->uri, "/classes/", 9) != 0 &&
1124e9ec 2313 strncmp(con->uri, "/jobs/", 5) != 0 &&
2aeb2b1d 2314 strncmp(con->uri, "/printers/", 10) != 0)
2315 {
5ea8888e 2316 LogMessage(L_ERROR, "cancel_job: cancel request on bad resource \'%s\'!",
1124e9ec 2317 con->uri);
2aeb2b1d 2318 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2319 return;
2320 }
2321
1d2c70a6 2322 /*
2323 * See if we have a job URI or a printer URI...
2324 */
2325
2326 if (strcmp(uri->name, "printer-uri") == 0)
2327 {
2328 /*
2329 * Got a printer URI; see if we also have a job-id attribute...
2330 */
2331
c0341b41 2332 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
1d2c70a6 2333 {
5ea8888e 2334 LogMessage(L_ERROR, "cancel_job: got a printer-uri attribute but no job-id!");
1d2c70a6 2335 send_ipp_error(con, IPP_BAD_REQUEST);
2336 return;
2337 }
2338
50b9556e 2339 if ((jobid = attr->values[0].integer) == 0)
2340 {
2341 /*
2342 * Find the current job on the specified printer...
2343 */
2344
2345 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
2346
bd5510a5 2347 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
50b9556e 2348 {
2349 /*
2350 * Bad URI...
2351 */
2352
2353 LogMessage(L_ERROR, "cancel_job: resource name \'%s\' no good!", resource);
2354 send_ipp_error(con, IPP_NOT_FOUND);
2355 return;
2356 }
2357
50b9556e 2358 /*
2359 * See if the printer is currently printing a job...
2360 */
2361
2362 if (printer->job)
2363 jobid = ((job_t *)printer->job)->id;
2364 else
2365 {
2366 /*
2367 * No, see if there are any pending jobs...
2368 */
2369
2370 for (job = Jobs; job != NULL; job = job->next)
2371 if (job->state->values[0].integer <= IPP_JOB_PROCESSING &&
2372 strcasecmp(job->dest, dest) == 0)
2373 break;
2374
2375 if (job != NULL)
2376 jobid = job->id;
2377 else
2378 {
2379 LogMessage(L_ERROR, "cancel_job: No active jobs on %s!", dest);
2380 send_ipp_error(con, IPP_NOT_POSSIBLE);
2381 return;
2382 }
2383 }
2384 }
1d2c70a6 2385 }
2386 else
2387 {
2388 /*
2389 * Got a job URI; parse it to get the job ID...
2390 */
2391
c0341b41 2392 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1d2c70a6 2393
2394 if (strncmp(resource, "/jobs/", 6) != 0)
2395 {
2396 /*
2397 * Not a valid URI!
2398 */
2399
5ea8888e 2400 LogMessage(L_ERROR, "cancel_job: bad job-uri attribute \'%s\'!",
bd84e0d1 2401 uri->values[0].string.text);
1d2c70a6 2402 send_ipp_error(con, IPP_BAD_REQUEST);
2403 return;
2404 }
2405
2406 jobid = atoi(resource + 6);
2407 }
2408
2409 /*
2410 * See if the job exists...
2411 */
2412
3b41df31 2413 if ((job = FindJob(jobid)) == NULL)
1d2c70a6 2414 {
2415 /*
2416 * Nope - return a "not found" error...
2417 */
2418
5ea8888e 2419 LogMessage(L_ERROR, "cancel_job: job #%d doesn't exist!", jobid);
1d2c70a6 2420 send_ipp_error(con, IPP_NOT_FOUND);
2421 return;
2422 }
2423
3b41df31 2424 /*
2425 * See if the job is owned by the requesting user...
2426 */
2427
bd5510a5 2428 if (!validate_user(job, con, job->username, username, sizeof(username)))
3b41df31 2429 {
ed3e11d8 2430 LogMessage(L_ERROR, "cancel_job: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
2431 username, jobid, job->username);
2432 send_ipp_error(con, IPP_FORBIDDEN);
2433 return;
3b41df31 2434 }
2435
7f51c82b 2436 /*
2437 * See if the job is already completed, cancelled, or aborted; if so,
2438 * we can't cancel...
2439 */
2440
2441 if (job->state->values[0].integer >= IPP_JOB_CANCELLED)
2442 {
2443 LogMessage(L_ERROR, "cancel_job: job id %d is %s - can't cancel!",
2444 jobid,
2445 job->state->values[0].integer == IPP_JOB_CANCELLED ? "cancelled" :
2446 job->state->values[0].integer == IPP_JOB_ABORTED ? "aborted" :
2447 "completed");
2448 send_ipp_error(con, IPP_NOT_POSSIBLE);
2449 return;
2450 }
2451
1d2c70a6 2452 /*
2453 * Cancel the job and return...
2454 */
2455
fd0624de 2456 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
2457 "Job cancelled by \'%s\'.", username);
2458
d65c1374 2459 CancelJob(jobid, 0);
96df88bb 2460 CheckJobs();
1d2c70a6 2461
c8f336b5 2462 LogMessage(L_INFO, "Job %d was cancelled by \'%s\'.", jobid, username);
cc0561c6 2463
0a3ac972 2464 con->response->request.status.status_code = IPP_OK;
e31bfb6e 2465}
2466
2467
a4b3db80 2468/*
2469 * 'cancel_subscription()' - Cancel a subscription.
2470 */
2471
2472static void
2473cancel_subscription(client_t *con, /* I - Client connection */
2474 int sub_id) /* I - Subscription ID */
2475{
2476}
2477
2478
b521f3fc 2479/*
2480 * 'check_quotas()' - Check quotas for a printer and user.
2481 */
2482
a4b3db80 2483static int /* O - 1 if OK, 0 if not */
2484check_quotas(client_t *con, /* I - Client connection */
2485 printer_t *p) /* I - Printer or class */
b521f3fc 2486{
9d0c9f28 2487 int i; /* Looping var */
a4b3db80 2488 ipp_attribute_t *attr; /* Current attribute */
2489 char username[33]; /* Username */
2490 quota_t *q; /* Quota data */
2491 struct passwd *pw; /* User password data */
b521f3fc 2492
2493
b2e10895 2494 LogMessage(L_DEBUG2, "check_quotas(%p[%d], %p[%s])\n",
2495 con, con->http.fd, p, p->name);
dbb05cda 2496
b521f3fc 2497 /*
2498 * Check input...
2499 */
2500
2501 if (con == NULL || p == NULL)
2502 return (0);
2503
b521f3fc 2504 /*
2505 * Figure out who is printing...
2506 */
2507
2508 attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME);
2509
2510 if (con->username[0])
def978d5 2511 strlcpy(username, con->username, sizeof(username));
b521f3fc 2512 else if (attr != NULL)
2513 {
2514 LogMessage(L_DEBUG, "check_quotas: requesting-user-name = \'%s\'",
2515 attr->values[0].string.text);
2516
def978d5 2517 strlcpy(username, attr->values[0].string.text, sizeof(username));
b521f3fc 2518 }
2519 else
2520 strcpy(username, "anonymous");
2521
7b1b1c6e 2522 /*
2523 * Check global active job limits for printers and users...
2524 */
2525
2526 if (MaxJobsPerPrinter)
2527 {
2528 /*
2529 * Check if there are too many pending jobs on this printer...
2530 */
2531
2532 if (GetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
2533 {
2534 LogMessage(L_INFO, "Too many jobs for printer \"%s\"...", p->name);
2535 return (0);
2536 }
2537 }
2538
2539 if (MaxJobsPerUser)
2540 {
2541 /*
2542 * Check if there are too many pending jobs for this user...
2543 */
2544
8bc932be 2545 if (GetUserJobCount(username) >= MaxJobsPerUser)
7b1b1c6e 2546 {
2547 LogMessage(L_INFO, "Too many jobs for user \"%s\"...", username);
2548 return (0);
2549 }
2550 }
2551
b521f3fc 2552 /*
2553 * Check against users...
2554 */
2555
7b1b1c6e 2556 if (p->num_users == 0 && p->k_limit == 0 && p->page_limit == 0)
2557 return (1);
2558
b521f3fc 2559 if (p->num_users)
2560 {
b27fa031 2561 pw = getpwnam(username);
2562 endpwent();
2563
b521f3fc 2564 for (i = 0; i < p->num_users; i ++)
b27fa031 2565 if (p->users[i][0] == '@')
2566 {
2567 /*
2568 * Check group membership...
2569 */
2570
9d0c9f28 2571 if (cupsdCheckGroup(username, pw, p->users[i] + 1))
2572 break;
b27fa031 2573 }
2574 else if (!strcasecmp(username, p->users[i]))
b521f3fc 2575 break;
2576
2577 if ((i < p->num_users) == p->deny_users)
2578 {
2579 LogMessage(L_INFO, "Denying user \"%s\" access to printer \"%s\"...",
2580 username, p->name);
2581 return (0);
2582 }
2583 }
2584
2585 /*
2586 * Check quotas...
2587 */
2588
2589 if (p->k_limit || p->page_limit)
2590 {
2591 if ((q = UpdateQuota(p, username, 0, 0)) == NULL)
2592 {
2593 LogMessage(L_ERROR, "Unable to allocate quota data for user \"%s\"!",
2594 username);
2595 return (0);
2596 }
2597
2598 if ((q->k_count >= p->k_limit && p->k_limit) ||
2599 (q->page_count >= p->page_limit && p->page_limit))
2600 {
2601 LogMessage(L_INFO, "User \"%s\" is over the quota limit...",
2602 username);
2603 return (0);
2604 }
2605 }
2606
2607 /*
2608 * If we have gotten this far, we're done!
2609 */
2610
2611 return (1);
2612}
2613
2614
962e5a9f 2615/*
2616 * 'copy_attribute()' - Copy a single attribute.
2617 */
2618
8fc34542 2619static ipp_attribute_t * /* O - New attribute */
a4b3db80 2620copy_attribute(
2621 ipp_t *to, /* O - Destination request/response */
2622 ipp_attribute_t *attr, /* I - Attribute to copy */
2623 int quickcopy) /* I - Do a quick copy? */
962e5a9f 2624{
2625 int i; /* Looping var */
2626 ipp_attribute_t *toattr; /* Destination attribute */
2627
2628
b2e10895 2629 LogMessage(L_DEBUG2, "copy_attribute(%p, %p[%s,%x,%x])\n", to, attr,
2630 attr->name ? attr->name : "(null)", attr->group_tag,
2631 attr->value_tag);
dbb05cda 2632
753453e4 2633 switch (attr->value_tag & ~IPP_TAG_COPY)
962e5a9f 2634 {
2635 case IPP_TAG_ZERO :
a444baaa 2636 toattr = ippAddSeparator(to);
962e5a9f 2637 break;
2638
2639 case IPP_TAG_INTEGER :
2640 case IPP_TAG_ENUM :
2641 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
2642 attr->name, attr->num_values, NULL);
2643
2644 for (i = 0; i < attr->num_values; i ++)
2645 toattr->values[i].integer = attr->values[i].integer;
2646 break;
2647
2648 case IPP_TAG_BOOLEAN :
2649 toattr = ippAddBooleans(to, attr->group_tag, attr->name,
2650 attr->num_values, NULL);
2651
2652 for (i = 0; i < attr->num_values; i ++)
2653 toattr->values[i].boolean = attr->values[i].boolean;
2654 break;
2655
2656 case IPP_TAG_STRING :
2657 case IPP_TAG_TEXT :
2658 case IPP_TAG_NAME :
2659 case IPP_TAG_KEYWORD :
2660 case IPP_TAG_URI :
2661 case IPP_TAG_URISCHEME :
2662 case IPP_TAG_CHARSET :
2663 case IPP_TAG_LANGUAGE :
2664 case IPP_TAG_MIMETYPE :
b6ea8f29 2665 toattr = ippAddStrings(to, attr->group_tag,
753453e4 2666 (ipp_tag_t)(attr->value_tag | quickcopy),
2667 attr->name, attr->num_values, NULL, NULL);
962e5a9f 2668
753453e4 2669 if (quickcopy)
2670 {
2671 for (i = 0; i < attr->num_values; i ++)
2672 toattr->values[i].string.text = attr->values[i].string.text;
2673 }
2674 else
2675 {
2676 for (i = 0; i < attr->num_values; i ++)
2677 toattr->values[i].string.text = strdup(attr->values[i].string.text);
2678 }
962e5a9f 2679 break;
2680
2681 case IPP_TAG_DATE :
2682 toattr = ippAddDate(to, attr->group_tag, attr->name,
2683 attr->values[0].date);
2684 break;
2685
2686 case IPP_TAG_RESOLUTION :
2687 toattr = ippAddResolutions(to, attr->group_tag, attr->name,
2688 attr->num_values, IPP_RES_PER_INCH,
2689 NULL, NULL);
2690
2691 for (i = 0; i < attr->num_values; i ++)
2692 {
2693 toattr->values[i].resolution.xres = attr->values[i].resolution.xres;
2694 toattr->values[i].resolution.yres = attr->values[i].resolution.yres;
2695 toattr->values[i].resolution.units = attr->values[i].resolution.units;
2696 }
2697 break;
2698
2699 case IPP_TAG_RANGE :
2700 toattr = ippAddRanges(to, attr->group_tag, attr->name,
2701 attr->num_values, NULL, NULL);
2702
2703 for (i = 0; i < attr->num_values; i ++)
2704 {
2705 toattr->values[i].range.lower = attr->values[i].range.lower;
2706 toattr->values[i].range.upper = attr->values[i].range.upper;
2707 }
2708 break;
2709
2710 case IPP_TAG_TEXTLANG :
2711 case IPP_TAG_NAMELANG :
b6ea8f29 2712 toattr = ippAddStrings(to, attr->group_tag,
753453e4 2713 (ipp_tag_t)(attr->value_tag | quickcopy),
962e5a9f 2714 attr->name, attr->num_values, NULL, NULL);
2715
753453e4 2716 if (quickcopy)
2717 {
2718 for (i = 0; i < attr->num_values; i ++)
2719 {
2720 toattr->values[i].string.charset = attr->values[i].string.charset;
2721 toattr->values[i].string.text = attr->values[i].string.text;
2722 }
2723 }
2724 else
962e5a9f 2725 {
753453e4 2726 for (i = 0; i < attr->num_values; i ++)
2727 {
2728 if (!i)
2729 toattr->values[i].string.charset =
2730 strdup(attr->values[i].string.charset);
2731 else
2732 toattr->values[i].string.charset =
2733 toattr->values[0].string.charset;
2734
2735 toattr->values[i].string.text = strdup(attr->values[i].string.text);
2736 }
962e5a9f 2737 }
2738 break;
2739
62bcac22 2740 case IPP_TAG_BEGIN_COLLECTION :
2741 toattr = ippAddCollections(to, attr->group_tag, attr->name,
2742 attr->num_values, NULL);
2743
2744 for (i = 0; i < attr->num_values; i ++)
2745 {
2746 toattr->values[i].collection = ippNew();
2747 copy_attrs(toattr->values[i].collection, attr->values[i].collection,
2748 NULL, IPP_TAG_ZERO, 0);
2749 }
2750 break;
2751
2752 default :
962e5a9f 2753 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
2754 attr->name, attr->num_values, NULL);
2755
2756 for (i = 0; i < attr->num_values; i ++)
2757 {
2758 toattr->values[i].unknown.length = attr->values[i].unknown.length;
2759
2760 if (toattr->values[i].unknown.length > 0)
2761 {
2762 if ((toattr->values[i].unknown.data = malloc(toattr->values[i].unknown.length)) == NULL)
2763 toattr->values[i].unknown.length = 0;
2764 else
2765 memcpy(toattr->values[i].unknown.data,
2766 attr->values[i].unknown.data,
2767 toattr->values[i].unknown.length);
2768 }
2769 }
2770 break; /* anti-compiler-warning-code */
2771 }
8fc34542 2772
2773 return (toattr);
962e5a9f 2774}
2775
2776
1d2c70a6 2777/*
2778 * 'copy_attrs()' - Copy attributes from one request to another.
2779 */
2780
e31bfb6e 2781static void
d6de4648 2782copy_attrs(ipp_t *to, /* I - Destination request */
2783 ipp_t *from, /* I - Source request */
4979ce3d 2784 ipp_attribute_t *req, /* I - Requested attributes */
9ef400ff 2785 ipp_tag_t group, /* I - Group to copy */
2786 int quickcopy) /* I - Do a quick copy? */
e31bfb6e 2787{
1d2c70a6 2788 int i; /* Looping var */
962e5a9f 2789 ipp_attribute_t *fromattr; /* Source attribute */
1d2c70a6 2790
2791
dbb05cda 2792 LogMessage(L_DEBUG2, "copy_attrs(%p, %p, %p, %x)\n", to, from, req, group);
1d2c70a6 2793
2794 if (to == NULL || from == NULL)
2795 return;
2796
c7fa9d06 2797 if (req != NULL && strcmp(req->values[0].string.text, "all") == 0)
2798 req = NULL; /* "all" means no filter... */
2799
1d2c70a6 2800 for (fromattr = from->attrs; fromattr != NULL; fromattr = fromattr->next)
2801 {
d6de4648 2802 /*
2803 * Filter attributes as needed...
2804 */
2805
d4c438d4 2806 if (group != IPP_TAG_ZERO && fromattr->group_tag != group &&
2807 fromattr->group_tag != IPP_TAG_ZERO)
2808 continue;
2809
2810 if (req != NULL && fromattr->name != NULL)
d6de4648 2811 {
2812 for (i = 0; i < req->num_values; i ++)
2813 if (strcmp(fromattr->name, req->values[i].string.text) == 0)
2814 break;
2815
2816 if (i == req->num_values)
2817 continue;
2818 }
2819
9ef400ff 2820 copy_attribute(to, fromattr, quickcopy);
1d2c70a6 2821 }
e31bfb6e 2822}
2823
2824
bd84e0d1 2825/*
0e4f2c25 2826 * 'copy_banner()' - Copy a banner file to the requests directory for the
2827 * specified job.
bd84e0d1 2828 */
2829
a4b3db80 2830static int /* O - Size of banner file in kbytes */
2831copy_banner(client_t *con, /* I - Client connection */
2832 job_t *job, /* I - Job information */
2833 const char *name) /* I - Name of banner */
bd84e0d1 2834{
a4b3db80 2835 int i; /* Looping var */
2836 int kbytes; /* Size of banner file in kbytes */
2837 char filename[1024]; /* Job filename */
2838 banner_t *banner; /* Pointer to banner */
2839 cups_file_t *in; /* Input file */
2840 cups_file_t *out; /* Output file */
2841 int ch; /* Character from file */
2842 char attrname[255], /* Name of attribute */
2843 *s; /* Pointer into name */
2844 ipp_attribute_t *attr; /* Attribute */
bd84e0d1 2845
2846
b2e10895 2847 LogMessage(L_DEBUG2, "copy_banner(%p[%d], %p[%d], %s)",
2848 con, con->http.fd, job, job->id, name ? name : "(null)");
bd84e0d1 2849
2850 /*
0e4f2c25 2851 * Find the banner; return if not found or "none"...
bd84e0d1 2852 */
2853
0e4f2c25 2854 if (name == NULL ||
2855 strcmp(name, "none") == 0 ||
2856 (banner = FindBanner(name)) == NULL)
2857 return (0);
bd84e0d1 2858
2859 /*
0e4f2c25 2860 * Open the banner and job files...
bd84e0d1 2861 */
2862
0e4f2c25 2863 if (add_file(con, job, banner->filetype, 0))
2864 return (0);
bd84e0d1 2865
0e4f2c25 2866 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
2867 job->num_files);
2868 if ((out = cupsFileOpen(filename, "w")) == NULL)
2869 {
2870 LogMessage(L_ERROR, "copy_banner: Unable to create banner job file %s - %s",
2871 filename, strerror(errno));
2872 job->num_files --;
2873 return (0);
2874 }
2875
2876 fchmod(cupsFileNumber(out), 0640);
48033118 2877 fchown(cupsFileNumber(out), RunUser, Group);
0e4f2c25 2878
e7bede57 2879 /*
2880 * Try the localized banner file under the subdirectory...
2881 */
2882
2883 strlcpy(attrname, con->request->attrs->next->values[0].string.text,
2884 sizeof(attrname));
2885 if (strlen(attrname) > 2 && attrname[2] == '-')
bd84e0d1 2886 {
2887 /*
e7bede57 2888 * Convert ll-cc to ll_CC...
bd84e0d1 2889 */
2890
e7bede57 2891 attrname[2] = '_';
2892 attrname[3] = toupper(attrname[3] & 255);
2893 attrname[4] = toupper(attrname[4] & 255);
2894 }
bd84e0d1 2895
e7bede57 2896 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
2897 attrname, name);
99de6da0 2898
e7bede57 2899 if (access(filename, 0) && strlen(attrname) > 2)
2900 {
2901 /*
2902 * Wasn't able to find "ll_CC" locale file; try the non-national
2903 * localization banner directory.
2904 */
0e4f2c25 2905
e7bede57 2906 attrname[2] = '\0';
0e4f2c25 2907
e7bede57 2908 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
2909 attrname, name);
1049abbe 2910 }
e7bede57 2911
2912 if (access(filename, 0))
1049abbe 2913 {
0e4f2c25 2914 /*
2915 * Use the non-localized banner file.
2916 */
bd84e0d1 2917
0e4f2c25 2918 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
1049abbe 2919 }
2920
0e4f2c25 2921 if ((in = cupsFileOpen(filename, "r")) == NULL)
bd84e0d1 2922 {
0e4f2c25 2923 cupsFileClose(out);
2924 unlink(filename);
2925 LogMessage(L_ERROR, "copy_banner: Unable to open banner template file %s - %s",
2926 filename, strerror(errno));
2927 job->num_files --;
2928 return (0);
bd84e0d1 2929 }
2930
879062a9 2931 /*
0e4f2c25 2932 * Parse the file to the end...
879062a9 2933 */
2934
0e4f2c25 2935 while ((ch = cupsFileGetChar(in)) != EOF)
2936 if (ch == '{')
879062a9 2937 {
0e4f2c25 2938 /*
2939 * Get an attribute name...
2940 */
879062a9 2941
0e4f2c25 2942 for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
da275f55 2943 if (!isalpha(ch & 255) && ch != '-' && ch != '?')
0e4f2c25 2944 break;
2945 else if (s < (attrname + sizeof(attrname) - 1))
2946 *s++ = ch;
2947 else
2948 break;
bce0c833 2949
0e4f2c25 2950 *s = '\0';
bce0c833 2951
0e4f2c25 2952 if (ch != '}')
2953 {
2954 /*
2955 * Ignore { followed by stuff that is not an attribute name...
2956 */
d7845573 2957
af57319f 2958 cupsFilePrintf(out, "{%s%c", attrname, ch);
0e4f2c25 2959 continue;
2960 }
d7845573 2961
0e4f2c25 2962 /*
2963 * See if it is defined...
2964 */
d7845573 2965
0e4f2c25 2966 if (attrname[0] == '?')
2967 s = attrname + 1;
2968 else
2969 s = attrname;
d7845573 2970
0e4f2c25 2971 if (strcmp(s, "printer-name") == 0)
2972 {
2973 cupsFilePuts(out, job->dest);
2974 continue;
2975 }
2976 else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
2977 {
2978 /*
2979 * See if we have a leading question mark...
2980 */
bd84e0d1 2981
0e4f2c25 2982 if (attrname[0] != '?')
2983 {
2984 /*
2985 * Nope, write to file as-is; probably a PostScript procedure...
2986 */
bd84e0d1 2987
0e4f2c25 2988 cupsFilePrintf(out, "{%s}", attrname);
2989 }
bd84e0d1 2990
0e4f2c25 2991 continue;
2992 }
bd84e0d1 2993
0e4f2c25 2994 /*
2995 * Output value(s)...
2996 */
bd84e0d1 2997
0e4f2c25 2998 for (i = 0; i < attr->num_values; i ++)
2999 {
3000 if (i)
3001 cupsFilePutChar(out, ',');
17b95e13 3002
0e4f2c25 3003 switch (attr->value_tag)
3004 {
3005 case IPP_TAG_INTEGER :
3006 case IPP_TAG_ENUM :
3007 if (strncmp(s, "time-at-", 8) == 0)
3008 cupsFilePuts(out, GetDateTime(attr->values[i].integer));
3009 else
3010 cupsFilePrintf(out, "%d", attr->values[i].integer);
3011 break;
bd84e0d1 3012
0e4f2c25 3013 case IPP_TAG_BOOLEAN :
3014 cupsFilePrintf(out, "%d", attr->values[i].boolean);
3015 break;
1049abbe 3016
0e4f2c25 3017 case IPP_TAG_NOVALUE :
3018 cupsFilePuts(out, "novalue");
3019 break;
bd84e0d1 3020
0e4f2c25 3021 case IPP_TAG_RANGE :
3022 cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
3023 attr->values[i].range.upper);
3024 break;
238bae16 3025
0e4f2c25 3026 case IPP_TAG_RESOLUTION :
3027 cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
3028 attr->values[i].resolution.yres,
3029 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3030 "dpi" : "dpc");
3031 break;
238bae16 3032
0e4f2c25 3033 case IPP_TAG_URI :
3034 case IPP_TAG_STRING :
3035 case IPP_TAG_TEXT :
3036 case IPP_TAG_NAME :
3037 case IPP_TAG_KEYWORD :
3038 case IPP_TAG_CHARSET :
3039 case IPP_TAG_LANGUAGE :
3040 if (strcasecmp(banner->filetype->type, "postscript") == 0)
3041 {
3042 /*
3043 * Need to quote strings for PS banners...
3044 */
238bae16 3045
0e4f2c25 3046 const char *p;
238bae16 3047
0e4f2c25 3048 for (p = attr->values[i].string.text; *p; p ++)
3049 {
3050 if (*p == '(' || *p == ')' || *p == '\\')
3051 {
3052 cupsFilePutChar(out, '\\');
3053 cupsFilePutChar(out, *p);
3054 }
3055 else if (*p < 32 || *p > 126)
6f83172d 3056 cupsFilePrintf(out, "\\%03o", *p & 255);
0e4f2c25 3057 else
3058 cupsFilePutChar(out, *p);
3059 }
238bae16 3060 }
0e4f2c25 3061 else
3062 cupsFilePuts(out, attr->values[i].string.text);
3063 break;
238bae16 3064
0e4f2c25 3065 default :
3066 break; /* anti-compiler-warning-code */
3067 }
238bae16 3068 }
0e4f2c25 3069 }
3070 else if (ch == '\\') /* Quoted char */
3071 {
3072 ch = cupsFileGetChar(in);
238bae16 3073
0e4f2c25 3074 if (ch != '{') /* Only do special handling for \{ */
3075 cupsFilePutChar(out, '\\');
238bae16 3076
0e4f2c25 3077 cupsFilePutChar(out, ch);
238bae16 3078 }
0e4f2c25 3079 else
3080 cupsFilePutChar(out, ch);
238bae16 3081
0e4f2c25 3082 cupsFileClose(in);
3083
3084 kbytes = (cupsFileTell(out) + 1023) / 1024;
3085
3086 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
3087 attr->values[0].integer += kbytes;
3088
3089 cupsFileClose(out);
3090
3091 return (kbytes);
3092}
3093
3094
3095/*
3096 * 'copy_file()' - Copy a PPD file or interface script...
3097 */
3098
3099static int /* O - 0 = success, -1 = error */
3100copy_file(const char *from, /* I - Source file */
3101 const char *to) /* I - Destination file */
3102{
3103 cups_file_t *src, /* Source file */
3104 *dst; /* Destination file */
3105 int bytes; /* Bytes to read/write */
3106 char buffer[2048]; /* Copy buffer */
3107
3108
3109 LogMessage(L_DEBUG2, "copy_file(\"%s\", \"%s\")\n", from, to);
3110
3111 /*
3112 * Open the source and destination file for a copy...
3113 */
3114
3115 if ((src = cupsFileOpen(from, "rb")) == NULL)
3116 return (-1);
3117
3118 if ((dst = cupsFileOpen(to, "wb")) == NULL)
3119 {
3120 cupsFileClose(src);
3121 return (-1);
238bae16 3122 }
3123
0e4f2c25 3124 /*
3125 * Copy the source file to the destination...
3126 */
3127
3128 while ((bytes = cupsFileRead(src, buffer, sizeof(buffer))) > 0)
3129 if (cupsFileWrite(dst, buffer, bytes) < bytes)
3130 {
3131 cupsFileClose(src);
3132 cupsFileClose(dst);
3133 return (-1);
3134 }
7ebf3a09 3135
e903a8f6 3136 /*
0e4f2c25 3137 * Close both files and return...
e903a8f6 3138 */
3139
0e4f2c25 3140 cupsFileClose(src);
e903a8f6 3141
0e4f2c25 3142 return (cupsFileClose(dst));
3143}
91606789 3144
e583bc2d 3145
0e4f2c25 3146/*
3147 * 'copy_model()' - Copy a PPD model file, substituting default values
3148 * as needed...
3149 */
4b96a841 3150
0e4f2c25 3151static int /* O - 0 = success, -1 = error */
a04b77d5 3152copy_model(client_t *con, /* I - Client connection */
3153 const char *from, /* I - Source file */
0e4f2c25 3154 const char *to) /* I - Destination file */
3155{
92847388 3156 fd_set *input; /* select() input set */
3157 struct timeval timeout; /* select() timeout */
e38f5343 3158 int maxfd; /* Maximum file descriptor for select() */
a04b77d5 3159 char tempfile[1024]; /* Temporary PPD file */
3160 int tempfd; /* Temporary PPD file descriptor */
3161 int temppid; /* Process ID of cups-driverd */
92847388 3162 int temppipe[2]; /* Temporary pipes */
a04b77d5 3163 char *argv[4], /* Command-line arguments */
3164 *envp[100]; /* Environment */
0e4f2c25 3165 cups_file_t *src, /* Source file */
3166 *dst; /* Destination file */
92847388 3167 int bytes, /* Bytes from pipe */
3168 total; /* Total bytes from pipe */
d17cde3c 3169 char buffer[2048], /* Copy buffer */
3170 *ptr; /* Pointer into buffer */
0e4f2c25 3171 int i; /* Looping var */
3172 char option[PPD_MAX_NAME], /* Option name */
3173 choice[PPD_MAX_NAME]; /* Choice name */
3174 int num_defaults; /* Number of default options */
3175 ppd_default_t *defaults; /* Default options */
09b90a68 3176 char cups_protocol[PPD_MAX_LINE];
3177 /* cupsProtocol attribute */
d17cde3c 3178 int have_letter, /* Have Letter size */
3179 have_a4; /* Have A4 size */
b4f2ed46 3180#ifdef HAVE_LIBPAPER
3181 char *paper_result; /* Paper size name from libpaper */
3182 char system_paper[64]; /* Paper size name buffer */
3183#endif /* HAVE_LIBPAPER */
e583bc2d 3184
a3e17a89 3185
a04b77d5 3186 LogMessage(L_DEBUG2, "copy_model(con=%p, from=\"%s\", to=\"%s\")\n",
3187 con, from, to);
3188
3189 /*
3190 * Run cups-driverd to get the PPD file...
3191 */
3192
3193 argv[0] = "cups-driverd";
3194 argv[1] = "cat";
3195 argv[2] = (char *)from;
3196 argv[3] = NULL;
3197
3198 cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
3199
3200 snprintf(buffer, sizeof(buffer), "%s/daemon/cups-driverd", ServerBin);
3201 snprintf(tempfile, sizeof(tempfile), "%s/%d.ppd", TempDir, con->http.fd);
3202 tempfd = open(tempfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
3203 if (tempfd < 0)
3204 return (-1);
3205
92847388 3206 cupsdOpenPipe(temppipe);
a04b77d5 3207
92847388 3208 if ((input = calloc(1, SetSize)) == NULL)
a04b77d5 3209 {
3210 close(tempfd);
3211 unlink(tempfile);
92847388 3212
3213 LogMessage(L_ERROR, "copy_model: Unable to allocate %d bytes for select()...",
3214 SetSize);
a04b77d5 3215 return (-1);
3216 }
3217
92847388 3218 LogMessage(L_DEBUG, "copy_model: Running \"cups-driverd cat %s\"...", from);
a04b77d5 3219
e38f5343 3220 if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
3221 -1, 0, &temppid))
a04b77d5 3222 {
92847388 3223 close(tempfd);
a04b77d5 3224 unlink(tempfile);
3225 return (-1);
3226 }
3227
92847388 3228 close(temppipe[1]);
3229
a04b77d5 3230 /*
92847388 3231 * Wait up to 30 seconds for the PPD file to be copied...
a04b77d5 3232 */
3233
92847388 3234 total = 0;
3235
e38f5343 3236 if (temppipe[0] > CGIPipes[0])
3237 maxfd = temppipe[0] + 1;
3238 else
3239 maxfd = CGIPipes[0] + 1;
3240
92847388 3241 for (;;)
a04b77d5 3242 {
92847388 3243 /*
3244 * See if we have data ready...
3245 */
3246
3247 bytes = 0;
3248
3249 FD_SET(temppipe[0], input);
e38f5343 3250 FD_SET(CGIPipes[0], input);
92847388 3251
3252 timeout.tv_sec = 30;
3253 timeout.tv_usec = 0;
3254
e38f5343 3255 if ((i = select(maxfd, input, NULL, NULL, &timeout)) < 0)
92847388 3256 {
3257 if (errno == EINTR)
3258 continue;
3259 else
3260 break;
3261 }
e38f5343 3262 else if (i == 0)
3263 {
3264 /*
3265 * We have timed out...
3266 */
92847388 3267
92847388 3268 break;
e38f5343 3269 }
92847388 3270
e38f5343 3271 if (FD_ISSET(temppipe[0], input))
92847388 3272 {
e38f5343 3273 /*
3274 * Read the PPD file from the pipe, and write it to the PPD file.
3275 */
3276
3277 if ((bytes = read(temppipe[0], buffer, sizeof(buffer))) > 0)
3278 {
3279 if (write(tempfd, buffer, bytes) < bytes)
3280 break;
92847388 3281
e38f5343 3282 total += bytes;
3283 }
3284 else
3285 break;
92847388 3286 }
e38f5343 3287
3288 if (FD_ISSET(CGIPipes[0], input))
3289 UpdateCGI();
a04b77d5 3290 }
3291
92847388 3292 close(temppipe[0]);
3293 close(tempfd);
3294
3295 if (!total)
a04b77d5 3296 {
92847388 3297 /*
3298 * No data from cups-deviced...
3299 */
3300
a04b77d5 3301 LogMessage(L_ERROR, "copy_model: empty PPD file!");
3302 unlink(tempfile);
3303 return (-1);
3304 }
753453e4 3305
d17cde3c 3306 /*
3307 * Read the source file and see what page sizes are supported...
3308 */
3309
a04b77d5 3310 if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
3311 {
3312 unlink(tempfile);
3313 return (-1);
3314 }
3315
d17cde3c 3316 have_letter = 0;
3317 have_a4 = 0;
3318
d17cde3c 3319 while (cupsFileGets(src, buffer, sizeof(buffer)) != NULL)
3320 if (!strncmp(buffer, "*PageSize ", 10))
3321 {
3322 /*
3323 * Strip UI text and command data from the end of the line...
3324 */
3325
3326 if ((ptr = strchr(buffer + 10, '/')) != NULL)
3327 *ptr = '\0';
3328 if ((ptr = strchr(buffer + 10, ':')) != NULL)
3329 *ptr = '\0';
3330
3331 for (ptr = buffer + 10; isspace(*ptr); ptr ++);
3332
3333 /*
3334 * Look for Letter and A4 page sizes...
3335 */
3336
3337 if (!strcmp(ptr, "Letter"))
3338 have_letter = 1;
3339
3340 if (!strcmp(ptr, "A4"))
3341 have_a4 = 1;
3342 }
3343
3344 cupsFileRewind(src);
3345
0e4f2c25 3346 /*
3347 * Open the destination (if possible) and set the default options...
3348 */
a3e17a89 3349
09b90a68 3350 num_defaults = 0;
3351 defaults = NULL;
3352 cups_protocol[0] = '\0';
b5cb0608 3353
0e4f2c25 3354 if ((dst = cupsFileOpen(to, "rb")) != NULL)
3355 {
d11458ff 3356 /*
0e4f2c25 3357 * Read all of the default lines from the old PPD...
d11458ff 3358 */
3359
adf75dba 3360 while (cupsFileGets(dst, buffer, sizeof(buffer)) != NULL)
0e4f2c25 3361 if (!strncmp(buffer, "*Default", 8))
d11458ff 3362 {
3363 /*
0e4f2c25 3364 * Add the default option...
d11458ff 3365 */
3366
0e4f2c25 3367 if (!ppd_parse_line(buffer, option, sizeof(option),
3368 choice, sizeof(choice)))
3369 num_defaults = ppd_add_default(option, choice, num_defaults,
3370 &defaults);
d11458ff 3371 }
09b90a68 3372 else if (!strncmp(buffer, "*cupsProtocol:", 14))
3373 strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
d11458ff 3374
0e4f2c25 3375 cupsFileClose(dst);
3376 }
b4f2ed46 3377#ifdef HAVE_LIBPAPER
3378 else if ((paper_result = systempapername()) != NULL)
3379 {
3380 /*
3381 * Set the default media sizes from the systemwide default...
3382 */
3383
3384 strlcpy(system_paper, paper_result, sizeof(system_paper));
da275f55 3385 system_paper[0] = toupper(system_paper[0] & 255);
b4f2ed46 3386
d17cde3c 3387 if ((!strcmp(system_paper, "Letter") && have_letter) ||
3388 (!strcmp(system_paper, "A4") && have_a4))
3389 {
3390 num_defaults = ppd_add_default("PageSize", system_paper,
3391 num_defaults, &defaults);
3392 num_defaults = ppd_add_default("PageRegion", system_paper,
3393 num_defaults, &defaults);
3394 num_defaults = ppd_add_default("PaperDimension", system_paper,
3395 num_defaults, &defaults);
3396 num_defaults = ppd_add_default("ImageableArea", system_paper,
3397 num_defaults, &defaults);
3398 }
b4f2ed46 3399 }
3400#endif /* HAVE_LIBPAPER */
0e4f2c25 3401 else
3402 {
a3e17a89 3403 /*
0e4f2c25 3404 * Add the default media sizes...
3405 *
3406 * Note: These values are generally not valid for large-format devices
3407 * like plotters, however it is probably safe to say that those
3408 * users will configure the media size after initially adding
3409 * the device anyways...
a3e17a89 3410 */
3411
0e4f2c25 3412 if (!DefaultLanguage ||
3413 !strcasecmp(DefaultLanguage, "C") ||
3414 !strcasecmp(DefaultLanguage, "POSIX") ||
3415 !strcasecmp(DefaultLanguage, "en") ||
3416 !strncasecmp(DefaultLanguage, "en_US", 5) ||
3417 !strncasecmp(DefaultLanguage, "en_CA", 5) ||
3418 !strncasecmp(DefaultLanguage, "fr_CA", 5))
01c9aafc 3419 {
0e4f2c25 3420 /*
3421 * These are the only locales that will default to "letter" size...
3422 */
b521f3fc 3423
d17cde3c 3424 if (have_letter)
3425 {
3426 num_defaults = ppd_add_default("PageSize", "Letter", num_defaults,
3427 &defaults);
3428 num_defaults = ppd_add_default("PageRegion", "Letter", num_defaults,
3429 &defaults);
3430 num_defaults = ppd_add_default("PaperDimension", "Letter", num_defaults,
3431 &defaults);
3432 num_defaults = ppd_add_default("ImageableArea", "Letter", num_defaults,
3433 &defaults);
3434 }
0e4f2c25 3435 }
d17cde3c 3436 else if (have_a4)
0e4f2c25 3437 {
3438 /*
3439 * The rest default to "a4" size...
3440 */
3441
3442 num_defaults = ppd_add_default("PageSize", "A4", num_defaults,
3443 &defaults);
3444 num_defaults = ppd_add_default("PageRegion", "A4", num_defaults,
3445 &defaults);
3446 num_defaults = ppd_add_default("PaperDimension", "A4", num_defaults,
3447 &defaults);
3448 num_defaults = ppd_add_default("ImageableArea", "A4", num_defaults,
3449 &defaults);
01c9aafc 3450 }
a3e17a89 3451 }
e903a8f6 3452
3453 /*
d17cde3c 3454 * Open the destination file for a copy...
e903a8f6 3455 */
4979ce3d 3456
0e4f2c25 3457 if ((dst = cupsFileOpen(to, "wb")) == NULL)
3458 {
ff40b65e 3459 if (num_defaults > 0)
3460 free(defaults);
3461
0e4f2c25 3462 cupsFileClose(src);
a04b77d5 3463 unlink(tempfile);
0e4f2c25 3464 return (-1);
3465 }
bd84e0d1 3466
3467 /*
0e4f2c25 3468 * Copy the source file to the destination...
bd84e0d1 3469 */
3470
0e4f2c25 3471 while (cupsFileGets(src, buffer, sizeof(buffer)) != NULL)
3472 {
3473 if (!strncmp(buffer, "*Default", 8))
3474 {
3475 /*
3476 * Check for an previous default option choice...
3477 */
99de6da0 3478
0e4f2c25 3479 if (!ppd_parse_line(buffer, option, sizeof(option),
3480 choice, sizeof(choice)))
3481 {
3482 for (i = 0; i < num_defaults; i ++)
3483 if (!strcmp(option, defaults[i].option))
3484 {
3485 /*
3486 * Substitute the previous choice...
3487 */
bd84e0d1 3488
0e4f2c25 3489 snprintf(buffer, sizeof(buffer), "*Default%s: %s", option,
3490 defaults[i].choice);
3491 break;
3492 }
3493 }
3494 }
bd84e0d1 3495
0e4f2c25 3496 cupsFilePrintf(dst, "%s\n", buffer);
3497 }
bd84e0d1 3498
09b90a68 3499 if (cups_protocol[0])
3500 cupsFilePrintf(dst, "%s\n", cups_protocol);
3501
ff40b65e 3502 if (num_defaults > 0)
3503 free(defaults);
3504
0e4f2c25 3505 /*
3506 * Close both files and return...
3507 */
3508
3509 cupsFileClose(src);
3510
a04b77d5 3511 unlink(tempfile);
3512
0e4f2c25 3513 return (cupsFileClose(dst));
bd84e0d1 3514}
3515
3516
e903a8f6 3517/*
0e4f2c25 3518 * 'create_job()' - Print a file to a printer or class.
e903a8f6 3519 */
3520
0e4f2c25 3521static void
3522create_job(client_t *con, /* I - Client connection */
3523 ipp_attribute_t *uri) /* I - Printer URI */
e903a8f6 3524{
a4b3db80 3525 ipp_attribute_t *attr; /* Current attribute */
3526 const char *dest; /* Destination */
3527 cups_ptype_t dtype; /* Destination type (printer or class) */
3528 int priority; /* Job priority */
3529 char *title; /* Job name/title */
3530 job_t *job; /* Current job */
3531 char job_uri[HTTP_MAX_URI], /* Job URI */
3532 method[HTTP_MAX_URI], /* Method portion of URI */
3533 username[HTTP_MAX_URI], /* Username portion of URI */
3534 host[HTTP_MAX_URI], /* Host portion of URI */
3535 resource[HTTP_MAX_URI]; /* Resource portion of URI */
3536 int port; /* Port portion of URI */
3537 printer_t *printer; /* Printer data */
3538 int kbytes; /* Size of print file */
3539 int i; /* Looping var */
3540 int lowerpagerange; /* Page range bound */
e903a8f6 3541
3542
b2e10895 3543 LogMessage(L_DEBUG2, "create_job(%p[%d], %s)\n", con, con->http.fd,
0e4f2c25 3544 uri->values[0].string.text);
dbb05cda 3545
e903a8f6 3546 /*
0e4f2c25 3547 * Verify that the POST operation was done to a valid URI.
e903a8f6 3548 */
3549
0e4f2c25 3550 if (strncmp(con->uri, "/classes/", 9) != 0 &&
3551 strncmp(con->uri, "/printers/", 10) != 0)
3552 {
3553 LogMessage(L_ERROR, "create_job: cancel request on bad resource \'%s\'!",
3554 con->uri);
3555 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3556 return;
3557 }
3558
3559 /*
3560 * Is the destination valid?
3561 */
3562
3563 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3564
bd5510a5 3565 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
0e4f2c25 3566 {
3567 /*
3568 * Bad URI...
3569 */
3570
3571 LogMessage(L_ERROR, "create_job: resource name \'%s\' no good!", resource);
3572 send_ipp_error(con, IPP_NOT_FOUND);
3573 return;
3574 }
3575
25392f52 3576 /*
3577 * Check remote printing to non-shared printer...
3578 */
3579
3580 if (!printer->shared &&
3581 strcasecmp(con->http.hostname, "localhost") &&
3582 strcasecmp(con->http.hostname, ServerName))
3583 {
3584 LogMessage(L_ERROR, "print_job: printer not shared!");
3585 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3586 return;
3587 }
3588
0e4f2c25 3589 /*
bd5510a5 3590 * Check policy...
0e4f2c25 3591 */
3592
48e211f3 3593 if (!cupsdCheckPolicy(printer->op_policy_ptr, con, NULL) ||
3594 ((printer->type & CUPS_PRINTER_AUTHENTICATED) && !con->username[0]))
0e4f2c25 3595 {
bd5510a5 3596 LogMessage(L_ERROR, "create_job: not authorized!");
3597 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3598 return;
0e4f2c25 3599 }
0e4f2c25 3600
bd5510a5 3601 /*
3602 * See if the printer is accepting jobs...
3603 */
0e4f2c25 3604
3605 if (!printer->accepting)
3606 {
3607 LogMessage(L_INFO, "create_job: destination \'%s\' is not accepting jobs.",
3608 dest);
3609 send_ipp_error(con, IPP_NOT_ACCEPTING);
3610 return;
3611 }
3612
3613 /*
3614 * Validate job template attributes; for now just copies and page-ranges...
3615 */
3616
3617 if ((attr = ippFindAttribute(con->request, "copies", IPP_TAG_INTEGER)) != NULL)
3618 {
3619 if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
3620 {
6248387b 3621 LogMessage(L_ERROR, "create_job: Bad copies value %d.",
0e4f2c25 3622 attr->values[0].integer);
6248387b 3623 send_ipp_error(con, IPP_ATTRIBUTES);
3624 ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
3625 "copies", attr->values[0].integer);
0e4f2c25 3626 return;
3627 }
3628 }
3629
3630 if ((attr = ippFindAttribute(con->request, "page-ranges", IPP_TAG_RANGE)) != NULL)
3631 {
3632 for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
3633 {
3634 if (attr->values[i].range.lower < lowerpagerange ||
3635 attr->values[i].range.lower > attr->values[i].range.upper)
3636 {
3637 LogMessage(L_ERROR, "create_job: bad page-ranges values %d-%d.",
3638 attr->values[i].range.lower, attr->values[i].range.upper);
3639 send_ipp_error(con, IPP_BAD_REQUEST);
3640 return;
3641 }
ebde520c 3642
0e4f2c25 3643 lowerpagerange = attr->values[i].range.upper + 1;
3644 }
3645 }
e903a8f6 3646
3647 /*
0e4f2c25 3648 * Make sure we aren't over our limit...
e903a8f6 3649 */
3650
0e4f2c25 3651 if (NumJobs >= MaxJobs && MaxJobs)
3652 CleanJobs();
e903a8f6 3653
0e4f2c25 3654 if (NumJobs >= MaxJobs && MaxJobs)
e903a8f6 3655 {
0e4f2c25 3656 LogMessage(L_INFO, "create_job: too many jobs.");
3657 send_ipp_error(con, IPP_NOT_POSSIBLE);
3658 return;
e903a8f6 3659 }
3660
0e4f2c25 3661 if (!check_quotas(con, printer))
753453e4 3662 {
0e4f2c25 3663 send_ipp_error(con, IPP_NOT_POSSIBLE);
3664 return;
3665 }
753453e4 3666
0e4f2c25 3667 /*
3668 * Create the job and set things up...
3669 */
753453e4 3670
0e4f2c25 3671 if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
3672 priority = attr->values[0].integer;
3673 else
3674 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
3675 priority = 50);
753453e4 3676
0e4f2c25 3677 if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
3678 title = attr->values[0].string.text;
3679 else
3680 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
3681 title = "Untitled");
753453e4 3682
0e4f2c25 3683 if ((job = AddJob(priority, printer->name)) == NULL)
3684 {
3685 LogMessage(L_ERROR, "create_job: unable to add job for destination \'%s\'!",
3686 dest);
3687 send_ipp_error(con, IPP_INTERNAL_ERROR);
3688 return;
3689 }
753453e4 3690
0e4f2c25 3691 job->dtype = dtype;
3692 job->attrs = con->request;
3693 con->request = NULL;
753453e4 3694
0e4f2c25 3695 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
3696
3697 if (con->username[0])
48e211f3 3698 {
0e4f2c25 3699 SetString(&job->username, con->username);
48e211f3 3700 save_auth_info(con, job->id);
3701 }
0e4f2c25 3702 else if (attr != NULL)
753453e4 3703 {
0e4f2c25 3704 LogMessage(L_DEBUG, "create_job: requesting-user-name = \'%s\'",
3705 attr->values[0].string.text);
753453e4 3706
0e4f2c25 3707 SetString(&job->username, attr->values[0].string.text);
753453e4 3708 }
0e4f2c25 3709 else
3710 SetString(&job->username, "anonymous");
753453e4 3711
0e4f2c25 3712 if (attr == NULL)
3713 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
3714 NULL, job->username);
3715 else
e903a8f6 3716 {
0e4f2c25 3717 attr->group_tag = IPP_TAG_JOB;
3718 SetString(&attr->name, "job-originating-user-name");
e903a8f6 3719 }
3720
0e4f2c25 3721 if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
3722 IPP_TAG_ZERO)) != NULL)
3723 {
3724 /*
3725 * Request contains a job-originating-host-name attribute; validate it...
3726 */
e903a8f6 3727
0e4f2c25 3728 if (attr->value_tag != IPP_TAG_NAME ||
3729 attr->num_values != 1 ||
3730 strcmp(con->http.hostname, "localhost") != 0)
e903a8f6 3731 {
3732 /*
0e4f2c25 3733 * Can't override the value if we aren't connected via localhost.
3734 * Also, we can only have 1 value and it must be a name value.
e903a8f6 3735 */
3736
0e4f2c25 3737 switch (attr->value_tag)
753453e4 3738 {
0e4f2c25 3739 case IPP_TAG_STRING :
3740 case IPP_TAG_TEXTLANG :
3741 case IPP_TAG_NAMELANG :
3742 case IPP_TAG_TEXT :
3743 case IPP_TAG_NAME :
3744 case IPP_TAG_KEYWORD :
3745 case IPP_TAG_URI :
3746 case IPP_TAG_URISCHEME :
3747 case IPP_TAG_CHARSET :
3748 case IPP_TAG_LANGUAGE :
3749 case IPP_TAG_MIMETYPE :
3750 /*
3751 * Free old strings...
3752 */
753453e4 3753
0e4f2c25 3754 for (i = 0; i < attr->num_values; i ++)
3755 {
3756 free(attr->values[i].string.text);
3757 attr->values[i].string.text = NULL;
3758 if (attr->values[i].string.charset)
3759 {
3760 free(attr->values[i].string.charset);
3761 attr->values[i].string.charset = NULL;
3762 }
3763 }
753453e4 3764
0e4f2c25 3765 default :
3766 break;
753453e4 3767 }
e903a8f6 3768
3769 /*
0e4f2c25 3770 * Use the default connection hostname instead...
e903a8f6 3771 */
3772
0e4f2c25 3773 attr->value_tag = IPP_TAG_NAME;
3774 attr->num_values = 1;
3775 attr->values[0].string.text = strdup(con->http.hostname);
3776 }
bb9df75d 3777
3778 attr->group_tag = IPP_TAG_JOB;
0e4f2c25 3779 }
3780 else
3781 {
3782 /*
3783 * No job-originating-host-name attribute, so use the hostname from
3784 * the connection...
3785 */
e903a8f6 3786
0e4f2c25 3787 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
3788 "job-originating-host-name", NULL, con->http.hostname);
3789 }
e903a8f6 3790
0e4f2c25 3791 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
3792 time(NULL));
3793 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3794 "time-at-processing", 0);
3795 attr->value_tag = IPP_TAG_NOVALUE;
3796 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3797 "time-at-completed", 0);
3798 attr->value_tag = IPP_TAG_NOVALUE;
e903a8f6 3799
0e4f2c25 3800 /*
3801 * Add remaining job attributes...
3802 */
3803
3804 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
3805 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
3806 "job-state", IPP_JOB_STOPPED);
3807 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3808 "job-media-sheets-completed", 0);
3809 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
bd5510a5 3810 printer->uri);
0e4f2c25 3811 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
3812 title);
e903a8f6 3813
0e4f2c25 3814 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
3815 attr->values[0].integer = 0;
3816 else
3817 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3818 "job-k-octets", 0);
e903a8f6 3819
0e4f2c25 3820 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
3821 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
3822 if (attr == NULL)
3823 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
3824 "job-hold-until", NULL, "no-hold");
3825 if (attr != NULL && strcmp(attr->values[0].string.text, "no-hold") != 0 &&
3826 !(printer->type & CUPS_PRINTER_REMOTE))
3827 {
3828 /*
3829 * Hold job until specified time...
3830 */
e903a8f6 3831
0e4f2c25 3832 SetJobHoldUntil(job->id, attr->values[0].string.text);
3833 }
3834 else
3835 job->hold_until = time(NULL) + 60;
fd527bfb 3836
0e4f2c25 3837 job->state->values[0].integer = IPP_JOB_HELD;
fd527bfb 3838
1ed20df6 3839 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
3840 Classification)
0e4f2c25 3841 {
3842 /*
3843 * Add job sheets options...
3844 */
e903a8f6 3845
0e4f2c25 3846 if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) == NULL)
e426d516 3847 {
0e4f2c25 3848 LogMessage(L_DEBUG, "Adding default job-sheets values \"%s,%s\"...",
3849 printer->job_sheets[0], printer->job_sheets[1]);
e426d516 3850
0e4f2c25 3851 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
3852 2, NULL, NULL);
3853 attr->values[0].string.text = strdup(printer->job_sheets[0]);
3854 attr->values[1].string.text = strdup(printer->job_sheets[1]);
e426d516 3855 }
e903a8f6 3856
0e4f2c25 3857 job->job_sheets = attr;
91606789 3858
0e4f2c25 3859 /*
3860 * Enforce classification level if set...
3861 */
b521f3fc 3862
0e4f2c25 3863 if (Classification)
3864 {
9041fee2 3865 LogMessage(L_INFO, "Classification=\"%s\", ClassifyOverride=%d",
3866 Classification ? Classification : "(null)", ClassifyOverride);
3867
0e4f2c25 3868 if (ClassifyOverride)
3869 {
3870 if (strcmp(attr->values[0].string.text, "none") == 0 &&
3871 (attr->num_values == 1 ||
3872 strcmp(attr->values[1].string.text, "none") == 0))
3873 {
3874 /*
3875 * Force the leading banner to have the classification on it...
3876 */
91606789 3877
0e4f2c25 3878 SetString(&attr->values[0].string.text, Classification);
f9f084a1 3879
3880 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
3881 "job-sheets=\"%s,none\", "
3882 "job-originating-user-name=\"%s\"",
3883 job->id, Classification,
3884 job->username);
0e4f2c25 3885 }
3886 else if (attr->num_values == 2 &&
3887 strcmp(attr->values[0].string.text, attr->values[1].string.text) != 0 &&
3888 strcmp(attr->values[0].string.text, "none") != 0 &&
3889 strcmp(attr->values[1].string.text, "none") != 0)
3890 {
3891 /*
3892 * Can't put two different security markings on the same document!
3893 */
b521f3fc 3894
0e4f2c25 3895 SetString(&attr->values[1].string.text, attr->values[0].string.text);
f9f084a1 3896
3897 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
3898 "job-sheets=\"%s,%s\", "
3899 "job-originating-user-name=\"%s\"",
3900 job->id, attr->values[0].string.text,
3901 attr->values[1].string.text,
3902 job->username);
0e4f2c25 3903 }
f9f084a1 3904 else if (strcmp(attr->values[0].string.text, Classification) &&
3905 strcmp(attr->values[0].string.text, "none") &&
3906 (attr->num_values == 1 ||
3907 (strcmp(attr->values[1].string.text, Classification) &&
3908 strcmp(attr->values[1].string.text, "none"))))
3909 {
3910 if (attr->num_values == 1)
3911 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION OVERRIDDEN "
3912 "job-sheets=\"%s\", "
3913 "job-originating-user-name=\"%s\"",
3914 job->id, attr->values[0].string.text,
3915 job->username);
3916 else
3917 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION OVERRIDDEN "
9041fee2 3918 "job-sheets=\"%s,%s\",fffff "
f9f084a1 3919 "job-originating-user-name=\"%s\"",
3920 job->id, attr->values[0].string.text,
3921 attr->values[1].string.text,
3922 job->username);
3923 }
0e4f2c25 3924 }
3925 else if (strcmp(attr->values[0].string.text, Classification) != 0 &&
3926 (attr->num_values == 1 ||
3927 strcmp(attr->values[1].string.text, Classification) != 0))
3928 {
3929 /*
f9f084a1 3930 * Force the banner to have the classification on it...
0e4f2c25 3931 */
3932
9041fee2 3933 if (attr->num_values > 1 &&
3934 !strcmp(attr->values[0].string.text, attr->values[1].string.text))
3935 {
3936 SetString(&(attr->values[0].string.text), Classification);
3937 SetString(&(attr->values[1].string.text), Classification);
3938 }
3939 else
3940 {
3941 if (attr->num_values == 1 ||
3942 strcmp(attr->values[0].string.text, "none"))
3943 SetString(&(attr->values[0].string.text), Classification);
f9f084a1 3944
9041fee2 3945 if (attr->num_values > 1 &&
3946 strcmp(attr->values[1].string.text, "none"))
3947 SetString(&(attr->values[1].string.text), Classification);
3948 }
f9f084a1 3949
3950 if (attr->num_values > 1)
3951 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
3952 "job-sheets=\"%s,%s\", "
3953 "job-originating-user-name=\"%s\"",
3954 job->id, attr->values[0].string.text,
3955 attr->values[1].string.text,
3956 job->username);
3957 else
3958 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
3959 "job-sheets=\"%s\", "
3960 "job-originating-user-name=\"%s\"",
3961 job->id, Classification,
3962 job->username);
0e4f2c25 3963 }
3964 }
e903a8f6 3965
0e4f2c25 3966 /*
3967 * See if we need to add the starting sheet...
3968 */
e903a8f6 3969
1ed20df6 3970 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
0e4f2c25 3971 {
1ed20df6 3972 LogMessage(L_INFO, "Adding start banner page \"%s\" to job %d.",
3973 attr->values[0].string.text, job->id);
3974
0e4f2c25 3975 kbytes = copy_banner(con, job, attr->values[0].string.text);
5d99df62 3976
0e4f2c25 3977 UpdateQuota(printer, job->username, 0, kbytes);
3978 }
3979 }
3980 else if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
3981 job->sheets = attr;
d4c438d4 3982
fd0624de 3983 /*
3984 * Add any job subscriptions...
3985 */
3986
3987 add_job_subscriptions(con, job);
3988
3989 /*
3990 * Set all but the first two attributes to the job attributes group...
3991 */
3992
42f94780 3993 for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
fd0624de 3994 attr->group_tag = IPP_TAG_JOB;
3995
0e4f2c25 3996 /*
3997 * Save and log the job...
3998 */
3999
4000 SaveJob(job->id);
d4c438d4 4001
0e4f2c25 4002 LogMessage(L_INFO, "Job %d created on \'%s\' by \'%s\'.", job->id,
4003 job->dest, job->username);
dbb05cda 4004
277a6a9b 4005 cupsdAddEvent(CUPSD_EVENT_JOB_CREATED, printer, job, "Job created.");
4006
0e4f2c25 4007 /*
4008 * Fill in the response info...
4009 */
5d99df62 4010
0e4f2c25 4011 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
e9f54388 4012 LocalPort, job->id);
5d99df62 4013
0e4f2c25 4014 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
5d99df62 4015
0e4f2c25 4016 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
5d99df62 4017
0e4f2c25 4018 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
4019 job->state->values[0].integer);
4020
4021 con->response->request.status.status_code = IPP_OK;
5d99df62 4022}
4023
4024
a4b3db80 4025/*
4026 * 'create_subscription()' - Create a notification subscription.
4027 */
4028
4029static void
4030create_subscription(
4031 client_t *con, /* I - Client connection */
4032 ipp_attribute_t *uri) /* I - Printer URI */
4033{
4034}
4035
4036
1d2c70a6 4037/*
c7fa9d06 4038 * 'delete_printer()' - Remove a printer or class from the system.
1d2c70a6 4039 */
4040
e31bfb6e 4041static void
c7fa9d06 4042delete_printer(client_t *con, /* I - Client connection */
4043 ipp_attribute_t *uri) /* I - URI of printer or class */
e31bfb6e 4044{
a4b3db80 4045 const char *dest; /* Destination */
4046 cups_ptype_t dtype; /* Destination type (printer or class) */
4047 char method[HTTP_MAX_URI], /* Method portion of URI */
4048 username[HTTP_MAX_URI], /* Username portion of URI */
4049 host[HTTP_MAX_URI], /* Host portion of URI */
4050 resource[HTTP_MAX_URI]; /* Resource portion of URI */
4051 int port; /* Port portion of URI */
4052 printer_t *printer; /* Printer/class */
4053 char filename[1024]; /* Script/PPD filename */
c7fa9d06 4054
4055
b2e10895 4056 LogMessage(L_DEBUG2, "delete_printer(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 4057 uri->values[0].string.text);
4058
f3d580b9 4059 /*
4060 * Was this operation called from the correct URI?
4061 */
4062
4063 if (strncmp(con->uri, "/admin/", 7) != 0)
4064 {
5ea8888e 4065 LogMessage(L_ERROR, "delete_printer: admin request on bad resource \'%s\'!",
1124e9ec 4066 con->uri);
f3d580b9 4067 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4068 return;
4069 }
4070
f3d580b9 4071 /*
c7fa9d06 4072 * Do we have a valid URI?
f3d580b9 4073 */
4074
c7fa9d06 4075 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4076
bd5510a5 4077 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
f3d580b9 4078 {
c7fa9d06 4079 /*
4080 * Bad URI...
4081 */
4082
5ea8888e 4083 LogMessage(L_ERROR, "delete_printer: resource name \'%s\' no good!", resource);
c7fa9d06 4084 send_ipp_error(con, IPP_NOT_FOUND);
f3d580b9 4085 return;
4086 }
4087
e9f54388 4088 /*
4089 * Check policy...
4090 */
4091
99baf768 4092 if (!cupsdCheckPolicy(DefaultPolicyPtr, con, NULL))
e9f54388 4093 {
4094 LogMessage(L_ERROR, "delete_printer: not authorized!");
4095 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4096 return;
4097 }
4098
c7fa9d06 4099 /*
753453e4 4100 * Remove old jobs...
c7fa9d06 4101 */
4102
dd9e85de 4103 CancelJobs(dest, NULL, 1);
a6988fb1 4104
42f94780 4105 /*
4106 * Remove old subscriptions and send a "deleted printer" event...
4107 */
4108
4109 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
4110 "%s \'%s\' deleted by \'%s\'.",
4111 (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
4112 dest, con->username);
4113
4114 cupsdExpireSubscriptions(printer, NULL);
4115
753453e4 4116 /*
4117 * Remove any old PPD or script files...
4118 */
4119
a6988fb1 4120 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot, dest);
c7fa9d06 4121 unlink(filename);
4122
a6988fb1 4123 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest);
c7fa9d06 4124 unlink(filename);
4125
997cf8b0 4126 if (dtype & CUPS_PRINTER_CLASS)
082b40d2 4127 {
5ea8888e 4128 LogMessage(L_INFO, "Class \'%s\' deleted by \'%s\'.", dest,
cc0561c6 4129 con->username);
a6988fb1 4130
9b2fe6bd 4131 DeletePrinter(printer, 0);
a6988fb1 4132 SaveAllClasses();
082b40d2 4133 }
cc0561c6 4134 else
082b40d2 4135 {
5ea8888e 4136 LogMessage(L_INFO, "Printer \'%s\' deleted by \'%s\'.", dest,
cc0561c6 4137 con->username);
a6988fb1 4138
9b2fe6bd 4139 DeletePrinter(printer, 0);
a6988fb1 4140 SaveAllPrinters();
082b40d2 4141 }
cc0561c6 4142
c7fa9d06 4143 /*
4144 * Return with no errors...
4145 */
4146
0a3ac972 4147 con->response->request.status.status_code = IPP_OK;
e31bfb6e 4148}
4149
4150
1d2c70a6 4151/*
4152 * 'get_default()' - Get the default destination.
4153 */
4154
e31bfb6e 4155static void
1d2c70a6 4156get_default(client_t *con) /* I - Client connection */
e31bfb6e 4157{
6bf62b17 4158 int i; /* Looping var */
4159 ipp_attribute_t *requested, /* requested-attributes */
4160 *history; /* History collection */
4161 int need_history; /* Need to send history collection? */
e5c4f5a1 4162 char printer_uri[HTTP_MAX_URI];
4163 /* Printer URI */
4164 time_t curtime; /* Current time */
6bf62b17 4165
4166
4167 LogMessage(L_DEBUG2, "get_default(%p[%d])\n", con, con->http.fd);
1d2c70a6 4168
bd5510a5 4169 /*
4170 * Check policy...
4171 */
4172
99baf768 4173 if (!cupsdCheckPolicy(DefaultPolicyPtr, con, NULL))
bd5510a5 4174 {
4175 LogMessage(L_ERROR, "get_default: not authorized!");
4176 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4177 return;
4178 }
4179
3da4463c 4180 if (DefaultPrinter != NULL)
4181 {
e5c4f5a1 4182 /*
4183 * Copy the printer attributes to the response using requested-attributes
4184 * and document-format attributes that may be provided by the client.
4185 */
4186
4187 if (!ippFindAttribute(DefaultPrinter->attrs, "printer-uri-supported",
4188 IPP_TAG_URI))
4189 {
4190 snprintf(printer_uri, sizeof(printer_uri), "ipp://%s:%d/printers/%s",
4191 con->servername, con->serverport, DefaultPrinter->name);
4192 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
4193 "printer-uri-supported", NULL, printer_uri);
4194 LogMessage(L_DEBUG2, "printer-uri-supported=\"%s\"", printer_uri);
4195 }
4196
4197 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
4198 DefaultPrinter->state);
4199
4200 add_printer_state_reasons(con, DefaultPrinter);
4201
4202 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
4203 "printer-state-message", NULL, DefaultPrinter->state_message);
4204
4205 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
4206 DefaultPrinter->accepting);
4207 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared",
4208 DefaultPrinter->shared);
4209
4210 curtime = time(NULL);
4211 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4212 "printer-up-time", curtime);
4213 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4214 "printer-state-time", DefaultPrinter->state_time);
4215 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
4216 ippTimeToDate(curtime));
4217
4218 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4219 "printer-error-policy", NULL, DefaultPrinter->op_policy);
4220 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4221 "printer-op-policy", NULL, DefaultPrinter->op_policy);
4222
4223 add_queued_job_count(con, DefaultPrinter);
4224
6bf62b17 4225 requested = ippFindAttribute(con->request, "requested-attributes",
4226 IPP_TAG_KEYWORD);
1d2c70a6 4227
6bf62b17 4228 copy_attrs(con->response, DefaultPrinter->attrs, requested, IPP_TAG_ZERO, 0);
4229 copy_attrs(con->response, CommonData, requested, IPP_TAG_ZERO, IPP_TAG_COPY);
4230
4231 need_history = 0;
4232
4233 if (MaxPrinterHistory > 0 && DefaultPrinter->num_history > 0 && requested)
4234 {
4235 for (i = 0; i < requested->num_values; i ++)
4236 if (!strcmp(requested->values[i].string.text, "all") ||
4237 !strcmp(requested->values[i].string.text, "printer-state-history"))
4238 {
4239 need_history = 1;
4240 break;
4241 }
4242 }
4243
4244 if (need_history)
4245 {
4246 history = ippAddCollections(con->response, IPP_TAG_PRINTER,
4247 "printer-state-history",
4248 DefaultPrinter->num_history, NULL);
4249
4250 for (i = 0; i < DefaultPrinter->num_history; i ++)
4251 copy_attrs(history->values[i].collection = ippNew(),
4252 DefaultPrinter->history[i],
4253 NULL, IPP_TAG_ZERO, 0);
4254 }
4255
4256 con->response->request.status.status_code = requested ? IPP_OK_SUBST : IPP_OK;
3da4463c 4257 }
4258 else
0a3ac972 4259 con->response->request.status.status_code = IPP_NOT_FOUND;
e31bfb6e 4260}
4261
4262
bd84e0d1 4263/*
4264 * 'get_devices()' - Get the list of available devices on the local system.
4265 */
4266
4267static void
4268get_devices(client_t *con) /* I - Client connection */
4269{
08379093 4270 int i; /* Looping var */
4271 ipp_attribute_t *limit, /* Limit attribute */
4272 *requested; /* requested-attributes attribute */
1d5ef583 4273 char command[1024], /* cups-deviced command */
08379093 4274 options[1024], /* Options to pass to command */
4275 attrs[1024], /* String for requested attributes */
4276 *aptr; /* Pointer into string */
4277 int alen; /* Length of attribute value */
1d5ef583 4278
4279
b2e10895 4280 LogMessage(L_DEBUG2, "get_devices(%p[%d])\n", con, con->http.fd);
dbb05cda 4281
bd5510a5 4282 /*
4283 * Check policy...
4284 */
4285
99baf768 4286 if (!cupsdCheckPolicy(DefaultPolicyPtr, con, NULL))
bd5510a5 4287 {
4288 LogMessage(L_ERROR, "get_devices: not authorized!");
4289 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4290 return;
4291 }
4292
bd84e0d1 4293 /*
1d5ef583 4294 * Run cups-deviced command with the given options...
bd84e0d1 4295 */
4296
1d5ef583 4297 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
08379093 4298 requested = ippFindAttribute(con->request, "requested-attributes",
4299 IPP_TAG_KEYWORD);
4300
4301 if (requested)
4302 {
4303 for (i = 0, aptr = attrs; i < requested->num_values; i ++)
4304 {
4305 /*
4306 * Check that we have enough room...
4307 */
4308
4309 alen = strlen(requested->values[i].string.text);
4310 if (alen > (sizeof(attrs) - (aptr - attrs) - 2))
4311 break;
4312
4313 /*
4314 * Put commas between values...
4315 */
4316
4317 if (i)
4318 *aptr++ = ',';
4319
4320 /*
4321 * Add the value to the end of the string...
4322 */
4323
4324 strcpy(aptr, requested->values[i].string.text);
4325 aptr += alen;
4326 }
4327
4328 /*
4329 * If we have more attribute names than will fit, default to "all"...
4330 */
4331
4332 if (i < requested->num_values)
4333 strcpy(attrs, "all");
4334 }
4335 else
4336 strcpy(attrs, "all");
bd84e0d1 4337
1d5ef583 4338 snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
08379093 4339 snprintf(options, sizeof(options),
4340 "cups-deviced %d+%d+requested-attributes=%s",
1d5ef583 4341 con->request->request.op.request_id,
08379093 4342 limit ? limit->values[0].integer : 0,
4343 attrs);
1d5ef583 4344
b38d93df 4345 if (SendCommand(con, command, options, 1))
1d5ef583 4346 {
4347 /*
4348 * Command started successfully, don't send an IPP response here...
4349 */
4350
4351 ippDelete(con->response);
4352 con->response = NULL;
4353 }
4354 else
4355 {
4356 /*
4357 * Command failed, return "internal error" so the user knows something
4358 * went wrong...
4359 */
4360
4361 send_ipp_error(con, IPP_INTERNAL_ERROR);
4362 }
bd84e0d1 4363}
4364
4365
1d2c70a6 4366/*
4367 * 'get_jobs()' - Get a list of jobs for the specified printer.
4368 */
4369
e31bfb6e 4370static void
1d2c70a6 4371get_jobs(client_t *con, /* I - Client connection */
4372 ipp_attribute_t *uri) /* I - Printer URI */
e31bfb6e 4373{
a4b3db80 4374 ipp_attribute_t *attr, /* Current attribute */
4375 *requested; /* Requested attributes */
4376 const char *dest; /* Destination */
4377 cups_ptype_t dtype; /* Destination type (printer or class) */
4378 cups_ptype_t dmask; /* Destination type mask */
4379 char method[HTTP_MAX_URI], /* Method portion of URI */
4380 username[HTTP_MAX_URI], /* Username portion of URI */
4381 host[HTTP_MAX_URI], /* Host portion of URI */
4382 resource[HTTP_MAX_URI]; /* Resource portion of URI */
4383 int port; /* Port portion of URI */
4384 int completed; /* Completed jobs? */
4385 int limit; /* Maximum number of jobs to return */
4386 int count; /* Number of jobs that match */
4387 job_t *job; /* Current job pointer */
4388 char job_uri[HTTP_MAX_URI]; /* Job URI... */
4389 printer_t *printer; /* Printer */
1d2c70a6 4390
4391
b2e10895 4392 LogMessage(L_DEBUG2, "get_jobs(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 4393 uri->values[0].string.text);
1d2c70a6 4394
4395 /*
4396 * Is the destination valid?
4397 */
4398
c0341b41 4399 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1d2c70a6 4400
28a96fe2 4401 if (strcmp(resource, "/") == 0 ||
4402 (strncmp(resource, "/jobs", 5) == 0 && strlen(resource) <= 6))
1d2c70a6 4403 {
bd5510a5 4404 dest = NULL;
4405 dtype = (cups_ptype_t)0;
4406 dmask = (cups_ptype_t)0;
4407 printer = NULL;
28a96fe2 4408 }
4409 else if (strncmp(resource, "/printers", 9) == 0 && strlen(resource) <= 10)
4410 {
bd5510a5 4411 dest = NULL;
4412 dtype = (cups_ptype_t)0;
4413 dmask = CUPS_PRINTER_CLASS;
4414 printer = NULL;
1d2c70a6 4415 }
4416 else if (strncmp(resource, "/classes", 8) == 0 && strlen(resource) <= 9)
4417 {
bd5510a5 4418 dest = NULL;
4419 dtype = CUPS_PRINTER_CLASS;
4420 dmask = CUPS_PRINTER_CLASS;
4421 printer = NULL;
1d2c70a6 4422 }
bd5510a5 4423 else if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
1d2c70a6 4424 {
4425 /*
4426 * Bad URI...
4427 */
4428
5ea8888e 4429 LogMessage(L_ERROR, "get_jobs: resource name \'%s\' no good!", resource);
1d2c70a6 4430 send_ipp_error(con, IPP_NOT_FOUND);
4431 return;
4432 }
28a96fe2 4433 else
4434 dmask = CUPS_PRINTER_CLASS;
1d2c70a6 4435
bd5510a5 4436 /*
4437 * Check policy...
4438 */
4439
4440 if (printer)
4441 {
99baf768 4442 if (!cupsdCheckPolicy(printer->op_policy_ptr, con, NULL))
bd5510a5 4443 {
4444 LogMessage(L_ERROR, "get_jobs: not authorized!");
4445 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4446 return;
4447 }
4448 }
99baf768 4449 else if (!cupsdCheckPolicy(DefaultPolicyPtr, con, NULL))
bd5510a5 4450 {
4451 LogMessage(L_ERROR, "get_jobs: not authorized!");
4452 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4453 return;
4454 }
4455
1d2c70a6 4456 /*
7cc9aebd 4457 * See if the "which-jobs" attribute have been specified...
1d2c70a6 4458 */
4459
c0341b41 4460 if ((attr = ippFindAttribute(con->request, "which-jobs", IPP_TAG_KEYWORD)) != NULL &&
4461 strcmp(attr->values[0].string.text, "completed") == 0)
bd84e0d1 4462 completed = 1;
4463 else
4464 completed = 0;
1d2c70a6 4465
4466 /*
a3e17a89 4467 * See if they want to limit the number of jobs reported...
1d2c70a6 4468 */
4469
c0341b41 4470 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
1d2c70a6 4471 limit = attr->values[0].integer;
4472 else
a3e17a89 4473 limit = 1000000;
1d2c70a6 4474
4475 /*
4476 * See if we only want to see jobs for a specific user...
4477 */
4478
c0341b41 4479 if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL &&
1d2c70a6 4480 attr->values[0].boolean)
4481 {
17b95e13 4482 if (con->username[0])
def978d5 4483 strlcpy(username, con->username, sizeof(username));
17b95e13 4484 else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
def978d5 4485 strlcpy(username, attr->values[0].string.text, sizeof(username));
17b95e13 4486 else
4487 strcpy(username, "anonymous");
1d2c70a6 4488 }
4489 else
4490 username[0] = '\0';
4491
27d555e8 4492 requested = ippFindAttribute(con->request, "requested-attributes",
4493 IPP_TAG_KEYWORD);
4494
1d2c70a6 4495 /*
4496 * OK, build a list of jobs for this printer...
4497 */
4498
4499 for (count = 0, job = Jobs; count < limit && job != NULL; job = job->next)
4500 {
4501 /*
4502 * Filter out jobs that don't match...
4503 */
4504
b2e10895 4505 LogMessage(L_DEBUG2, "get_jobs: job->id = %d", job->id);
96df88bb 4506
28a96fe2 4507 if ((dest != NULL && strcmp(job->dest, dest) != 0) &&
4508 (job->printer == NULL || dest == NULL ||
4509 strcmp(job->printer->name, dest) != 0))
1d2c70a6 4510 continue;
28a96fe2 4511 if ((job->dtype & dmask) != dtype &&
4512 (job->printer == NULL || (job->printer->type & dmask) != dtype))
1d2c70a6 4513 continue;
4514 if (username[0] != '\0' && strcmp(username, job->username) != 0)
4515 continue;
4516
1049abbe 4517 if (completed && job->state->values[0].integer <= IPP_JOB_STOPPED)
bd84e0d1 4518 continue;
1049abbe 4519 if (!completed && job->state->values[0].integer > IPP_JOB_STOPPED)
bd84e0d1 4520 continue;
4521
1d2c70a6 4522 count ++;
4523
b2e10895 4524 LogMessage(L_DEBUG2, "get_jobs: count = %d", count);
96df88bb 4525
1d2c70a6 4526 /*
91606789 4527 * Send the requested attributes for each job...
1d2c70a6 4528 */
4529
a6988fb1 4530 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
e9f54388 4531 LocalPort, job->id);
1d2c70a6 4532
27b6a264 4533 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4534 "job-more-info", NULL, job_uri);
1d2c70a6 4535
27b6a264 4536 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
1049abbe 4537 "job-uri", NULL, job_uri);
1d2c70a6 4538
7ebf3a09 4539 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
0ccccc99 4540 "job-printer-up-time", time(NULL));
7ebf3a09 4541
1049abbe 4542 /*
4543 * Copy the job attributes to the response using the requested-attributes
4544 * attribute that may be provided by the client.
4545 */
1d2c70a6 4546
9ef400ff 4547 copy_attrs(con->response, job->attrs, requested, IPP_TAG_JOB, 0);
1d2c70a6 4548
7ebf3a09 4549 add_job_state_reasons(con, job);
4550
1d2c70a6 4551 ippAddSeparator(con->response);
4552 }
4553
27d555e8 4554 if (requested != NULL)
0a3ac972 4555 con->response->request.status.status_code = IPP_OK_SUBST;
1d2c70a6 4556 else
0a3ac972 4557 con->response->request.status.status_code = IPP_OK;
e31bfb6e 4558}
4559
4560
1d2c70a6 4561/*
4562 * 'get_job_attrs()' - Get job attributes.
4563 */
4564
e31bfb6e 4565static void
a4b3db80 4566get_job_attrs(client_t *con, /* I - Client connection */
4567 ipp_attribute_t *uri) /* I - Job URI */
e31bfb6e 4568{
a4b3db80 4569 ipp_attribute_t *attr, /* Current attribute */
4570 *requested; /* Requested attributes */
4571 int jobid; /* Job ID */
4572 job_t *job; /* Current job */
4573 char method[HTTP_MAX_URI], /* Method portion of URI */
4574 username[HTTP_MAX_URI], /* Username portion of URI */
4575 host[HTTP_MAX_URI], /* Host portion of URI */
4576 resource[HTTP_MAX_URI]; /* Resource portion of URI */
4577 int port; /* Port portion of URI */
4578 char job_uri[HTTP_MAX_URI]; /* Job URI... */
1d2c70a6 4579
4580
b2e10895 4581 LogMessage(L_DEBUG2, "get_job_attrs(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 4582 uri->values[0].string.text);
1d2c70a6 4583
4584 /*
4585 * See if we have a job URI or a printer URI...
4586 */
4587
4588 if (strcmp(uri->name, "printer-uri") == 0)
4589 {
4590 /*
4591 * Got a printer URI; see if we also have a job-id attribute...
4592 */
4593
c0341b41 4594 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
1d2c70a6 4595 {
5ea8888e 4596 LogMessage(L_ERROR, "get_job_attrs: got a printer-uri attribute but no job-id!");
1d2c70a6 4597 send_ipp_error(con, IPP_BAD_REQUEST);
4598 return;
4599 }
4600
4601 jobid = attr->values[0].integer;
4602 }
4603 else
4604 {
4605 /*
4606 * Got a job URI; parse it to get the job ID...
4607 */
4608
c0341b41 4609 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1d2c70a6 4610
4611 if (strncmp(resource, "/jobs/", 6) != 0)
4612 {
4613 /*
4614 * Not a valid URI!
4615 */
4616
5ea8888e 4617 LogMessage(L_ERROR, "get_job_attrs: bad job-uri attribute \'%s\'!\n",
bd84e0d1 4618 uri->values[0].string.text);
1d2c70a6 4619 send_ipp_error(con, IPP_BAD_REQUEST);
4620 return;
4621 }
4622
4623 jobid = atoi(resource + 6);
4624 }
4625
4626 /*
4627 * See if the job exists...
4628 */
4629
4630 if ((job = FindJob(jobid)) == NULL)
4631 {
4632 /*
4633 * Nope - return a "not found" error...
4634 */
4635
5ea8888e 4636 LogMessage(L_ERROR, "get_job_attrs: job #%d doesn't exist!", jobid);
1d2c70a6 4637 send_ipp_error(con, IPP_NOT_FOUND);
4638 return;
4639 }
4640
e9f54388 4641 /*
4642 * Check policy...
4643 */
4644
99baf768 4645 if (!cupsdCheckPolicy(DefaultPolicyPtr, con, NULL))
e9f54388 4646 {
4647 LogMessage(L_ERROR, "get_job_attrs: not authorized!");
4648 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4649 return;
4650 }
4651
782359ca 4652 /*
4653 * Put out the standard attributes...
4654 */
4655
a6988fb1 4656 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d",
e9f54388 4657 ServerName, LocalPort, job->id);
782359ca 4658
782359ca 4659 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
4660
782359ca 4661 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4662 "job-more-info", NULL, job_uri);
4663
782359ca 4664 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4665 "job-uri", NULL, job_uri);
4666
7ebf3a09 4667 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
0ccccc99 4668 "job-printer-up-time", time(NULL));
7ebf3a09 4669
1d2c70a6 4670 /*
d6de4648 4671 * Copy the job attributes to the response using the requested-attributes
1d2c70a6 4672 * attribute that may be provided by the client.
4673 */
4674
27d555e8 4675 requested = ippFindAttribute(con->request, "requested-attributes",
4676 IPP_TAG_KEYWORD);
4677
9ef400ff 4678 copy_attrs(con->response, job->attrs, requested, IPP_TAG_JOB, 0);
1d2c70a6 4679
7ebf3a09 4680 add_job_state_reasons(con, job);
4681
27d555e8 4682 if (requested != NULL)
0a3ac972 4683 con->response->request.status.status_code = IPP_OK_SUBST;
1049abbe 4684 else
0a3ac972 4685 con->response->request.status.status_code = IPP_OK;
e31bfb6e 4686}
4687
4688
a4b3db80 4689/*
4690 * 'get_notifications()' - Get events for a subscription.
4691 */
4692
4693static void
4694get_notifications(client_t *con, /* I - Client connection */
4695 int id) /* I - Subscription ID */
4696{
4697}
4698
4699
bd84e0d1 4700/*
4701 * 'get_ppds()' - Get the list of PPD files on the local system.
4702 */
4703
4704static void
4705get_ppds(client_t *con) /* I - Client connection */
4706{
08379093 4707 int i; /* Looping var */
4708 ipp_attribute_t *limit, /* Limit attribute */
4709 *make, /* ppd-make attribute */
4710 *requested; /* requested-attributes attribute */
4711 char command[1024], /* cups-deviced command */
4712 options[1024], /* Options to pass to command */
4713 attrs[1024], /* String for requested attributes */
4714 *aptr; /* Pointer into string */
4715 int alen; /* Length of attribute value */
4716
4717
b2e10895 4718 LogMessage(L_DEBUG2, "get_ppds(%p[%d])\n", con, con->http.fd);
dbb05cda 4719
e9f54388 4720 /*
4721 * Check policy...
4722 */
4723
99baf768 4724 if (!cupsdCheckPolicy(DefaultPolicyPtr, con, NULL))
e9f54388 4725 {
4726 LogMessage(L_ERROR, "get_ppds: not authorized!");
4727 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4728 return;
4729 }
4730
bd84e0d1 4731 /*
08379093 4732 * Run cups-driverd command with the given options...
bd84e0d1 4733 */
4734
08379093 4735 limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
9d0c9f28 4736 make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
08379093 4737 requested = ippFindAttribute(con->request, "requested-attributes",
4738 IPP_TAG_KEYWORD);
bd84e0d1 4739
08379093 4740 if (requested)
4741 {
4742 for (i = 0, aptr = attrs; i < requested->num_values; i ++)
4743 {
4744 /*
4745 * Check that we have enough room...
4746 */
4747
4748 alen = strlen(requested->values[i].string.text);
4749 if (alen > (sizeof(attrs) - (aptr - attrs) - 2))
4750 break;
4751
4752 /*
4753 * Put commas between values...
4754 */
4755
4756 if (i)
4757 *aptr++ = ',';
4758
4759 /*
4760 * Add the value to the end of the string...
4761 */
4762
4763 strcpy(aptr, requested->values[i].string.text);
4764 aptr += alen;
4765 }
4766
4767 /*
4768 * If we have more attribute names than will fit, default to "all"...
4769 */
4770
4771 if (i < requested->num_values)
4772 strcpy(attrs, "all");
4773 }
4774 else
4775 strcpy(attrs, "all");
4776
4777 snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
4778 snprintf(options, sizeof(options),
9d0c9f28 4779 "cups-driverd list+%d+%d+requested-attributes=%s%s%s",
08379093 4780 con->request->request.op.request_id,
4781 limit ? limit->values[0].integer : 0,
4782 attrs,
9d0c9f28 4783 make ? "%20ppd-make=" : "",
f2bc527f 4784 make ? make->values[0].string.text : "");
08379093 4785
b38d93df 4786 if (SendCommand(con, command, options, 0))
08379093 4787 {
4788 /*
4789 * Command started successfully, don't send an IPP response here...
4790 */
4791
4792 ippDelete(con->response);
4793 con->response = NULL;
4794 }
4795 else
4796 {
4797 /*
4798 * Command failed, return "internal error" so the user knows something
4799 * went wrong...
4800 */
4801
4802 send_ipp_error(con, IPP_INTERNAL_ERROR);
4803 }
bd84e0d1 4804}
4805
4806
1d2c70a6 4807/*
4808 * 'get_printer_attrs()' - Get printer attributes.
4809 */
4810
4811static void
4812get_printer_attrs(client_t *con, /* I - Client connection */
4813 ipp_attribute_t *uri) /* I - Printer URI */
4814{
1049abbe 4815 const char *dest; /* Destination */
1d2c70a6 4816 cups_ptype_t dtype; /* Destination type (printer or class) */
4817 char method[HTTP_MAX_URI],
4818 /* Method portion of URI */
4819 username[HTTP_MAX_URI],
4820 /* Username portion of URI */
4821 host[HTTP_MAX_URI],
4822 /* Host portion of URI */
4823 resource[HTTP_MAX_URI];
4824 /* Resource portion of URI */
4825 int port; /* Port portion of URI */
cbbfcc63 4826 printer_t *printer; /* Printer/class */
e5c4f5a1 4827 char printer_uri[HTTP_MAX_URI];
4828 /* Printer URI */
f3d580b9 4829 time_t curtime; /* Current time */
62bcac22 4830 int i; /* Looping var */
4831 ipp_attribute_t *requested, /* requested-attributes */
4832 *history; /* History collection */
1b730285 4833 int need_history; /* Need to send history collection? */
1d2c70a6 4834
4835
b2e10895 4836 LogMessage(L_DEBUG2, "get_printer_attrs(%p[%d], %s)\n", con, con->http.fd,
9b4f824a 4837 uri->values[0].string.text);
1d2c70a6 4838
4839 /*
4840 * Is the destination valid?
4841 */
4842
c0341b41 4843 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1d2c70a6 4844
bd5510a5 4845 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
1d2c70a6 4846 {
4847 /*
4848 * Bad URI...
4849 */
4850
5ea8888e 4851 LogMessage(L_ERROR, "get_printer_attrs: resource name \'%s\' no good!", resource);
1d2c70a6 4852 send_ipp_error(con, IPP_NOT_FOUND);
4853 return;
4854 }
4855
e9f54388 4856 /*
4857 * Check policy...
4858 */
4859
99baf768 4860 if (!cupsdCheckPolicy(printer->op_policy_ptr, con, NULL))
e9f54388 4861 {
4862 LogMessage(L_ERROR, "get_printer_attrs: not authorized!");
4863 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4864 return;
4865 }
4866
f3d580b9 4867 curtime = time(NULL);
4868
d6de4648 4869 /*
4870 * Copy the printer attributes to the response using requested-attributes
4871 * and document-format attributes that may be provided by the client.
4872 */
4873
e5c4f5a1 4874 if (!ippFindAttribute(printer->attrs, "printer-uri-supported",
4875 IPP_TAG_URI))
4876 {
4877 snprintf(printer_uri, sizeof(printer_uri), "ipp://%s:%d/printers/%s",
4878 con->servername, con->serverport, printer->name);
4879 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
4880 "printer-uri-supported", NULL, printer_uri);
4881 LogMessage(L_DEBUG2, "printer-uri-supported=\"%s\"", printer_uri);
4882 }
4883
96df88bb 4884 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
4885 printer->state);
4886
7ebf3a09 4887 add_printer_state_reasons(con, printer);
4888
b05809b0 4889 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
4890 "printer-state-message", NULL, printer->state_message);
96df88bb 4891
f3d580b9 4892 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
4893 printer->accepting);
25392f52 4894 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared",
4895 printer->shared);
f3d580b9 4896
4897 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
a3e17a89 4898 "printer-up-time", curtime);
62bcac22 4899 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
4900 "printer-state-time", printer->state_time);
f3d580b9 4901 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
4902 ippTimeToDate(curtime));
4903
0f9c1053 4904 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4905 "printer-error-policy", NULL, printer->op_policy);
4906 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
4907 "printer-op-policy", NULL, printer->op_policy);
4908
7ebf3a09 4909 add_queued_job_count(con, printer);
4910
62bcac22 4911 requested = ippFindAttribute(con->request, "requested-attributes",
4912 IPP_TAG_KEYWORD);
a3e17a89 4913
62bcac22 4914 copy_attrs(con->response, printer->attrs, requested, IPP_TAG_ZERO, 0);
4915 copy_attrs(con->response, CommonData, requested, IPP_TAG_ZERO, IPP_TAG_COPY);
3f2fa036 4916
1b730285 4917 need_history = 0;
4918
4919 if (MaxPrinterHistory > 0 && printer->num_history > 0 && requested)
4920 {
4921 for (i = 0; i < requested->num_values; i ++)
4922 if (!strcmp(requested->values[i].string.text, "all") ||
4923 !strcmp(requested->values[i].string.text, "printer-state-history"))
4924 {
4925 need_history = 1;
4926 break;
4927 }
4928 }
4929
4930 if (need_history)
62bcac22 4931 {
4932 history = ippAddCollections(con->response, IPP_TAG_PRINTER,
4933 "printer-state-history",
4934 printer->num_history, NULL);
4935
4936 for (i = 0; i < printer->num_history; i ++)
4937 copy_attrs(history->values[i].collection = ippNew(), printer->history[i],
4938 NULL, IPP_TAG_ZERO, 0);
4939 }
4940
4941 con->response->request.status.status_code = requested ? IPP_OK_SUBST : IPP_OK;
1d2c70a6 4942}
4943
4944
4945/*
27d555e8 4946 * 'get_printers()' - Get a list of printers or classes.
7ebf3a09 4947 */
4948
4949static void
4950get_printers(client_t *con, /* I - Client connection */
4951 int type) /* I - 0 or CUPS_PRINTER_CLASS */
4952{
a4b3db80 4953 int i; /* Looping var */
4954 ipp_attribute_t *requested, /* requested-attributes */
4955 *history, /* History collection */
4956 *attr; /* Current attribute */
4957 int need_history; /* Need to send history collection? */
4958 int limit; /* Maximum number of printers to return */
4959 int count; /* Number of printers that match */
4960 printer_t *printer; /* Current printer pointer */
4961 time_t curtime; /* Current time */
4962 int printer_type, /* printer-type attribute */
4963 printer_mask; /* printer-type-mask attribute */
4964 char *location; /* Location string */
4965 char name[IPP_MAX_NAME], /* Printer name */
4966 *nameptr; /* Pointer into name */
4967 printer_t *iclass; /* Implicit class */
bf9da908 4968 const char *username; /* Current user */
e5c4f5a1 4969 char printer_uri[HTTP_MAX_URI];
4970 /* Printer URI */
7ebf3a09 4971
4972
b2e10895 4973 LogMessage(L_DEBUG2, "get_printers(%p[%d], %x)\n", con, con->http.fd, type);
7ebf3a09 4974
e9f54388 4975 /*
4976 * Check policy...
4977 */
4978
99baf768 4979 if (!cupsdCheckPolicy(DefaultPolicyPtr, con, NULL))
e9f54388 4980 {
4981 LogMessage(L_ERROR, "get_printers: not authorized!");
4982 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4983 return;
4984 }
4985
7ebf3a09 4986 /*
a3e17a89 4987 * See if they want to limit the number of printers reported...
7ebf3a09 4988 */
4989
4990 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
4991 limit = attr->values[0].integer;
4992 else
a3e17a89 4993 limit = 10000000;
4994
4995 /*
4996 * Support filtering...
4997 */
4998
4999 if ((attr = ippFindAttribute(con->request, "printer-type", IPP_TAG_ENUM)) != NULL)
5000 printer_type = attr->values[0].integer;
5001 else
5002 printer_type = 0;
5003
5004 if ((attr = ippFindAttribute(con->request, "printer-type-mask", IPP_TAG_ENUM)) != NULL)
5005 printer_mask = attr->values[0].integer;
5006 else
5007 printer_mask = 0;
5008
471f1564 5009 if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
a3e17a89 5010 location = attr->values[0].string.text;
5011 else
5012 location = NULL;
7ebf3a09 5013
bf9da908 5014 if (con->username[0])
5015 username = con->username;
5016 else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
5017 username = attr->values[0].string.text;
5018 else
5019 username = NULL;
5020
27d555e8 5021 requested = ippFindAttribute(con->request, "requested-attributes",
5022 IPP_TAG_KEYWORD);
5023
1b730285 5024 need_history = 0;
5025
647ed206 5026 if (MaxPrinterHistory > 0 && requested)
1b730285 5027 {
5028 for (i = 0; i < requested->num_values; i ++)
5029 if (!strcmp(requested->values[i].string.text, "all") ||
5030 !strcmp(requested->values[i].string.text, "printer-state-history"))
5031 {
5032 need_history = 1;
5033 break;
5034 }
5035 }
5036
7ebf3a09 5037 /*
5038 * OK, build a list of printers for this printer...
5039 */
5040
5041 curtime = time(NULL);
5042
5043 for (count = 0, printer = Printers;
5044 count < limit && printer != NULL;
5045 printer = printer->next)
25392f52 5046 if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
a3e17a89 5047 (printer->type & printer_mask) == printer_type &&
471f1564 5048 (location == NULL || printer->location == NULL ||
25392f52 5049 !strcasecmp(printer->location, location)))
7ebf3a09 5050 {
753453e4 5051 /*
5052 * If HideImplicitMembers is enabled, see if this printer or class
5053 * is a member of an implicit class...
5054 */
5055
5056 if (ImplicitClasses && HideImplicitMembers &&
5057 (printer->type & CUPS_PRINTER_REMOTE))
5058 {
5059 /*
5060 * Make a copy of the printer name...
753453e4 5061 */
5062
36992080 5063 strlcpy(name, printer->name, sizeof(name));
753453e4 5064
5065 if ((nameptr = strchr(name, '@')) != NULL)
5066 {
5067 /*
5068 * Strip trailing @server...
5069 */
5070
5071 *nameptr = '\0';
5072
5073 /*
5074 * Find the core printer, if any...
5075 */
5076
5077 if ((iclass = FindPrinter(name)) != NULL &&
5078 (iclass->type & CUPS_PRINTER_IMPLICIT))
5079 continue;
5080 }
5081 }
5082
bf9da908 5083 /*
5084 * If a username is specified, see if it is allowed or denied
5085 * access...
5086 */
5087
5088 if (printer->num_users && username && !user_allowed(printer, username))
5089 continue;
5090
753453e4 5091 /*
5092 * Add the group separator as needed...
5093 */
5094
972c5ace 5095 if (count > 0)
5096 ippAddSeparator(con->response);
5097
5098 count ++;
5099
7ebf3a09 5100 /*
5101 * Send the following attributes for each printer:
5102 *
5103 * printer-state
5104 * printer-state-message
5105 * printer-is-accepting-jobs
25392f52 5106 * printer-is-shared
5107 * printer-up-time
5108 * printer-state-time
7ebf3a09 5109 * + all printer attributes
5110 */
5111
e5c4f5a1 5112 if (!ippFindAttribute(printer->attrs, "printer-uri-supported",
5113 IPP_TAG_URI))
5114 {
5115 snprintf(printer_uri, sizeof(printer_uri), "ipp://%s:%d/printers/%s",
5116 con->servername, con->serverport, printer->name);
5117 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
5118 "printer-uri-supported", NULL, printer_uri);
5119 LogMessage(L_DEBUG2, "printer-uri-supported=\"%s\"", printer_uri);
5120 }
5121
7ebf3a09 5122 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
5123 "printer-state", printer->state);
5124
5125 add_printer_state_reasons(con, printer);
5126
b05809b0 5127 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
5128 "printer-state-message", NULL, printer->state_message);
7ebf3a09 5129
5130 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
5131 printer->accepting);
25392f52 5132 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared",
5133 printer->shared);
7ebf3a09 5134
5135 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
a3e17a89 5136 "printer-up-time", curtime);
62bcac22 5137 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
5138 "printer-state-time", printer->state_time);
7ebf3a09 5139 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
5140 ippTimeToDate(curtime));
5141
0f9c1053 5142 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
5143 "printer-error-policy", NULL, printer->op_policy);
5144 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
5145 "printer-op-policy", NULL, printer->op_policy);
5146
7ebf3a09 5147 add_queued_job_count(con, printer);
5148
9ef400ff 5149 copy_attrs(con->response, printer->attrs, requested, IPP_TAG_ZERO, 0);
3f2fa036 5150
5151 copy_attrs(con->response, CommonData, requested, IPP_TAG_ZERO,
5152 IPP_TAG_COPY);
62bcac22 5153
647ed206 5154 if (need_history && printer->num_history > 0)
62bcac22 5155 {
5156 history = ippAddCollections(con->response, IPP_TAG_PRINTER,
5157 "printer-state-history",
5158 printer->num_history, NULL);
5159
5160 for (i = 0; i < printer->num_history; i ++)
5161 copy_attrs(history->values[i].collection = ippNew(),
5162 printer->history[i], NULL, IPP_TAG_ZERO, 0);
5163 }
7ebf3a09 5164 }
5165
62bcac22 5166 con->response->request.status.status_code = requested ? IPP_OK_SUBST : IPP_OK;
7ebf3a09 5167}
5168
5169
a4b3db80 5170/*
5171 * 'get_subscription_attrs()' - Get subscription attributes.
5172 */
5173
5174static void
5175get_subscription_attrs(client_t *con, /* I - Client connection */
5176 int sub_id) /* I - Subscription ID */
5177{
5178}
5179
5180
5181/*
5182 * 'get_subscriptions()' - Get subscriptions.
5183 */
5184
5185static void
5186get_subscriptions(client_t *con, /* I - Client connection */
5187 ipp_attribute_t *uri) /* I - Printer URI */
5188{
5189}
5190
5191
7ebf3a09 5192/*
5193 * 'hold_job()' - Hold a print job.
1d2c70a6 5194 */
5195
5196static void
a4b3db80 5197hold_job(client_t *con, /* I - Client connection */
5198 ipp_attribute_t *uri) /* I - Job or Printer URI */
e31bfb6e 5199{
a4b3db80 5200 ipp_attribute_t *attr, /* Current job-hold-until */
5201 *newattr; /* New job-hold-until */
5202 int jobid; /* Job ID */
5203 char method[HTTP_MAX_URI], /* Method portion of URI */
5204 username[HTTP_MAX_URI], /* Username portion of URI */
5205 host[HTTP_MAX_URI], /* Host portion of URI */
5206 resource[HTTP_MAX_URI]; /* Resource portion of URI */
5207 int port; /* Port portion of URI */
5208 job_t *job; /* Job information */
e31bfb6e 5209
5210
b2e10895 5211 LogMessage(L_DEBUG2, "hold_job(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 5212 uri->values[0].string.text);
e31bfb6e 5213
2aeb2b1d 5214 /*
5215 * Verify that the POST operation was done to a valid URI.
5216 */
5217
5218 if (strncmp(con->uri, "/classes/", 9) != 0 &&
bd84e0d1 5219 strncmp(con->uri, "/jobs/", 5) != 0 &&
2aeb2b1d 5220 strncmp(con->uri, "/printers/", 10) != 0)
5221 {
5ea8888e 5222 LogMessage(L_ERROR, "hold_job: hold request on bad resource \'%s\'!",
1124e9ec 5223 con->uri);
2aeb2b1d 5224 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5225 return;
5226 }
5227
e31bfb6e 5228 /*
bd84e0d1 5229 * See if we have a job URI or a printer URI...
e31bfb6e 5230 */
5231
bd84e0d1 5232 if (strcmp(uri->name, "printer-uri") == 0)
e31bfb6e 5233 {
4791a466 5234 /*
bd84e0d1 5235 * Got a printer URI; see if we also have a job-id attribute...
4791a466 5236 */
e31bfb6e 5237
bd84e0d1 5238 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4791a466 5239 {
5ea8888e 5240 LogMessage(L_ERROR, "hold_job: got a printer-uri attribute but no job-id!");
4791a466 5241 send_ipp_error(con, IPP_BAD_REQUEST);
5242 return;
5243 }
4791a466 5244
bd84e0d1 5245 jobid = attr->values[0].integer;
e31bfb6e 5246 }
bd84e0d1 5247 else
e31bfb6e 5248 {
782359ca 5249 /*
bd84e0d1 5250 * Got a job URI; parse it to get the job ID...
782359ca 5251 */
5252
bd84e0d1 5253 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5254
5255 if (strncmp(resource, "/jobs/", 6) != 0)
c6e90b24 5256 {
5257 /*
bd84e0d1 5258 * Not a valid URI!
5259 */
5260
5ea8888e 5261 LogMessage(L_ERROR, "hold_job: bad job-uri attribute \'%s\'!",
bd84e0d1 5262 uri->values[0].string.text);
5263 send_ipp_error(con, IPP_BAD_REQUEST);
5264 return;
5265 }
5266
5267 jobid = atoi(resource + 6);
5268 }
5269
5270 /*
5271 * See if the job exists...
5272 */
5273
5274 if ((job = FindJob(jobid)) == NULL)
5275 {
5276 /*
5277 * Nope - return a "not found" error...
5278 */
5279
5ea8888e 5280 LogMessage(L_ERROR, "hold_job: job #%d doesn't exist!", jobid);
bd84e0d1 5281 send_ipp_error(con, IPP_NOT_FOUND);
5282 return;
5283 }
5284
5285 /*
5286 * See if the job is owned by the requesting user...
5287 */
5288
bd5510a5 5289 if (!validate_user(job, con, job->username, username, sizeof(username)))
bd84e0d1 5290 {
ed3e11d8 5291 LogMessage(L_ERROR, "hold_job: \"%s\" not authorized to hold job id %d owned by \"%s\"!",
5292 username, jobid, job->username);
5293 send_ipp_error(con, IPP_FORBIDDEN);
5294 return;
bd84e0d1 5295 }
5296
5297 /*
5298 * Hold the job and return...
5299 */
5300
5301 HoldJob(jobid);
5302
5856eaaf 5303 if ((newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
5304 newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
5305
e583bc2d 5306 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
5307 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
5308
5856eaaf 5309 if (attr != NULL)
e583bc2d 5310 {
5856eaaf 5311 /*
5312 * Free the old hold value and copy the new one over...
5313 */
5314
5315 free(attr->values[0].string.text);
5316
5317 if (newattr != NULL)
5318 {
5319 attr->value_tag = newattr->value_tag;
5320 attr->values[0].string.text = strdup(newattr->values[0].string.text);
5321 }
5322 else
5323 {
5324 attr->value_tag = IPP_TAG_KEYWORD;
5325 attr->values[0].string.text = strdup("indefinite");
5326 }
5327
a6988fb1 5328 /*
5329 * Hold job until specified time...
5330 */
e583bc2d 5331
a6988fb1 5332 SetJobHoldUntil(job->id, attr->values[0].string.text);
5333 }
e583bc2d 5334
c8f336b5 5335 LogMessage(L_INFO, "Job %d was held by \'%s\'.", jobid, username);
bd84e0d1 5336
0a3ac972 5337 con->response->request.status.status_code = IPP_OK;
bd84e0d1 5338}
5339
5340
a3e17a89 5341/*
753453e4 5342 * 'move_job()' - Move a job to a new destination.
a3e17a89 5343 */
5344
5345static void
5346move_job(client_t *con, /* I - Client connection */
5347 ipp_attribute_t *uri) /* I - Job URI */
5348{
a4b3db80 5349 ipp_attribute_t *attr; /* Current attribute */
5350 int jobid; /* Job ID */
5351 job_t *job; /* Current job */
5352 const char *dest; /* Destination */
5353 cups_ptype_t dtype; /* Destination type (printer or class) */
5354 char method[HTTP_MAX_URI], /* Method portion of URI */
5355 username[HTTP_MAX_URI], /* Username portion of URI */
5356 host[HTTP_MAX_URI], /* Host portion of URI */
5357 resource[HTTP_MAX_URI]; /* Resource portion of URI */
5358 int port; /* Port portion of URI */
5359 printer_t *printer; /* Printer */
a3e17a89 5360
5361
b2e10895 5362 LogMessage(L_DEBUG2, "move_job(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 5363 uri->values[0].string.text);
a3e17a89 5364
5365 /*
5366 * See if we have a job URI or a printer URI...
5367 */
5368
5369 if (strcmp(uri->name, "printer-uri") == 0)
5370 {
5371 /*
5372 * Got a printer URI; see if we also have a job-id attribute...
5373 */
5374
5375 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
5376 {
5377 LogMessage(L_ERROR, "move_job: got a printer-uri attribute but no job-id!");
5378 send_ipp_error(con, IPP_BAD_REQUEST);
5379 return;
5380 }
5381
5382 jobid = attr->values[0].integer;
5383 }
5384 else
5385 {
5386 /*
5387 * Got a job URI; parse it to get the job ID...
5388 */
5389
5390 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5391
5392 if (strncmp(resource, "/jobs/", 6) != 0)
5393 {
5394 /*
5395 * Not a valid URI!
5396 */
5397
5398 LogMessage(L_ERROR, "move_job: bad job-uri attribute \'%s\'!\n",
5399 uri->values[0].string.text);
5400 send_ipp_error(con, IPP_BAD_REQUEST);
5401 return;
5402 }
5403
5404 jobid = atoi(resource + 6);
5405 }
5406
5407 /*
5408 * See if the job exists...
5409 */
5410
5411 if ((job = FindJob(jobid)) == NULL)
5412 {
5413 /*
5414 * Nope - return a "not found" error...
5415 */
5416
5417 LogMessage(L_ERROR, "move_job: job #%d doesn't exist!", jobid);
5418 send_ipp_error(con, IPP_NOT_FOUND);
5419 return;
5420 }
5421
5422 /*
5423 * See if the job has been completed...
5424 */
5425
5426 if (job->state->values[0].integer > IPP_JOB_STOPPED)
5427 {
5428 /*
5429 * Return a "not-possible" error...
5430 */
5431
5432 LogMessage(L_ERROR, "move_job: job #%d is finished and cannot be altered!", jobid);
5433 send_ipp_error(con, IPP_NOT_POSSIBLE);
5434 return;
5435 }
5436
5437 /*
5438 * See if the job is owned by the requesting user...
5439 */
5440
bd5510a5 5441 if (!validate_user(job, con, job->username, username, sizeof(username)))
a3e17a89 5442 {
ed3e11d8 5443 LogMessage(L_ERROR, "move_job: \"%s\" not authorized to move job id %d owned by \"%s\"!",
5444 username, jobid, job->username);
5445 send_ipp_error(con, IPP_FORBIDDEN);
5446 return;
a3e17a89 5447 }
5448
5449 if ((attr = ippFindAttribute(con->request, "job-printer-uri", IPP_TAG_URI)) == NULL)
5450 {
5451 /*
5452 * Need job-printer-uri...
5453 */
5454
5455 LogMessage(L_ERROR, "move_job: job-printer-uri attribute missing!");
5456 send_ipp_error(con, IPP_BAD_REQUEST);
5457 return;
5458 }
5459
5460 /*
e9f54388 5461 * Get the new printer or class...
a3e17a89 5462 */
5463
5464 httpSeparate(attr->values[0].string.text, method, username, host, &port,
5465 resource);
bd5510a5 5466 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
a3e17a89 5467 {
5468 /*
5469 * Bad URI...
5470 */
5471
5472 LogMessage(L_ERROR, "move_job: resource name \'%s\' no good!", resource);
5473 send_ipp_error(con, IPP_NOT_FOUND);
5474 return;
5475 }
5476
e9f54388 5477 /*
5478 * Check policy...
5479 */
5480
99baf768 5481 if (!cupsdCheckPolicy(printer->op_policy_ptr, con, NULL))
e9f54388 5482 {
5483 LogMessage(L_ERROR, "move_job: not authorized!");
5484 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5485 return;
5486 }
5487
5488 /*
5489 * Move the job to a different printer or class...
5490 */
5491
a3e17a89 5492 MoveJob(jobid, dest);
5493
5494 /*
5495 * Start jobs if possible...
5496 */
5497
5498 CheckJobs();
5499
5500 /*
5501 * Return with "everything is OK" status...
5502 */
5503
0a3ac972 5504 con->response->request.status.status_code = IPP_OK;
a3e17a89 5505}
5506
5507
0e4f2c25 5508/*
5509 * 'ppd_add_default()' - Add a PPD default choice.
5510 */
5511
5512static int /* O - Number of defaults */
a4b3db80 5513ppd_add_default(
5514 const char *option, /* I - Option name */
5515 const char *choice, /* I - Choice name */
5516 int num_defaults, /* I - Number of defaults */
5517 ppd_default_t **defaults) /* IO - Defaults */
0e4f2c25 5518{
5519 int i; /* Looping var */
5520 ppd_default_t *temp; /* Temporary defaults array */
5521
5522
5523 /*
5524 * First check if the option already has a default value; the PPD spec
5525 * says that the first one is used...
5526 */
5527
5528 for (i = 0, temp = *defaults; i < num_defaults; i ++)
5529 if (!strcmp(option, temp[i].option))
5530 return (num_defaults);
5531
5532 /*
5533 * Now add the option...
5534 */
5535
5536 if (num_defaults == 0)
5537 temp = malloc(sizeof(ppd_default_t));
5538 else
5539 temp = realloc(*defaults, (num_defaults + 1) * sizeof(ppd_default_t));
5540
5541 if (!temp)
5542 {
5543 LogMessage(L_ERROR, "ppd_add_default: Unable to add default value for \"%s\" - %s",
5544 option, strerror(errno));
5545 return (num_defaults);
5546 }
5547
5548 *defaults = temp;
5549 temp += num_defaults;
5550
5551 strlcpy(temp->option, option, sizeof(temp->option));
5552 strlcpy(temp->choice, choice, sizeof(temp->choice));
5553
5554 return (num_defaults + 1);
5555}
5556
5557
5558/*
5559 * 'ppd_parse_line()' - Parse a PPD default line.
5560 */
5561
5562static int /* O - 0 on success, -1 on failure */
5563ppd_parse_line(const char *line, /* I - Line */
5564 char *option, /* O - Option name */
5565 int olen, /* I - Size of option name */
5566 char *choice, /* O - Choice name */
5567 int clen) /* I - Size of choice name */
5568{
5569 /*
5570 * Verify this is a default option line...
5571 */
5572
5573 if (strncmp(line, "*Default", 8))
5574 return (-1);
5575
5576 /*
5577 * Read the option name...
5578 */
5579
da275f55 5580 for (line += 8, olen --; isalnum(*line & 255); line ++)
0e4f2c25 5581 if (olen > 0)
5582 {
5583 *option++ = *line;
5584 olen --;
5585 }
5586
5587 *option = '\0';
5588
5589 /*
5590 * Skip everything else up to the colon (:)...
5591 */
5592
5593 while (*line && *line != ':')
5594 line ++;
5595
5596 if (!*line)
5597 return (-1);
5598
5599 line ++;
5600
5601 /*
5602 * Now grab the option choice, skipping leading whitespace...
5603 */
5604
da275f55 5605 while (isspace(*line & 255))
0e4f2c25 5606 line ++;
5607
da275f55 5608 for (clen --; isalnum(*line & 255); line ++)
0e4f2c25 5609 if (clen > 0)
5610 {
5611 *choice++ = *line;
5612 clen --;
5613 }
5614
5615 *choice = '\0';
5616
5617 /*
5618 * Return with no errors...
5619 */
5620
5621 return (0);
5622}
5623
5624
bd84e0d1 5625/*
5626 * 'print_job()' - Print a file to a printer or class.
5627 */
5628
5629static void
5630print_job(client_t *con, /* I - Client connection */
5631 ipp_attribute_t *uri) /* I - Printer URI */
5632{
a4b3db80 5633 ipp_attribute_t *attr; /* Current attribute */
5634 ipp_attribute_t *format; /* Document-format attribute */
5635 const char *dest; /* Destination */
5636 cups_ptype_t dtype; /* Destination type (printer or class) */
5637 int priority; /* Job priority */
5638 char *title; /* Job name/title */
5639 job_t *job; /* Current job */
5640 int jobid; /* Job ID number */
5641 char job_uri[HTTP_MAX_URI], /* Job URI */
5642 method[HTTP_MAX_URI], /* Method portion of URI */
5643 username[HTTP_MAX_URI], /* Username portion of URI */
5644 host[HTTP_MAX_URI], /* Host portion of URI */
5645 resource[HTTP_MAX_URI], /* Resource portion of URI */
5646 filename[1024]; /* Job filename */
5647 int port; /* Port portion of URI */
5648 mime_type_t *filetype; /* Type of file */
5649 char super[MIME_MAX_SUPER], /* Supertype of file */
5650 type[MIME_MAX_TYPE], /* Subtype of file */
5651 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
bd84e0d1 5652 /* Textual name of mime type */
a4b3db80 5653 printer_t *printer; /* Printer data */
5654 struct stat fileinfo; /* File information */
5655 int kbytes; /* Size of file */
5656 int i; /* Looping var */
5657 int lowerpagerange; /* Page range bound */
5658 int compression; /* Document compression */
bd84e0d1 5659
5660
b2e10895 5661 LogMessage(L_DEBUG2, "print_job(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 5662 uri->values[0].string.text);
bd84e0d1 5663
5664 /*
5665 * Verify that the POST operation was done to a valid URI.
5666 */
5667
5668 if (strncmp(con->uri, "/classes/", 9) != 0 &&
5669 strncmp(con->uri, "/printers/", 10) != 0)
5670 {
5ea8888e 5671 LogMessage(L_ERROR, "print_job: cancel request on bad resource \'%s\'!",
bd84e0d1 5672 con->uri);
5673 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5674 return;
5675 }
5676
879062a9 5677 /*
bce0c833 5678 * Validate job template attributes; for now just copies and page-ranges...
879062a9 5679 */
5680
5681 if ((attr = ippFindAttribute(con->request, "copies", IPP_TAG_INTEGER)) != NULL)
5682 {
da956953 5683 if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
879062a9 5684 {
6248387b 5685 LogMessage(L_ERROR, "print_job: Bad copies value %d.",
879062a9 5686 attr->values[0].integer);
6248387b 5687 send_ipp_error(con, IPP_ATTRIBUTES);
5688 ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
5689 "copies", attr->values[0].integer);
879062a9 5690 return;
5691 }
5692 }
5693
bce0c833 5694 if ((attr = ippFindAttribute(con->request, "page-ranges", IPP_TAG_RANGE)) != NULL)
5695 {
5696 for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
5697 {
5698 if (attr->values[i].range.lower < lowerpagerange ||
5699 attr->values[i].range.lower > attr->values[i].range.upper)
5700 {
5701 LogMessage(L_ERROR, "print_job: bad page-ranges values %d-%d.",
5702 attr->values[i].range.lower, attr->values[i].range.upper);
5703 send_ipp_error(con, IPP_BAD_REQUEST);
5704 return;
5705 }
5706
5707 lowerpagerange = attr->values[i].range.upper + 1;
5708 }
5709 }
5710
bd84e0d1 5711 /*
5712 * OK, see if the client is sending the document compressed - CUPS
d59a189c 5713 * only supports "none" and "gzip".
bd84e0d1 5714 */
5715
d59a189c 5716 compression = CUPS_FILE_NONE;
5717
5718 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL)
bd84e0d1 5719 {
d59a189c 5720 if (strcmp(attr->values[0].string.text, "none")
5721#ifdef HAVE_LIBZ
5722 && strcmp(attr->values[0].string.text, "gzip")
5723#endif /* HAVE_LIBZ */
5724 )
5725 {
5726 LogMessage(L_ERROR, "print_job: Unsupported compression \"%s\"!",
5727 attr->values[0].string.text);
5728 send_ipp_error(con, IPP_ATTRIBUTES);
5729 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
5730 "compression", NULL, attr->values[0].string.text);
5731 return;
5732 }
5733
5734#ifdef HAVE_LIBZ
5735 if (!strcmp(attr->values[0].string.text, "gzip"))
5736 compression = CUPS_FILE_GZIP;
5737#endif /* HAVE_LIBZ */
bd84e0d1 5738 }
5739
5740 /*
5741 * Do we have a file to print?
5742 */
5743
fc757c63 5744 if (!con->filename)
bd84e0d1 5745 {
5ea8888e 5746 LogMessage(L_ERROR, "print_job: No file!?!");
bd84e0d1 5747 send_ipp_error(con, IPP_BAD_REQUEST);
5748 return;
5749 }
5750
5751 /*
5752 * Is it a format we support?
5753 */
5754
5755 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
5756 {
5757 /*
5758 * Grab format from client...
5759 */
5760
5761 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
5762 {
5ea8888e 5763 LogMessage(L_ERROR, "print_job: could not scan type \'%s\'!",
bd84e0d1 5764 format->values[0].string.text);
5765 send_ipp_error(con, IPP_BAD_REQUEST);
5766 return;
5767 }
5768 }
5769 else
5770 {
5771 /*
5772 * No document format attribute? Auto-type it!
5773 */
5774
5775 strcpy(super, "application");
5776 strcpy(type, "octet-stream");
5777 }
5778
48e211f3 5779 if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
bd84e0d1 5780 {
5781 /*
5782 * Auto-type the file...
5783 */
5784
5ea8888e 5785 LogMessage(L_DEBUG, "print_job: auto-typing file...");
bd84e0d1 5786
d59a189c 5787 filetype = mimeFileType(MimeDatabase, con->filename, &compression);
bd84e0d1 5788
5789 if (filetype != NULL)
5790 {
5791 /*
5792 * Replace the document-format attribute value with the auto-typed one.
c6e90b24 5793 */
782359ca 5794
a6988fb1 5795 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
5796 filetype->type);
4791a466 5797
5798 if (format != NULL)
5799 {
5800 free(format->values[0].string.text);
5801 format->values[0].string.text = strdup(mimetype);
5802 }
5803 else
5804 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
5805 "document-format", NULL, mimetype);
c6e90b24 5806 }
526462ee 5807 else
5808 filetype = mimeType(MimeDatabase, super, type);
e31bfb6e 5809 }
5810 else
5811 filetype = mimeType(MimeDatabase, super, type);
5812
5813 if (filetype == NULL)
5814 {
e93faa10 5815 LogMessage(L_ERROR, "print_job: Unsupported format \'%s/%s\'!",
5fa65e01 5816 super, type);
deb855a2 5817 LogMessage(L_INFO, "Hint: Do you have the raw file printing rules enabled?");
1d2c70a6 5818 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
e93faa10 5819
5820 if (format)
5821 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
5822 "document-format", NULL, format->values[0].string.text);
5823
e31bfb6e 5824 return;
5825 }
5826
5ea8888e 5827 LogMessage(L_DEBUG, "print_job: request file type is %s/%s.",
bd84e0d1 5828 filetype->super, filetype->type);
e31bfb6e 5829
56e9c951 5830 /*
5831 * Read any embedded job ticket info from PS files...
5832 */
5833
48e211f3 5834 if (!strcasecmp(filetype->super, "application") &&
5835 !strcasecmp(filetype->type, "postscript"))
56e9c951 5836 read_ps_job_ticket(con);
5837
e31bfb6e 5838 /*
5839 * Is the destination valid?
5840 */
5841
c0341b41 5842 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
e31bfb6e 5843
bd5510a5 5844 if ((dest = ValidateDest(host, resource, &dtype, &printer)) == NULL)
e31bfb6e 5845 {
5846 /*
5847 * Bad URI...
5848 */
5849
5ea8888e 5850 LogMessage(L_ERROR, "print_job: resource name \'%s\' no good!", resource);
1d2c70a6 5851 send_ipp_error(con, IPP_NOT_FOUND);
e31bfb6e 5852 return;
5853 }
5854
25392f52 5855 /*
5856 * Check remote printing to non-shared printer...
5857 */
5858
5859 if (!printer->shared &&
5860 strcasecmp(con->http.hostname, "localhost") &&
5861 strcasecmp(con->http.hostname, ServerName))
5862 {
5863 LogMessage(L_ERROR, "print_job: printer not shared!");
5864 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5865 return;
5866 }
5867
e9f54388 5868 /*
5869 * Check policy...
5870 */
5871
48e211f3 5872 if (!cupsdCheckPolicy(printer->op_policy_ptr, con, NULL) ||
5873 ((printer->type & CUPS_PRINTER_AUTHENTICATED) && !con->username[0]))
e9f54388 5874 {
5875 LogMessage(L_ERROR, "print_job: not authorized!");
5876 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5877 return;
5878 }
5879
f3d580b9 5880 /*
5881 * See if the printer is accepting jobs...
5882 */
5883
cbbfcc63 5884 if (!printer->accepting)
5885 {
5ea8888e 5886 LogMessage(L_INFO, "print_job: destination \'%s\' is not accepting jobs.",
1917e0e9 5887 dest);
cbbfcc63 5888 send_ipp_error(con, IPP_NOT_ACCEPTING);
5889 return;
f3d580b9 5890 }
5891
d7845573 5892 /*
5893 * Make sure we aren't over our limit...
5894 */
5895
5896 if (NumJobs >= MaxJobs && MaxJobs)
5897 CleanJobs();
5898
5899 if (NumJobs >= MaxJobs && MaxJobs)
5900 {
a931c98a 5901 LogMessage(L_INFO, "print_job: too many jobs - %d jobs, max jobs is %d.",
5902 NumJobs, MaxJobs);
d7845573 5903 send_ipp_error(con, IPP_NOT_POSSIBLE);
5904 return;
5905 }
5906
b521f3fc 5907 if (!check_quotas(con, printer))
5908 {
5909 send_ipp_error(con, IPP_NOT_POSSIBLE);
5910 return;
5911 }
5912
e31bfb6e 5913 /*
5914 * Create the job and set things up...
5915 */
5916
c0341b41 5917 if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
e31bfb6e 5918 priority = attr->values[0].integer;
5919 else
1049abbe 5920 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
5921 priority = 50);
e31bfb6e 5922
27b6a264 5923 if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
5924 title = attr->values[0].string.text;
5925 else
1049abbe 5926 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
5927 title = "Untitled");
27b6a264 5928
cc0561c6 5929 if ((job = AddJob(priority, printer->name)) == NULL)
e31bfb6e 5930 {
5ea8888e 5931 LogMessage(L_ERROR, "print_job: unable to add job for destination \'%s\'!",
1917e0e9 5932 dest);
e31bfb6e 5933 send_ipp_error(con, IPP_INTERNAL_ERROR);
5934 return;
5935 }
5936
1049abbe 5937 job->dtype = dtype;
5938 job->attrs = con->request;
5939 con->request = NULL;
e31bfb6e 5940
a3e17a89 5941 /*
5942 * Copy the rest of the job info...
5943 */
5944
17b95e13 5945 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
5946
5947 if (con->username[0])
48e211f3 5948 {
36992080 5949 SetString(&job->username, con->username);
48e211f3 5950 save_auth_info(con, job->id);
5951 }
375f8c01 5952 else if (attr != NULL)
e31bfb6e 5953 {
5ea8888e 5954 LogMessage(L_DEBUG, "print_job: requesting-user-name = \'%s\'",
bd84e0d1 5955 attr->values[0].string.text);
1d2c70a6 5956
36992080 5957 SetString(&job->username, attr->values[0].string.text);
e31bfb6e 5958 }
17b95e13 5959 else
36992080 5960 SetString(&job->username, "anonymous");
1049abbe 5961
5962 if (attr == NULL)
5963 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
5964 NULL, job->username);
5965 else
5966 {
4979ce3d 5967 attr->group_tag = IPP_TAG_JOB;
36992080 5968 SetString(&attr->name, "job-originating-user-name");
1049abbe 5969 }
e31bfb6e 5970
1049abbe 5971 /*
5972 * Add remaining job attributes...
5973 */
5974
238bae16 5975 if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
5976 IPP_TAG_ZERO)) != NULL)
5977 {
5978 /*
5979 * Request contains a job-originating-host-name attribute; validate it...
5980 */
5981
5982 if (attr->value_tag != IPP_TAG_NAME ||
5983 attr->num_values != 1 ||
5984 strcmp(con->http.hostname, "localhost") != 0)
5985 {
5986 /*
5987 * Can't override the value if we aren't connected via localhost.
5988 * Also, we can only have 1 value and it must be a name value.
5989 */
5990
238bae16 5991 switch (attr->value_tag)
5992 {
5993 case IPP_TAG_STRING :
5994 case IPP_TAG_TEXTLANG :
5995 case IPP_TAG_NAMELANG :
5996 case IPP_TAG_TEXT :
5997 case IPP_TAG_NAME :
5998 case IPP_TAG_KEYWORD :
5999 case IPP_TAG_URI :
6000 case IPP_TAG_URISCHEME :
6001 case IPP_TAG_CHARSET :
6002 case IPP_TAG_LANGUAGE :
6003 case IPP_TAG_MIMETYPE :
6004 /*
6005 * Free old strings...
6006 */
6007
6008 for (i = 0; i < attr->num_values; i ++)
6009 {
6010 free(attr->values[i].string.text);
6011 attr->values[i].string.text = NULL;
6012 if (attr->values[i].string.charset)
6013 {
6014 free(attr->values[i].string.charset);
6015 attr->values[i].string.charset = NULL;
6016 }
6017 }
6018
6019 default :
6020 break;
6021 }
6022
6023 /*
6024 * Use the default connection hostname instead...
6025 */
6026
6027 attr->value_tag = IPP_TAG_NAME;
6028 attr->num_values = 1;
6029 attr->values[0].string.text = strdup(con->http.hostname);
6030 }
bb9df75d 6031
6032 attr->group_tag = IPP_TAG_JOB;
238bae16 6033 }
6034 else
6035 {
6036 /*
6037 * No job-originating-host-name attribute, so use the hostname from
6038 * the connection...
6039 */
6040
6041 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
6042 "job-originating-host-name", NULL, con->http.hostname);
6043 }
6044
1049abbe 6045 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
6046 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
cd4e71e5 6047 "job-state", IPP_JOB_PENDING);
aa0cb334 6048 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
6049 "job-media-sheets-completed", 0);
1049abbe 6050 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
bd5510a5 6051 printer->uri);
100cd8c8 6052 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
6053 title);
1049abbe 6054
91606789 6055 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) == NULL)
6056 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
6057 "job-k-octets", 0);
6058
b521f3fc 6059 if (stat(con->filename, &fileinfo))
6060 kbytes = 0;
6061 else
6062 kbytes = (fileinfo.st_size + 1023) / 1024;
6063
6064 UpdateQuota(printer, job->username, 0, kbytes);
6065 attr->values[0].integer += kbytes;
91606789 6066
7ebf3a09 6067 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
0ccccc99 6068 time(NULL));
7ebf3a09 6069 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
6070 "time-at-processing", 0);
6071 attr->value_tag = IPP_TAG_NOVALUE;
6072 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
6073 "time-at-completed", 0);
6074 attr->value_tag = IPP_TAG_NOVALUE;
6075
e583bc2d 6076 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
6077 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4104c7ec 6078 if (attr == NULL)
6079 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
6080 "job-hold-until", NULL, "no-hold");
e583bc2d 6081
753453e4 6082 if (attr != NULL && strcmp(attr->values[0].string.text, "no-hold") != 0 &&
6083 !(printer->type & CUPS_PRINTER_REMOTE))
e583bc2d 6084 {
6085 /*
6086 * Hold job until specified time...
6087 */
6088
6089 job->state->values[0].integer = IPP_JOB_HELD;
6090 SetJobHoldUntil(job->id, attr->values[0].string.text);
6091 }
6092
1ed20df6 6093 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
6094 Classification)
e903a8f6 6095 {
6096 /*
6097 * Add job sheets options...
6098 */
6099
ebde520c 6100 if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) == NULL)
e903a8f6 6101 {
753453e4 6102 LogMessage(L_DEBUG, "Adding default job-sheets values \"%s,%s\"...",
6103 printer->job_sheets[0], printer->job_sheets[1]);
6104
e903a8f6 6105 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
6106 2, NULL, NULL);
6107 attr->values[0].string.text = strdup(printer->job_sheets[0]);
6108 attr->values[1].string.text = strdup(printer->job_sheets[1]);
6109 }
6110
b5cb0608 6111 job->job_sheets = attr;
6112
d11458ff 6113 /*
6114 * Enforce classification level if set...
6115 */
6116
36992080 6117 if (Classification)
d11458ff 6118 {
9041fee2 6119 LogMessage(L_INFO, "Classification=\"%s\", ClassifyOverride=%d",
6120 Classification ? Classification : "(null)", ClassifyOverride);
6121
753453e4 6122 if (ClassifyOverride)
6123 {
6124 if (strcmp(attr->values[0].string.text, "none") == 0 &&
6125 (attr->num_values == 1 ||
6126 strcmp(attr->values[1].string.text, "none") == 0))
6127 {
6128 /*
6129 * Force the leading banner to have the classification on it...
6130 */
6131
36992080 6132 SetString(&attr->values[0].string.text, Classification);
f9f084a1 6133
6134 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
6135 "job-sheets=\"%s,none\", "
6136 "job-originating-user-name=\"%s\"",
6137 job->id, Classification,
6138 job->username);
753453e4 6139 }
6140 else if (attr->num_values == 2 &&
6141 strcmp(attr->values[0].string.text, attr->values[1].string.text) != 0 &&
6142 strcmp(attr->values[0].string.text, "none") != 0 &&
6143 strcmp(attr->values[1].string.text, "none") != 0)
6144 {
6145 /*
6146 * Can't put two different security markings on the same document!
6147 */
6148
36992080 6149 SetString(&attr->values[1].string.text, attr->values[0].string.text);
f9f084a1 6150
6151 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
6152 "job-sheets=\"%s,%s\", "
6153 "job-originating-user-name=\"%s\"",
6154 job->id, attr->values[0].string.text,
6155 attr->values[1].string.text,
6156 job->username);
753453e4 6157 }
f9f084a1 6158 else if (strcmp(attr->values[0].string.text, Classification) &&
6159 strcmp(attr->values[0].string.text, "none") &&
6160 (attr->num_values == 1 ||
6161 (strcmp(attr->values[1].string.text, Classification) &&
6162 strcmp(attr->values[1].string.text, "none"))))
6163 {
6164 if (attr->num_values == 1)
6165 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION OVERRIDDEN "
6166 "job-sheets=\"%s\", "
6167 "job-originating-user-name=\"%s\"",
6168 job->id, attr->values[0].string.text,
6169 job->username);
6170 else
6171 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION OVERRIDDEN "
6172 "job-sheets=\"%s,%s\", "
6173 "job-originating-user-name=\"%s\"",
6174 job->id, attr->values[0].string.text,
6175 attr->values[1].string.text,
6176 job->username);
6177 }
753453e4 6178 }
6179 else if (strcmp(attr->values[0].string.text, Classification) != 0 &&
6180 (attr->num_values == 1 ||
6181 strcmp(attr->values[1].string.text, Classification) != 0))
d11458ff 6182 {
6183 /*
f9f084a1 6184 * Force the banner to have the classification on it...
d11458ff 6185 */
6186
9041fee2 6187 if (attr->num_values > 1 &&
6188 !strcmp(attr->values[0].string.text, attr->values[1].string.text))
6189 {
6190 SetString(&(attr->values[0].string.text), Classification);
6191 SetString(&(attr->values[1].string.text), Classification);
6192 }
6193 else
6194 {
6195 if (attr->num_values == 1 ||
6196 strcmp(attr->values[0].string.text, "none"))
6197 SetString(&(attr->values[0].string.text), Classification);
6198
6199 if (attr->num_values > 1 &&
6200 strcmp(attr->values[1].string.text, "none"))
6201 SetString(&(attr->values[1].string.text), Classification);
6202 }
f9f084a1 6203
6204 if (attr->num_values > 1)
6205 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
6206 "job-sheets=\"%s,%s\", "
6207 "job-originating-user-name=\"%s\"",
6208 job->id, attr->values[0].string.text,
6209 attr->values[1].string.text,
6210 job->username);
6211 else
6212 LogMessage(L_NOTICE, "[Job %d] CLASSIFICATION FORCED "
6213 "job-sheets=\"%s\", "
6214 "job-originating-user-name=\"%s\"",
6215 job->id, Classification,
6216 job->username);
d11458ff 6217 }
6218 }
6219
e903a8f6 6220 /*
6221 * Add the starting sheet...
6222 */
6223
1ed20df6 6224 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
01c9aafc 6225 {
1ed20df6 6226 LogMessage(L_INFO, "Adding start banner page \"%s\" to job %d.",
6227 attr->values[0].string.text, job->id);
6228
01c9aafc 6229 kbytes = copy_banner(con, job, attr->values[0].string.text);
b521f3fc 6230
01c9aafc 6231 UpdateQuota(printer, job->username, 0, kbytes);
6232 }
e903a8f6 6233 }
753453e4 6234 else if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
6235 job->sheets = attr;
e903a8f6 6236
6237 /*
6238 * Add the job file...
6239 */
6240
d59a189c 6241 if (add_file(con, job, filetype, compression))
e903a8f6 6242 return;
6243
a6988fb1 6244 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
6245 job->num_files);
e903a8f6 6246 rename(con->filename, filename);
fc757c63 6247 ClearString(&con->filename);
e903a8f6 6248
6249 /*
6250 * See if we need to add the ending sheet...
6251 */
6252
1ed20df6 6253 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
6254 attr->num_values > 1)
e903a8f6 6255 {
6256 /*
6257 * Yes...
6258 */
6259
1ed20df6 6260 LogMessage(L_INFO, "Adding end banner page \"%s\" to job %d.",
6261 attr->values[1].string.text, job->id);
6262
b521f3fc 6263 kbytes = copy_banner(con, job, attr->values[1].string.text);
1ed20df6 6264
b521f3fc 6265 UpdateQuota(printer, job->username, 0, kbytes);
e903a8f6 6266 }
6267
fd0624de 6268 /*
6269 * Add any job subscriptions...
6270 */
6271
6272 add_job_subscriptions(con, job);
6273
6274 /*
6275 * Set all but the first two attributes to the job attributes group...
6276 */
6277
42f94780 6278 for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
fd0624de 6279 attr->group_tag = IPP_TAG_JOB;
6280
e903a8f6 6281 /*
6282 * Log and save the job...
6283 */
6284
6285 LogMessage(L_INFO, "Job %d queued on \'%s\' by \'%s\'.", job->id,
6286 job->dest, job->username);
aa7e125a 6287 LogMessage(L_DEBUG, "Job %d hold_until = %d", job->id, (int)job->hold_until);
e903a8f6 6288
4979ce3d 6289 SaveJob(job->id);
6290
277a6a9b 6291 cupsdAddEvent(CUPSD_EVENT_JOB_CREATED, printer, job, "Job created.");
6292
e31bfb6e 6293 /*
ceaef43a 6294 * Start the job if possible... Since CheckJobs() can cancel a job if it
6295 * doesn't print, we need to re-find the job afterwards...
e31bfb6e 6296 */
6297
ceaef43a 6298 jobid = job->id;
6299
e31bfb6e 6300 CheckJobs();
6301
ceaef43a 6302 job = FindJob(jobid);
6303
e31bfb6e 6304 /*
6305 * Fill in the response info...
6306 */
6307
a6988fb1 6308 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
e9f54388 6309 LocalPort, jobid);
99de6da0 6310
bd84e0d1 6311 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
e31bfb6e 6312
ceaef43a 6313 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
e31bfb6e 6314
1049abbe 6315 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
ceaef43a 6316 job ? job->state->values[0].integer : IPP_JOB_CANCELLED);
7ebf3a09 6317 add_job_state_reasons(con, job);
e31bfb6e 6318
0a3ac972 6319 con->response->request.status.status_code = IPP_OK;
e31bfb6e 6320}
6321
6322
56e9c951 6323/*
6324 * 'read_ps_job_ticket()' - Reads a job ticket embedded in a PS file.
6325 *
6326 * This function only gets called when printing a single PostScript
6327 * file using the Print-Job operation. It doesn't work for Create-Job +
6328 * Send-File, since the job attributes need to be set at job creation
6329 * time for banners to work. The embedded PS job ticket stuff is here
6330 * only to allow the Windows printer driver for CUPS to pass in JCL
6331 * options and IPP attributes which otherwise would be lost.
6332 *
6333 * The format of a PS job ticket is simple:
6334 *
6335 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
6336 *
6337 * %cupsJobTicket: attr1=value1
6338 * %cupsJobTicket: attr2=value2
6339 * ...
6340 * %cupsJobTicket: attrN=valueN
6341 *
6342 * Job ticket lines must appear immediately after the first line that
6343 * specifies PostScript format (%!PS-Adobe-3.0), and CUPS will stop
6344 * looking for job ticket info when it finds a line that does not begin
6345 * with "%cupsJobTicket:".
6346 *
6347 * The maximum length of a job ticket line, including the prefix, is
4e43f59e 6348 * 255 characters to conform with the Adobe DSC.
56e9c951 6349 *
6350 * Read-only attributes are rejected with a notice to the error log in
6351 * case a malicious user tries anything. Since the job ticket is read
6352 * prior to attribute validation in print_job(), job ticket attributes
6353 * will go through the same validation as IPP attributes...
6354 */
6355
6356static void
6357read_ps_job_ticket(client_t *con) /* I - Client connection */
6358{
d59a189c 6359 cups_file_t *fp; /* File to read from */
6360 char line[256]; /* Line data */
56e9c951 6361 int num_options; /* Number of options */
6362 cups_option_t *options; /* Options */
6363 ipp_t *ticket; /* New attributes */
6364 ipp_attribute_t *attr, /* Current attribute */
6365 *attr2, /* Job attribute */
6366 *prev2; /* Previous job attribute */
6367
6368
6369 /*
6370 * First open the print file...
6371 */
6372
d59a189c 6373 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
56e9c951 6374 {
6375 LogMessage(L_ERROR, "read_ps_job_ticket: Unable to open PostScript print file - %s",
6376 strerror(errno));
6377 return;
6378 }
6379
6380 /*
6381 * Skip the first line...
6382 */
6383
d59a189c 6384 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
56e9c951 6385 {
6386 LogMessage(L_ERROR, "read_ps_job_ticket: Unable to read from PostScript print file - %s",
4e43f59e 6387 strerror(errno));
d59a189c 6388 cupsFileClose(fp);
56e9c951 6389 return;
6390 }
6391
d59a189c 6392 if (strncmp(line, "%!PS-Adobe-", 11) != 0)
56e9c951 6393 {
6394 /*
6395 * Not a DSC-compliant file, so no job ticket info will be available...
6396 */
6397
d59a189c 6398 cupsFileClose(fp);
56e9c951 6399 return;
6400 }
6401
6402 /*
6403 * Read job ticket info from the file...
6404 */
6405
6406 num_options = 0;
6407 options = NULL;
6408
d59a189c 6409 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
56e9c951 6410 {
6411 /*
6412 * Stop at the first non-ticket line...
6413 */
6414
d59a189c 6415 if (strncmp(line, "%cupsJobTicket:", 15) != 0)
56e9c951 6416 break;
6417
6418 /*
6419 * Add the options to the option array...
6420 */
6421
d59a189c 6422 num_options = cupsParseOptions(line + 15, num_options, &options);
56e9c951 6423 }
6424
6425 /*
6426 * Done with the file; see if we have any options...
6427 */
6428
d59a189c 6429 cupsFileClose(fp);
56e9c951 6430
6431 if (num_options == 0)
6432 return;
6433
6434 /*
6435 * OK, convert the options to an attribute list, and apply them to
6436 * the request...
6437 */
6438
6439 ticket = ippNew();
6440 cupsEncodeOptions(ticket, num_options, options);
6441
6442 /*
6443 * See what the user wants to change.
6444 */
6445
6446 for (attr = ticket->attrs; attr != NULL; attr = attr->next)
6447 {
6448 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
6449 continue;
6450
6451 if (strcmp(attr->name, "job-originating-host-name") == 0 ||
6452 strcmp(attr->name, "job-originating-user-name") == 0 ||
6453 strcmp(attr->name, "job-media-sheets-completed") == 0 ||
6454 strcmp(attr->name, "job-k-octets") == 0 ||
6455 strcmp(attr->name, "job-id") == 0 ||
6456 strncmp(attr->name, "job-state", 9) == 0 ||
6457 strncmp(attr->name, "time-at-", 8) == 0)
6458 continue; /* Read-only attrs */
6459
6460 if ((attr2 = ippFindAttribute(con->request, attr->name, IPP_TAG_ZERO)) != NULL)
6461 {
6462 /*
6463 * Some other value; first free the old value...
6464 */
6465
2d5e06dd 6466 if (con->request->attrs == attr2)
50f63f23 6467 {
56e9c951 6468 con->request->attrs = attr2->next;
50f63f23 6469 prev2 = NULL;
6470 }
2d5e06dd 6471 else
6472 {
6473 for (prev2 = con->request->attrs; prev2 != NULL; prev2 = prev2->next)
6474 if (prev2->next == attr2)
6475 {
6476 prev2->next = attr2->next;
6477 break;
6478 }
6479 }
6480
6481 if (con->request->last == attr2)
6482 con->request->last = prev2;
56e9c951 6483
6484 _ipp_free_attr(attr2);
6485 }
6486
6487 /*
6488 * Add new option by copying it...
6489 */
6490
6491 copy_attribute(con->request, attr, 0);
6492 }
6493
6494 /*
6495 * Then free the attribute list and option array...
6496 */
6497
6498 ippDelete(ticket);
6499 cupsFreeOptions(num_options, options);
6500}
6501
6502
f3d580b9 6503/*
6504 * 'reject_jobs()' - Reject print jobs to a printer.
6505 */
6506
6507static void
6508reject_jobs(client_t *con, /* I - Client connection */
6509 ipp_attribute_t *uri) /* I - Printer or class URI */
6510{
a4b3db80 6511 cups_ptype_t dtype; /* Destination type (printer or class) */
6512 char method[HTTP_MAX_URI], /* Method portion of URI */
6513 username[HTTP_MAX_URI], /* Username portion of URI */
6514 host[HTTP_MAX_URI], /* Host portion of URI */
6515 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6516 int port; /* Port portion of URI */
6517 const char *name; /* Printer name */
6518 printer_t *printer; /* Printer data */
6519 ipp_attribute_t *attr; /* printer-state-message text */
f3d580b9 6520
6521
b2e10895 6522 LogMessage(L_DEBUG2, "reject_jobs(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 6523 uri->values[0].string.text);
f3d580b9 6524
6525 /*
6526 * Was this operation called from the correct URI?
6527 */
6528
6529 if (strncmp(con->uri, "/admin/", 7) != 0)
6530 {
5ea8888e 6531 LogMessage(L_ERROR, "reject_jobs: admin request on bad resource \'%s\'!",
1124e9ec 6532 con->uri);
f3d580b9 6533 send_ipp_error(con, IPP_NOT_AUTHORIZED);
6534 return;
6535 }
6536
6537 /*
6538 * Is the destination valid?
6539 */
6540
6541 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6542
bd5510a5 6543 if ((name = ValidateDest(host, resource, &dtype, &printer)) == NULL)
f3d580b9 6544 {
6545 /*
6546 * Bad URI...
6547 */
6548
5ea8888e 6549 LogMessage(L_ERROR, "reject_jobs: resource name \'%s\' no good!", resource);
f3d580b9 6550 send_ipp_error(con, IPP_NOT_FOUND);
6551 return;
6552 }
6553
e9f54388 6554 /*
6555 * Check policy...
6556 */
6557
99baf768 6558 if (!cupsdCheckPolicy(printer->op_policy_ptr, con, NULL))
e9f54388 6559 {
6560 LogMessage(L_ERROR, "reject_jobs: not authorized!");
6561 send_ipp_error(con, IPP_NOT_AUTHORIZED);
6562 return;
6563 }
6564
f3d580b9 6565 /*
6566 * Reject jobs sent to the printer...
6567 */
6568
f3d580b9 6569 printer->accepting = 0;
6570
6571 if ((attr = ippFindAttribute(con->request, "printer-state-message",
6572 IPP_TAG_TEXT)) == NULL)
6573 strcpy(printer->state_message, "Rejecting Jobs");
6574 else
def978d5 6575 strlcpy(printer->state_message, attr->values[0].string.text,
6576 sizeof(printer->state_message));
f3d580b9 6577
c8a55d2c 6578 AddPrinterHistory(printer);
6579
997cf8b0 6580 if (dtype & CUPS_PRINTER_CLASS)
6581 {
5d99df62 6582 SaveAllClasses();
5d99df62 6583
5ea8888e 6584 LogMessage(L_INFO, "Class \'%s\' rejecting jobs (\'%s\').", name,
cc0561c6 6585 con->username);
997cf8b0 6586 }
cc0561c6 6587 else
997cf8b0 6588 {
6589 SaveAllPrinters();
6590
5ea8888e 6591 LogMessage(L_INFO, "Printer \'%s\' rejecting jobs (\'%s\').", name,
cc0561c6 6592 con->username);
997cf8b0 6593 }
cc0561c6 6594
f3d580b9 6595 /*
6596 * Everything was ok, so return OK status...
6597 */
6598
0a3ac972 6599 con->response->request.status.status_code = IPP_OK;
f3d580b9 6600}
6601
6602
bd84e0d1 6603/*
f63a2256 6604 * 'release_job()' - Release a held print job.
6605 */
6606
6607static void
6608release_job(client_t *con, /* I - Client connection */
6609 ipp_attribute_t *uri) /* I - Job or Printer URI */
6610{
a4b3db80 6611 ipp_attribute_t *attr; /* Current attribute */
6612 int jobid; /* Job ID */
6613 char method[HTTP_MAX_URI], /* Method portion of URI */
6614 username[HTTP_MAX_URI], /* Username portion of URI */
6615 host[HTTP_MAX_URI], /* Host portion of URI */
6616 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6617 int port; /* Port portion of URI */
6618 job_t *job; /* Job information */
f63a2256 6619
6620
b2e10895 6621 LogMessage(L_DEBUG2, "release_job(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 6622 uri->values[0].string.text);
f63a2256 6623
6624 /*
6625 * Verify that the POST operation was done to a valid URI.
6626 */
6627
6628 if (strncmp(con->uri, "/classes/", 9) != 0 &&
6629 strncmp(con->uri, "/jobs/", 5) != 0 &&
6630 strncmp(con->uri, "/printers/", 10) != 0)
6631 {
6632 LogMessage(L_ERROR, "release_job: release request on bad resource \'%s\'!",
6633 con->uri);
6634 send_ipp_error(con, IPP_NOT_AUTHORIZED);
6635 return;
6636 }
6637
6638 /*
6639 * See if we have a job URI or a printer URI...
6640 */
6641
6642 if (strcmp(uri->name, "printer-uri") == 0)
6643 {
6644 /*
6645 * Got a printer URI; see if we also have a job-id attribute...
6646 */
6647
6648 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
6649 {
6650 LogMessage(L_ERROR, "release_job: got a printer-uri attribute but no job-id!");
6651 send_ipp_error(con, IPP_BAD_REQUEST);
6652 return;
6653 }
6654
6655 jobid = attr->values[0].integer;
6656 }
6657 else
6658 {
6659 /*
6660 * Got a job URI; parse it to get the job ID...
6661 */
6662
6663 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6664
6665 if (strncmp(resource, "/jobs/", 6) != 0)
6666 {
6667 /*
6668 * Not a valid URI!
6669 */
6670
6671 LogMessage(L_ERROR, "release_job: bad job-uri attribute \'%s\'!",
6672 uri->values[0].string.text);
6673 send_ipp_error(con, IPP_BAD_REQUEST);
6674 return;
6675 }
6676
6677 jobid = atoi(resource + 6);
6678 }
6679
6680 /*
6681 * See if the job exists...
6682 */
6683
6684 if ((job = FindJob(jobid)) == NULL)
6685 {
6686 /*
6687 * Nope - return a "not found" error...
6688 */
6689
6690 LogMessage(L_ERROR, "release_job: job #%d doesn't exist!", jobid);
6691 send_ipp_error(con, IPP_NOT_FOUND);
6692 return;
6693 }
6694
6695 /*
6696 * See if job is "held"...
6697 */
6698
6699 if (job->state->values[0].integer != IPP_JOB_HELD)
6700 {
6701 /*
6702 * Nope - return a "not possible" error...
6703 */
6704
6705 LogMessage(L_ERROR, "release_job: job #%d is not held!", jobid);
6706 send_ipp_error(con, IPP_NOT_POSSIBLE);
6707 return;
6708 }
6709
6710 /*
6711 * See if the job is owned by the requesting user...
6712 */
6713
bd5510a5 6714 if (!validate_user(job, con, job->username, username, sizeof(username)))
f63a2256 6715 {
ed3e11d8 6716 LogMessage(L_ERROR, "release_job: \"%s\" not authorized to release job id %d owned by \"%s\"!",
6717 username, jobid, job->username);
6718 send_ipp_error(con, IPP_FORBIDDEN);
6719 return;
f63a2256 6720 }
6721
5856eaaf 6722 /*
6723 * Reset the job-hold-until value to "no-hold"...
6724 */
6725
6726 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
6727 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
6728
6729 if (attr != NULL)
6730 {
6731 free(attr->values[0].string.text);
6732 attr->value_tag = IPP_TAG_KEYWORD;
6733 attr->values[0].string.text = strdup("no-hold");
6734 }
6735
f63a2256 6736 /*
6737 * Release the job and return...
6738 */
6739
6740 ReleaseJob(jobid);
6741
c8f336b5 6742 LogMessage(L_INFO, "Job %d was released by \'%s\'.", jobid, username);
f63a2256 6743
0a3ac972 6744 con->response->request.status.status_code = IPP_OK;
f63a2256 6745}
6746
6747
a4b3db80 6748/*
6749 * 'renew_subscription()' - Renew an existing subscription...
6750 */
6751
6752static void
6753renew_subscription(client_t *con, /* I - Client connection */
6754 int sub_id) /* I - Subscription ID */
6755{
6756}
6757
6758
f63a2256 6759/*
6760 * 'restart_job()' - Restart an old print job.
bd84e0d1 6761 */
6762
6763static void
6764restart_job(client_t *con, /* I - Client connection */
a4b3db80 6765 ipp_attribute_t *uri) /* I - Job or Printer URI */
bd84e0d1 6766{
a4b3db80 6767 ipp_attribute_t *attr; /* Current attribute */
6768 int jobid; /* Job ID */
6769 char method[HTTP_MAX_URI], /* Method portion of URI */
6770 username[HTTP_MAX_URI], /* Username portion of URI */
6771 host[HTTP_MAX_URI], /* Host portion of URI */
6772 resource[HTTP_MAX_URI]; /* Resource portion of URI */
6773 int port; /* Port portion of URI */
6774 job_t *job; /* Job information */
bd84e0d1 6775
6776
b2e10895 6777 LogMessage(L_DEBUG2, "restart_job(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 6778 uri->values[0].string.text);
bd84e0d1 6779
6780 /*
6781 * Verify that the POST operation was done to a valid URI.
6782 */
6783
6784 if (strncmp(con->uri, "/classes/", 9) != 0 &&
6785 strncmp(con->uri, "/jobs/", 5) != 0 &&
6786 strncmp(con->uri, "/printers/", 10) != 0)
6787 {
5ea8888e 6788 LogMessage(L_ERROR, "restart_job: restart request on bad resource \'%s\'!",
bd84e0d1 6789 con->uri);
6790 send_ipp_error(con, IPP_NOT_AUTHORIZED);
6791 return;
6792 }
6793
6794 /*
6795 * See if we have a job URI or a printer URI...
6796 */
6797
6798 if (strcmp(uri->name, "printer-uri") == 0)
6799 {
6800 /*
6801 * Got a printer URI; see if we also have a job-id attribute...
6802 */
6803
6804 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
6805 {
5ea8888e 6806 LogMessage(L_ERROR, "restart_job: got a printer-uri attribute but no job-id!");
bd84e0d1 6807 send_ipp_error(con, IPP_BAD_REQUEST);
6808 return;
6809 }
6810
6811 jobid = attr->values[0].integer;
6812 }
6813 else
6814 {
6815 /*
6816 * Got a job URI; parse it to get the job ID...
6817 */
6818
6819 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6820
6821 if (strncmp(resource, "/jobs/", 6) != 0)
6822 {
6823 /*
6824 * Not a valid URI!
6825 */
6826
5ea8888e 6827 LogMessage(L_ERROR, "restart_job: bad job-uri attribute \'%s\'!",
bd84e0d1 6828 uri->values[0].string.text);
6829 send_ipp_error(con, IPP_BAD_REQUEST);
6830 return;
6831 }
6832
6833 jobid = atoi(resource + 6);
6834 }
6835
6836 /*
6837 * See if the job exists...
6838 */
6839
6840 if ((job = FindJob(jobid)) == NULL)
6841 {
6842 /*
6843 * Nope - return a "not found" error...
6844 */
6845
5ea8888e 6846 LogMessage(L_ERROR, "restart_job: job #%d doesn't exist!", jobid);
bd84e0d1 6847 send_ipp_error(con, IPP_NOT_FOUND);
6848 return;
6849 }
6850
f63a2256 6851 /*
6852 * See if job is in any of the "completed" states...
6853 */
6854
6855 if (job->state->values[0].integer <= IPP_JOB_PROCESSING)
6856 {
6857 /*
6858 * Nope - return a "not possible" error...
6859 */
6860
6861 LogMessage(L_ERROR, "restart_job: job #%d is not complete!", jobid);
6862 send_ipp_error(con, IPP_NOT_POSSIBLE);
6863 return;
6864 }
6865
6866 /*
6867 * See if we have retained the job files...
6868 */
6869
d4c438d4 6870 if (!JobFiles && job->state->values[0].integer > IPP_JOB_STOPPED)
f63a2256 6871 {
6872 /*
6873 * Nope - return a "not possible" error...
6874 */
6875
6876 LogMessage(L_ERROR, "restart_job: job #%d cannot be restarted - no files!", jobid);
6877 send_ipp_error(con, IPP_NOT_POSSIBLE);
6878 return;
6879 }
6880
bd84e0d1 6881 /*
6882 * See if the job is owned by the requesting user...
6883 */
6884
bd5510a5 6885 if (!validate_user(job, con, job->username, username, sizeof(username)))
bd84e0d1 6886 {
ed3e11d8 6887 LogMessage(L_ERROR, "restart_job: \"%s\" not authorized to restart job id %d owned by \"%s\"!",
6888 username, jobid, job->username);
6889 send_ipp_error(con, IPP_FORBIDDEN);
6890 return;
bd84e0d1 6891 }
6892
6893 /*
6894 * Restart the job and return...
6895 */
6896
6897 RestartJob(jobid);
6898
c8f336b5 6899 LogMessage(L_INFO, "Job %d was restarted by \'%s\'.", jobid, username);
bd84e0d1 6900
0a3ac972 6901 con->response->request.status.status_code = IPP_OK;
bd84e0d1 6902}
6903
6904
48e211f3 6905/*
6906 * 'save_auth_info()' - Save authentication information for a job.
6907 */
6908
6909static void
6910save_auth_info(client_t *con, /* I - Client connection */
6911 int job_id) /* I - Job ID */
6912{
6913 int i; /* Looping var */
6914 char filename[1024]; /* Job authentication filename */
6915 cups_file_t *fp; /* Job authentication file */
6916 char line[1024]; /* Line for file */
6917
6918
6919 /*
6920 * This function saves the in-memory authentication information for
6921 * a job so that it can be used to authenticate with a remote host.
6922 * The information is stored in a file that is readable only by the
6923 * root user. The username and password are Base-64 encoded, each
6924 * on a separate line, followed by random number (up to 1024) of
6925 * newlines to limit the amount of information that is exposed.
6926 *
6927 * Because of the potential for exposing of authentication information,
6928 * this functionality is only enabled when running cupsd as root.
6929 *
6930 * This caching only works for the Basic and BasicDigest authentication
6931 * types. Digest authentication cannot be cached this way, and in
6932 * the future Kerberos authentication may make all of this obsolete.
6933 *
6934 * Authentication information is saved whenever an authenticated
6935 * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
6936 * performed.
6937 *
6938 * This information is deleted after a job is completed or canceled,
6939 * so reprints may require subsequent re-authentication.
6940 */
6941
6942 if (RunUser)
6943 return;
6944
6945 /*
6946 * Create the authentication file and change permissions...
6947 */
6948
6949 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job_id);
6950 if ((fp = cupsFileOpen(filename, "w")) == NULL)
6951 {
6952 LogMessage(L_ERROR, "Unable to save authentication info to \"%s\" - %s",
6953 filename, strerror(errno));
6954 return;
6955 }
6956
6957 fchown(cupsFileNumber(fp), 0, 0);
6958 fchmod(cupsFileNumber(fp), 0400);
6959
6960 /*
6961 * Write the authenticated username...
6962 */
6963
6964 httpEncode64_2(line, sizeof(line), con->username, strlen(con->username));
6965 cupsFilePrintf(fp, "%s\n", line);
6966
6967 /*
6968 * Write the authenticated password...
6969 */
6970
6971 httpEncode64_2(line, sizeof(line), con->password, strlen(con->password));
6972 cupsFilePrintf(fp, "%s\n", line);
6973
6974 /*
6975 * Write a random number of newlines to the end of the file...
6976 */
6977
6978 for (i = (rand() % 1024); i >= 0; i --)
6979 cupsFilePutChar(fp, '\n');
6980
6981 /*
6982 * Close the file and return...
6983 */
6984
6985 cupsFileClose(fp);
6986}
6987
6988
bd84e0d1 6989/*
6990 * 'send_document()' - Send a file to a printer or class.
6991 */
6992
6993static void
6994send_document(client_t *con, /* I - Client connection */
6995 ipp_attribute_t *uri) /* I - Printer URI */
6996{
6997 ipp_attribute_t *attr; /* Current attribute */
6998 ipp_attribute_t *format; /* Document-format attribute */
bd84e0d1 6999 int jobid; /* Job ID number */
7000 job_t *job; /* Current job */
7001 char job_uri[HTTP_MAX_URI],
7002 /* Job URI */
7003 method[HTTP_MAX_URI],
7004 /* Method portion of URI */
7005 username[HTTP_MAX_URI],
7006 /* Username portion of URI */
7007 host[HTTP_MAX_URI],
7008 /* Host portion of URI */
7009 resource[HTTP_MAX_URI];
7010 /* Resource portion of URI */
7011 int port; /* Port portion of URI */
a3e17a89 7012 mime_type_t *filetype; /* Type of file */
bd84e0d1 7013 char super[MIME_MAX_SUPER],
7014 /* Supertype of file */
7015 type[MIME_MAX_TYPE],
7016 /* Subtype of file */
7017 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
7018 /* Textual name of mime type */
e09246c8 7019 char filename[1024]; /* Job filename */
a3e17a89 7020 printer_t *printer; /* Current printer */
91606789 7021 struct stat fileinfo; /* File information */
b521f3fc 7022 int kbytes; /* Size of file */
d59a189c 7023 int compression; /* Type of compression */
bd84e0d1 7024
7025
b2e10895 7026 LogMessage(L_DEBUG2, "send_document(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 7027 uri->values[0].string.text);
bd84e0d1 7028
7029 /*
7030 * Verify that the POST operation was done to a valid URI.
7031 */
7032
7033 if (strncmp(con->uri, "/classes/", 9) != 0 &&
7034 strncmp(con->uri, "/jobs/", 6) != 0 &&
7035 strncmp(con->uri, "/printers/", 10) != 0)
7036 {
5ea8888e 7037 LogMessage(L_ERROR, "send_document: print request on bad resource \'%s\'!",
bd84e0d1 7038 con->uri);
7039 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7040 return;
7041 }
7042
7043 /*
7044 * See if we have a job URI or a printer URI...
7045 */
7046
7047 if (strcmp(uri->name, "printer-uri") == 0)
7048 {
7049 /*
7050 * Got a printer URI; see if we also have a job-id attribute...
7051 */
7052
7053 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
7054 {
5ea8888e 7055 LogMessage(L_ERROR, "send_document: got a printer-uri attribute but no job-id!");
bd84e0d1 7056 send_ipp_error(con, IPP_BAD_REQUEST);
7057 return;
7058 }
7059
7060 jobid = attr->values[0].integer;
7061 }
7062 else
7063 {
7064 /*
7065 * Got a job URI; parse it to get the job ID...
7066 */
7067
7068 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
7069
7070 if (strncmp(resource, "/jobs/", 6) != 0)
7071 {
7072 /*
7073 * Not a valid URI!
7074 */
7075
5ea8888e 7076 LogMessage(L_ERROR, "send_document: bad job-uri attribute \'%s\'!",
bd84e0d1 7077 uri->values[0].string.text);
7078 send_ipp_error(con, IPP_BAD_REQUEST);
7079 return;
7080 }
7081
7082 jobid = atoi(resource + 6);
7083 }
7084
7085 /*
7086 * See if the job exists...
7087 */
7088
7089 if ((job = FindJob(jobid)) == NULL)
7090 {
7091 /*
7092 * Nope - return a "not found" error...
7093 */
7094
5ea8888e 7095 LogMessage(L_ERROR, "send_document: job #%d doesn't exist!", jobid);
bd84e0d1 7096 send_ipp_error(con, IPP_NOT_FOUND);
7097 return;
7098 }
7099
7100 /*
7101 * See if the job is owned by the requesting user...
7102 */
7103
bd5510a5 7104 if (!validate_user(job, con, job->username, username, sizeof(username)))
bd84e0d1 7105 {
ed3e11d8 7106 LogMessage(L_ERROR, "send_document: \"%s\" not authorized to send document for job id %d owned by \"%s\"!",
7107 username, jobid, job->username);
7108 send_ipp_error(con, IPP_FORBIDDEN);
7109 return;
bd84e0d1 7110 }
7111
7112 /*
7113 * OK, see if the client is sending the document compressed - CUPS
d59a189c 7114 * only supports "none" and "gzip".
bd84e0d1 7115 */
7116
d59a189c 7117 compression = CUPS_FILE_NONE;
7118
7119 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL)
bd84e0d1 7120 {
d59a189c 7121 if (strcmp(attr->values[0].string.text, "none")
7122#ifdef HAVE_LIBZ
7123 && strcmp(attr->values[0].string.text, "gzip")
7124#endif /* HAVE_LIBZ */
7125 )
7126 {
7127 LogMessage(L_ERROR, "print_job: Unsupported compression \"%s\"!",
7128 attr->values[0].string.text);
7129 send_ipp_error(con, IPP_ATTRIBUTES);
7130 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
7131 "compression", NULL, attr->values[0].string.text);
7132 return;
7133 }
7134
7135#ifdef HAVE_LIBZ
7136 if (!strcmp(attr->values[0].string.text, "gzip"))
7137 compression = CUPS_FILE_GZIP;
7138#endif /* HAVE_LIBZ */
bd84e0d1 7139 }
7140
7141 /*
7142 * Do we have a file to print?
7143 */
7144
fc757c63 7145 if (!con->filename)
bd84e0d1 7146 {
5ea8888e 7147 LogMessage(L_ERROR, "send_document: No file!?!");
bd84e0d1 7148 send_ipp_error(con, IPP_BAD_REQUEST);
7149 return;
7150 }
7151
7152 /*
7153 * Is it a format we support?
7154 */
7155
7156 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
7157 {
7158 /*
7159 * Grab format from client...
7160 */
7161
7162 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
7163 {
5ea8888e 7164 LogMessage(L_ERROR, "send_document: could not scan type \'%s\'!",
bd84e0d1 7165 format->values[0].string.text);
7166 send_ipp_error(con, IPP_BAD_REQUEST);
7167 return;
7168 }
7169 }
7170 else
7171 {
7172 /*
7173 * No document format attribute? Auto-type it!
7174 */
7175
7176 strcpy(super, "application");
7177 strcpy(type, "octet-stream");
7178 }
7179
7180 if (strcmp(super, "application") == 0 &&
7181 strcmp(type, "octet-stream") == 0)
7182 {
7183 /*
7184 * Auto-type the file...
7185 */
7186
5ea8888e 7187 LogMessage(L_DEBUG, "send_document: auto-typing file...");
bd84e0d1 7188
d59a189c 7189 filetype = mimeFileType(MimeDatabase, con->filename, &compression);
bd84e0d1 7190
7191 if (filetype != NULL)
7192 {
7193 /*
7194 * Replace the document-format attribute value with the auto-typed one.
7195 */
7196
a6988fb1 7197 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
7198 filetype->type);
bd84e0d1 7199
7200 if (format != NULL)
7201 {
7202 free(format->values[0].string.text);
7203 format->values[0].string.text = strdup(mimetype);
7204 }
7205 else
7206 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
7207 "document-format", NULL, mimetype);
7208 }
526462ee 7209 else
7210 filetype = mimeType(MimeDatabase, super, type);
bd84e0d1 7211 }
7212 else
7213 filetype = mimeType(MimeDatabase, super, type);
7214
7215 if (filetype == NULL)
7216 {
e93faa10 7217 LogMessage(L_ERROR, "send_document: Unsupported format \'%s/%s\'!",
5fa65e01 7218 super, type);
deb855a2 7219 LogMessage(L_INFO, "Hint: Do you have the raw file printing rules enabled?");
bd84e0d1 7220 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
e93faa10 7221
7222 if (format)
7223 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
7224 "document-format", NULL, format->values[0].string.text);
7225
bd84e0d1 7226 return;
7227 }
7228
5ea8888e 7229 LogMessage(L_DEBUG, "send_document: request file type is %s/%s.",
bd84e0d1 7230 filetype->super, filetype->type);
7231
7232 /*
7233 * Add the file to the job...
7234 */
7235
d59a189c 7236 if (add_file(con, job, filetype, compression))
bd84e0d1 7237 return;
bd84e0d1 7238
b521f3fc 7239 if (job->dtype & CUPS_PRINTER_CLASS)
7240 printer = FindClass(job->dest);
7241 else
7242 printer = FindPrinter(job->dest);
7243
7244 if (stat(con->filename, &fileinfo))
7245 kbytes = 0;
7246 else
7247 kbytes = (fileinfo.st_size + 1023) / 1024;
7248
7249 UpdateQuota(printer, job->username, 0, kbytes);
7250
7251 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
7252 attr->values[0].integer += kbytes;
91606789 7253
a6988fb1 7254 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
7255 job->num_files);
bd84e0d1 7256 rename(con->filename, filename);
7257
fc757c63 7258 ClearString(&con->filename);
bd84e0d1 7259
b4257036 7260 LogMessage(L_INFO, "File of type %s/%s queued in job #%d by \'%s\'.",
7261 filetype->super, filetype->type, job->id, job->username);
bd84e0d1 7262
1049abbe 7263 /*
7264 * Start the job if this is the last document...
7265 */
7266
7267 if ((attr = ippFindAttribute(con->request, "last-document", IPP_TAG_BOOLEAN)) != NULL &&
7268 attr->values[0].boolean)
7269 {
a3e17a89 7270 /*
7271 * See if we need to add the ending sheet...
7272 */
7273
1ed20df6 7274 if (printer != NULL &&
7275 !(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
ebde520c 7276 (attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL &&
e903a8f6 7277 attr->num_values > 1)
a3e17a89 7278 {
7279 /*
7280 * Yes...
7281 */
7282
1ed20df6 7283 LogMessage(L_INFO, "Adding end banner page \"%s\" to job %d.",
7284 attr->values[1].string.text, job->id);
7285
b521f3fc 7286 kbytes = copy_banner(con, job, attr->values[1].string.text);
1ed20df6 7287
b521f3fc 7288 UpdateQuota(printer, job->username, 0, kbytes);
a3e17a89 7289 }
7290
e583bc2d 7291 if (job->state->values[0].integer == IPP_JOB_STOPPED)
7292 job->state->values[0].integer = IPP_JOB_PENDING;
9d8d71c1 7293 else if (job->state->values[0].integer == IPP_JOB_HELD)
7294 {
7295 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
7296 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
7297
7298 if (attr == NULL || strcmp(attr->values[0].string.text, "no-hold") == 0)
7299 job->state->values[0].integer = IPP_JOB_PENDING;
7300 }
e583bc2d 7301
1e437ea6 7302 SaveJob(job->id);
ceaef43a 7303
7304 /*
7305 * Start the job if possible... Since CheckJobs() can cancel a job if it
7306 * doesn't print, we need to re-find the job afterwards...
7307 */
7308
7309 jobid = job->id;
7310
1049abbe 7311 CheckJobs();
ceaef43a 7312
7313 job = FindJob(jobid);
1049abbe 7314 }
4b96a841 7315 else
1e437ea6 7316 {
4b96a841 7317 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
7318 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
7319
7320 if (attr == NULL || strcmp(attr->values[0].string.text, "no-hold") == 0)
7321 {
7322 job->state->values[0].integer = IPP_JOB_HELD;
7323 job->hold_until = time(NULL) + 60;
7324 SaveJob(job->id);
7325 }
1e437ea6 7326 }
1049abbe 7327
bd84e0d1 7328 /*
7329 * Fill in the response info...
7330 */
7331
a6988fb1 7332 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
e9f54388 7333 LocalPort, jobid);
99de6da0 7334
1049abbe 7335 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
7336 job_uri);
bd84e0d1 7337
ceaef43a 7338 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
bd84e0d1 7339
1049abbe 7340 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
ceaef43a 7341 job ? job->state->values[0].integer : IPP_JOB_CANCELLED);
7ebf3a09 7342 add_job_state_reasons(con, job);
bd84e0d1 7343
0a3ac972 7344 con->response->request.status.status_code = IPP_OK;
bd84e0d1 7345}
7346
7347
e31bfb6e 7348/*
7349 * 'send_ipp_error()' - Send an error status back to the IPP client.
7350 */
7351
7352static void
7353send_ipp_error(client_t *con, /* I - Client connection */
7354 ipp_status_t status) /* I - IPP status code */
7355{
b2e10895 7356 LogMessage(L_DEBUG2, "send_ipp_error(%p[%d], %x)\n", con, con->http.fd,
7357 status);
e31bfb6e 7358
dbb05cda 7359 LogMessage(L_DEBUG, "Sending error: %s", ippErrorString(status));
e31bfb6e 7360
b38d93df 7361 if (status == IPP_NOT_AUTHORIZED)
45d9c992 7362 {
7363 /*
7364 * Send HTTP_UNAUTHORIZED response instead of IPP response, so that
7365 * the client will properly authenticate the request...
7366 */
7367
7368 SendError(con, HTTP_UNAUTHORIZED);
7369
7370 ippDelete(con->response);
7371 con->response = NULL;
7372
7373 return;
7374 }
7375
0a3ac972 7376 con->response->request.status.status_code = status;
dd41ddbb 7377
7378 if (ippFindAttribute(con->response, "attributes-charset", IPP_TAG_ZERO) == NULL)
7379 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
7380 "attributes-charset", NULL, DefaultCharset);
7381
7382 if (ippFindAttribute(con->response, "attributes-natural-language",
7383 IPP_TAG_ZERO) == NULL)
7384 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
7385 "attributes-natural-language", NULL, DefaultLanguage);
e31bfb6e 7386}
7387
7388
3270670b 7389/*
7390 * 'set_default()' - Set the default destination...
7391 */
7392
7393static void
7394set_default(client_t *con, /* I - Client connection */
7395 ipp_attribute_t *uri) /* I - Printer URI */
7396{
7397 cups_ptype_t dtype; /* Destination type (printer or class) */
7398 char method[HTTP_MAX_URI],
7399 /* Method portion of URI */
7400 username[HTTP_MAX_URI],
7401 /* Username portion of URI */
7402 host[HTTP_MAX_URI],
7403 /* Host portion of URI */
7404 resource[HTTP_MAX_URI];
7405 /* Resource portion of URI */
7406 int port; /* Port portion of URI */
1049abbe 7407 const char *name; /* Printer name */
bd5510a5 7408 printer_t *printer; /* Printer */
3270670b 7409
7410
b2e10895 7411 LogMessage(L_DEBUG2, "set_default(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 7412 uri->values[0].string.text);
3270670b 7413
7414 /*
7415 * Was this operation called from the correct URI?
7416 */
7417
7418 if (strncmp(con->uri, "/admin/", 7) != 0)
7419 {
5ea8888e 7420 LogMessage(L_ERROR, "set_default: admin request on bad resource \'%s\'!",
1124e9ec 7421 con->uri);
3270670b 7422 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7423 return;
7424 }
7425
7426 /*
7427 * Is the destination valid?
7428 */
7429
7430 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
7431
bd5510a5 7432 if ((name = ValidateDest(host, resource, &dtype, &printer)) == NULL)
3270670b 7433 {
7434 /*
7435 * Bad URI...
7436 */
7437
5ea8888e 7438 LogMessage(L_ERROR, "set_default: resource name \'%s\' no good!", resource);
3270670b 7439 send_ipp_error(con, IPP_NOT_FOUND);
7440 return;
7441 }
7442
e9f54388 7443 /*
7444 * Check policy...
7445 */
7446
99baf768 7447 if (!cupsdCheckPolicy(DefaultPolicyPtr, con, NULL))
e9f54388 7448 {
7449 LogMessage(L_ERROR, "set_default: not authorized!");
7450 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7451 return;
7452 }
7453
3270670b 7454 /*
7455 * Set it as the default...
7456 */
7457
bd5510a5 7458 DefaultPrinter = printer;
3270670b 7459
cc0561c6 7460 SaveAllPrinters();
7461 SaveAllClasses();
7462
5ea8888e 7463 LogMessage(L_INFO, "Default destination set to \'%s\' by \'%s\'.", name,
cc0561c6 7464 con->username);
7465
3270670b 7466 /*
7467 * Everything was ok, so return OK status...
7468 */
7469
0a3ac972 7470 con->response->request.status.status_code = IPP_OK;
3270670b 7471}
7472
7473
9cbd98eb 7474/*
7475 * 'set_job_attrs()' - Set job attributes.
7476 */
7477
7478static void
be87dcd3 7479set_job_attrs(client_t *con, /* I - Client connection */
7480 ipp_attribute_t *uri) /* I - Job URI */
9cbd98eb 7481{
a3e17a89 7482 ipp_attribute_t *attr, /* Current attribute */
a3e17a89 7483 *attr2, /* Job attribute */
7484 *prev2; /* Previous job attribute */
9cbd98eb 7485 int jobid; /* Job ID */
7486 job_t *job; /* Current job */
9cbd98eb 7487 char method[HTTP_MAX_URI],
7488 /* Method portion of URI */
7489 username[HTTP_MAX_URI],
7490 /* Username portion of URI */
7491 host[HTTP_MAX_URI],
7492 /* Host portion of URI */
7493 resource[HTTP_MAX_URI];
7494 /* Resource portion of URI */
7495 int port; /* Port portion of URI */
9cbd98eb 7496
7497
b2e10895 7498 LogMessage(L_DEBUG2, "set_job_attrs(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 7499 uri->values[0].string.text);
9cbd98eb 7500
8fc34542 7501 /*
7502 * Start with "everything is OK" status...
7503 */
7504
7505 con->response->request.status.status_code = IPP_OK;
7506
9cbd98eb 7507 /*
7508 * See if we have a job URI or a printer URI...
7509 */
7510
7511 if (strcmp(uri->name, "printer-uri") == 0)
7512 {
7513 /*
7514 * Got a printer URI; see if we also have a job-id attribute...
7515 */
7516
7517 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
7518 {
7519 LogMessage(L_ERROR, "set_job_attrs: got a printer-uri attribute but no job-id!");
7520 send_ipp_error(con, IPP_BAD_REQUEST);
7521 return;
7522 }
7523
7524 jobid = attr->values[0].integer;
7525 }
7526 else
7527 {
7528 /*
7529 * Got a job URI; parse it to get the job ID...
7530 */
7531
7532 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
7533
7534 if (strncmp(resource, "/jobs/", 6) != 0)
7535 {
7536 /*
7537 * Not a valid URI!
7538 */
7539
7540 LogMessage(L_ERROR, "set_job_attrs: bad job-uri attribute \'%s\'!\n",
7541 uri->values[0].string.text);
7542 send_ipp_error(con, IPP_BAD_REQUEST);
7543 return;
7544 }
7545
7546 jobid = atoi(resource + 6);
7547 }
7548
7549 /*
7550 * See if the job exists...
7551 */
7552
7553 if ((job = FindJob(jobid)) == NULL)
7554 {
7555 /*
7556 * Nope - return a "not found" error...
7557 */
7558
7559 LogMessage(L_ERROR, "set_job_attrs: job #%d doesn't exist!", jobid);
7560 send_ipp_error(con, IPP_NOT_FOUND);
7561 return;
7562 }
7563
7564 /*
7565 * See if the job has been completed...
7566 */
7567
7568 if (job->state->values[0].integer > IPP_JOB_STOPPED)
7569 {
7570 /*
7571 * Return a "not-possible" error...
7572 */
7573
7574 LogMessage(L_ERROR, "set_job_attrs: job #%d is finished and cannot be altered!", jobid);
7575 send_ipp_error(con, IPP_NOT_POSSIBLE);
7576 return;
7577 }
7578
7579 /*
7580 * See if the job is owned by the requesting user...
7581 */
7582
bd5510a5 7583 if (!validate_user(job, con, job->username, username, sizeof(username)))
9cbd98eb 7584 {
ed3e11d8 7585 LogMessage(L_ERROR, "set_job_attrs: \"%s\" not authorized to alter job id %d owned by \"%s\"!",
7586 username, jobid, job->username);
7587 send_ipp_error(con, IPP_FORBIDDEN);
7588 return;
9cbd98eb 7589 }
7590
7591 /*
7592 * See what the user wants to change.
a3e17a89 7593 */
7594
962e5a9f 7595 for (attr = con->request->attrs; attr != NULL; attr = attr->next)
9cbd98eb 7596 {
a3e17a89 7597 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
7598 continue;
9cbd98eb 7599
8fc34542 7600 if (!strcmp(attr->name, "attributes-charset") ||
7601 !strcmp(attr->name, "attributes-natural-language") ||
7602 !strcmp(attr->name, "document-compression") ||
7603 !strcmp(attr->name, "document-format") ||
7604 !strcmp(attr->name, "job-detailed-status-messages") ||
7605 !strcmp(attr->name, "job-document-access-errors") ||
7606 !strcmp(attr->name, "job-id") ||
7607 !strcmp(attr->name, "job-k-octets") ||
7608 !strcmp(attr->name, "job-originating-host-name") ||
7609 !strcmp(attr->name, "job-originating-user-name") ||
7610 !strcmp(attr->name, "job-printer-up-time") ||
7611 !strcmp(attr->name, "job-printer-uri") ||
7612 !strcmp(attr->name, "job-sheets") ||
7613 !strcmp(attr->name, "job-state-message") ||
7614 !strcmp(attr->name, "job-state-reasons") ||
7615 !strcmp(attr->name, "job-uri") ||
7616 !strcmp(attr->name, "number-of-documents") ||
7617 !strcmp(attr->name, "number-of-intervening-jobs") ||
7618 !strcmp(attr->name, "output-device-assigned") ||
7619 !strncmp(attr->name, "date-time-at-", 13) ||
7620 !strncmp(attr->name, "job-impressions", 15) ||
7621 !strncmp(attr->name, "job-k-octets", 12) ||
7622 !strncmp(attr->name, "job-media-sheets", 16) ||
7623 !strncmp(attr->name, "time-at-", 8))
7624 {
7625 /*
7626 * Read-only attrs!
7627 */
bb27c189 7628
8fc34542 7629 send_ipp_error(con, IPP_ATTRIBUTES_NOT_SETTABLE);
7630
7631 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
7632 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
7633
7634 continue;
7635 }
7636
7637 if (!strcmp(attr->name, "job-priority"))
9cbd98eb 7638 {
7639 /*
8fc34542 7640 * Change the job priority...
9cbd98eb 7641 */
7642
8fc34542 7643 if (attr->value_tag != IPP_TAG_INTEGER)
7644 {
7645 send_ipp_error(con, IPP_REQUEST_VALUE);
7646
7647 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
7648 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
7649 }
7650 else if (job->state->values[0].integer >= IPP_JOB_PROCESSING)
7651 {
7652 send_ipp_error(con, IPP_NOT_POSSIBLE);
7653 return;
7654 }
7655 else if (con->response->request.status.status_code == IPP_OK)
7656 SetJobPriority(jobid, attr->values[0].integer);
9cbd98eb 7657 }
8fc34542 7658 else if (!strcmp(attr->name, "job-state"))
7659 {
7660 /*
7661 * Change the job state...
7662 */
7663
7664 if (attr->value_tag != IPP_TAG_ENUM)
7665 {
7666 send_ipp_error(con, IPP_REQUEST_VALUE);
7667
7668 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
7669 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
7670 }
7671 else
7672 {
7673 switch (attr->values[0].integer)
7674 {
7675 case IPP_JOB_PENDING :
7676 case IPP_JOB_HELD :
7677 if (job->state->values[0].integer > IPP_JOB_HELD)
7678 {
7679 send_ipp_error(con, IPP_NOT_POSSIBLE);
7680 return;
7681 }
7682 else if (con->response->request.status.status_code == IPP_OK)
7683 job->state->values[0].integer = attr->values[0].integer;
7684 break;
7685
7686 case IPP_JOB_PROCESSING :
7687 case IPP_JOB_STOPPED :
7688 if (job->state->values[0].integer != attr->values[0].integer)
7689 {
7690 send_ipp_error(con, IPP_NOT_POSSIBLE);
7691 return;
7692 }
7693 break;
7694
7695 case IPP_JOB_CANCELLED :
7696 case IPP_JOB_ABORTED :
7697 case IPP_JOB_COMPLETED :
7698 if (job->state->values[0].integer > IPP_JOB_PROCESSING)
7699 {
7700 send_ipp_error(con, IPP_NOT_POSSIBLE);
7701 return;
7702 }
7703 else if (con->response->request.status.status_code == IPP_OK)
7704 {
7705 CancelJob(job->id, 0);
7706
7707 if (JobHistory)
7708 {
7709 job->state->values[0].integer = attr->values[0].integer;
7710 SaveJob(job->id);
7711 }
7712 }
7713 break;
7714 }
7715 }
7716 }
7717 else if (con->response->request.status.status_code != IPP_OK)
7718 continue;
503d5043 7719 else if ((attr2 = ippFindAttribute(job->attrs, attr->name, IPP_TAG_ZERO)) != NULL)
a3e17a89 7720 {
7721 /*
962e5a9f 7722 * Some other value; first free the old value...
a3e17a89 7723 */
9cbd98eb 7724
e38a7cef 7725 if (job->attrs->attrs == attr2)
50f63f23 7726 {
e38a7cef 7727 job->attrs->attrs = attr2->next;
7728 prev2 = NULL;
50f63f23 7729 }
a3e17a89 7730 else
2d5e06dd 7731 {
e38a7cef 7732 for (prev2 = job->attrs->attrs; prev2 != NULL; prev2 = prev2->next)
2d5e06dd 7733 if (prev2->next == attr2)
7734 {
7735 prev2->next = attr2->next;
7736 break;
7737 }
7738 }
7739
e38a7cef 7740 if (job->attrs->last == attr2)
7741 job->attrs->last = prev2;
a3e17a89 7742
7743 _ipp_free_attr(attr2);
962e5a9f 7744
7745 /*
7746 * Then copy the attribute...
7747 */
7748
753453e4 7749 copy_attribute(job->attrs, attr, 0);
962e5a9f 7750
a3e17a89 7751 /*
503d5043 7752 * See if the job-name or job-hold-until is being changed.
a3e17a89 7753 */
9cbd98eb 7754
471f1564 7755 if (strcmp(attr->name, "job-hold-until") == 0)
56dd1f7a 7756 {
503d5043 7757 SetJobHoldUntil(job->id, attr->values[0].string.text);
56dd1f7a 7758
262ed403 7759 if (strcmp(attr->values[0].string.text, "no-hold") == 0)
7760 ReleaseJob(job->id);
56dd1f7a 7761 else
262ed403 7762 HoldJob(job->id);
56dd1f7a 7763 }
a3e17a89 7764 }
7765 else if (attr->value_tag == IPP_TAG_DELETEATTR)
7766 {
7767 /*
7768 * Delete the attribute...
7769 */
7770
7771 for (attr2 = job->attrs->attrs, prev2 = NULL;
7772 attr2 != NULL;
7773 prev2 = attr2, attr2 = attr2->next)
7774 if (attr2->name && strcmp(attr2->name, attr->name) == 0)
7775 break;
7776
7777 if (attr2)
7778 {
7779 if (prev2)
7780 prev2->next = attr2->next;
7781 else
7782 job->attrs->attrs = attr2->next;
7783
2d5e06dd 7784 if (attr2 == job->attrs->last)
7785 job->attrs->last = prev2;
7786
a3e17a89 7787 _ipp_free_attr(attr2);
7788 }
7789 }
7790 else
7791 {
7792 /*
962e5a9f 7793 * Add new option by copying it...
a3e17a89 7794 */
7795
753453e4 7796 copy_attribute(job->attrs, attr, 0);
a3e17a89 7797 }
9cbd98eb 7798 }
7799
56dd1f7a 7800 /*
7801 * Save the job...
7802 */
7803
7804 SaveJob(job->id);
7805
9cbd98eb 7806 /*
7807 * Start jobs if possible...
7808 */
7809
7810 CheckJobs();
9cbd98eb 7811}
7812
7813
f3d580b9 7814/*
7815 * 'start_printer()' - Start a printer.
7816 */
7817
7818static void
7819start_printer(client_t *con, /* I - Client connection */
7820 ipp_attribute_t *uri) /* I - Printer URI */
7821{
7822 cups_ptype_t dtype; /* Destination type (printer or class) */
7823 char method[HTTP_MAX_URI],
7824 /* Method portion of URI */
7825 username[HTTP_MAX_URI],
7826 /* Username portion of URI */
7827 host[HTTP_MAX_URI],
7828 /* Host portion of URI */
7829 resource[HTTP_MAX_URI];
7830 /* Resource portion of URI */
7831 int port; /* Port portion of URI */
1049abbe 7832 const char *name; /* Printer name */
f3d580b9 7833 printer_t *printer; /* Printer data */
7834
3270670b 7835
b2e10895 7836 LogMessage(L_DEBUG2, "start_printer(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 7837 uri->values[0].string.text);
f3d580b9 7838
7839 /*
7840 * Was this operation called from the correct URI?
7841 */
7842
7843 if (strncmp(con->uri, "/admin/", 7) != 0)
7844 {
5ea8888e 7845 LogMessage(L_ERROR, "start_printer: admin request on bad resource \'%s\'!",
1124e9ec 7846 con->uri);
f3d580b9 7847 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7848 return;
7849 }
7850
7851 /*
7852 * Is the destination valid?
7853 */
7854
7855 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
7856
bd5510a5 7857 if ((name = ValidateDest(host, resource, &dtype, &printer)) == NULL)
f3d580b9 7858 {
7859 /*
7860 * Bad URI...
7861 */
7862
5ea8888e 7863 LogMessage(L_ERROR, "start_printer: resource name \'%s\' no good!", resource);
f3d580b9 7864 send_ipp_error(con, IPP_NOT_FOUND);
7865 return;
7866 }
7867
e9f54388 7868 /*
7869 * Check policy...
7870 */
7871
99baf768 7872 if (!cupsdCheckPolicy(printer->op_policy_ptr, con, NULL))
e9f54388 7873 {
7874 LogMessage(L_ERROR, "start_printer: not authorized!");
7875 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7876 return;
7877 }
7878
f3d580b9 7879 /*
7880 * Start the printer...
7881 */
7882
9ae34eb7 7883 printer->state_message[0] = '\0';
7884
aa3b74e1 7885 StartPrinter(printer, 1);
cc0561c6 7886
c8a55d2c 7887 if (dtype & CUPS_PRINTER_CLASS)
5ea8888e 7888 LogMessage(L_INFO, "Class \'%s\' started by \'%s\'.", name,
cc0561c6 7889 con->username);
5ea8888e 7890 LogMessage(L_INFO, "Printer \'%s\' started by \'%s\'.", name,
cc0561c6 7891 con->username);
7892
432073bb 7893 CheckJobs();
7894
f3d580b9 7895 /*
7896 * Everything was ok, so return OK status...
7897 */
7898
0a3ac972 7899 con->response->request.status.status_code = IPP_OK;
f3d580b9 7900}
7901
7902
7903/*
7904 * 'stop_printer()' - Stop a printer.
7905 */
7906
7907static void
7908stop_printer(client_t *con, /* I - Client connection */
7909 ipp_attribute_t *uri) /* I - Printer URI */
7910{
7911 cups_ptype_t dtype; /* Destination type (printer or class) */
7912 char method[HTTP_MAX_URI],
7913 /* Method portion of URI */
7914 username[HTTP_MAX_URI],
7915 /* Username portion of URI */
7916 host[HTTP_MAX_URI],
7917 /* Host portion of URI */
7918 resource[HTTP_MAX_URI];
7919 /* Resource portion of URI */
7920 int port; /* Port portion of URI */
1049abbe 7921 const char *name; /* Printer name */
f3d580b9 7922 printer_t *printer; /* Printer data */
7923 ipp_attribute_t *attr; /* printer-state-message attribute */
7924
7925
b2e10895 7926 LogMessage(L_DEBUG2, "stop_printer(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 7927 uri->values[0].string.text);
f3d580b9 7928
7929 /*
7930 * Was this operation called from the correct URI?
7931 */
7932
7933 if (strncmp(con->uri, "/admin/", 7) != 0)
7934 {
5ea8888e 7935 LogMessage(L_ERROR, "stop_printer: admin request on bad resource \'%s\'!",
1124e9ec 7936 con->uri);
f3d580b9 7937 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7938 return;
7939 }
7940
7941 /*
7942 * Is the destination valid?
7943 */
7944
7945 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
7946
bd5510a5 7947 if ((name = ValidateDest(host, resource, &dtype, &printer)) == NULL)
f3d580b9 7948 {
7949 /*
7950 * Bad URI...
7951 */
7952
5ea8888e 7953 LogMessage(L_ERROR, "stop_printer: resource name \'%s\' no good!", resource);
f3d580b9 7954 send_ipp_error(con, IPP_NOT_FOUND);
7955 return;
7956 }
7957
e9f54388 7958 /*
7959 * Check policy...
7960 */
7961
99baf768 7962 if (!cupsdCheckPolicy(printer->op_policy_ptr, con, NULL))
e9f54388 7963 {
7964 LogMessage(L_ERROR, "stop_printer: not authorized!");
7965 send_ipp_error(con, IPP_NOT_AUTHORIZED);
7966 return;
7967 }
7968
f3d580b9 7969 /*
7970 * Stop the printer...
7971 */
7972
f3d580b9 7973 if ((attr = ippFindAttribute(con->request, "printer-state-message",
7974 IPP_TAG_TEXT)) == NULL)
7975 strcpy(printer->state_message, "Paused");
7976 else
970017a4 7977 {
def978d5 7978 strlcpy(printer->state_message, attr->values[0].string.text,
7979 sizeof(printer->state_message));
970017a4 7980 }
f3d580b9 7981
aa3b74e1 7982 StopPrinter(printer, 1);
9ae34eb7 7983
c8a55d2c 7984 if (dtype & CUPS_PRINTER_CLASS)
5ea8888e 7985 LogMessage(L_INFO, "Class \'%s\' stopped by \'%s\'.", name,
cc0561c6 7986 con->username);
7987 else
5ea8888e 7988 LogMessage(L_INFO, "Printer \'%s\' stopped by \'%s\'.", name,
cc0561c6 7989 con->username);
7990
f3d580b9 7991 /*
7992 * Everything was ok, so return OK status...
7993 */
7994
0a3ac972 7995 con->response->request.status.status_code = IPP_OK;
f3d580b9 7996}
7997
7998
bf9da908 7999/*
8000 * 'user_allowed()' - See if a user is allowed to print to a queue.
8001 */
8002
8003static int /* O - 0 if not allowed, 1 if allowed */
8004user_allowed(printer_t *p, /* I - Printer or class */
8005 const char *username) /* I - Username */
8006{
9d0c9f28 8007 int i; /* Looping var */
bf9da908 8008 struct passwd *pw; /* User password data */
bf9da908 8009
8010
8011 if (p->num_users == 0)
8012 return (1);
8013
8014 if (!strcmp(username, "root"))
8015 return (1);
8016
8017 pw = getpwnam(username);
8018 endpwent();
8019
8020 for (i = 0; i < p->num_users; i ++)
8021 {
8022 if (p->users[i][0] == '@')
8023 {
8024 /*
8025 * Check group membership...
8026 */
8027
9d0c9f28 8028 if (cupsdCheckGroup(username, pw, p->users[i] + 1))
8029 break;
bf9da908 8030 }
8031 else if (!strcasecmp(username, p->users[i]))
8032 break;
8033 }
8034
8035 return ((i < p->num_users) != p->deny_users);
8036}
8037
8038
1d2c70a6 8039/*
8040 * 'validate_job()' - Validate printer options and destination.
8041 */
8042
e31bfb6e 8043static void
1d2c70a6 8044validate_job(client_t *con, /* I - Client connection */
8045 ipp_attribute_t *uri) /* I - Printer URI */
e31bfb6e 8046{
1d2c70a6 8047 ipp_attribute_t *attr; /* Current attribute */
8048 ipp_attribute_t *format; /* Document-format attribute */
8049 cups_ptype_t dtype; /* Destination type (printer or class) */
8050 char method[HTTP_MAX_URI],
8051 /* Method portion of URI */
8052 username[HTTP_MAX_URI],
8053 /* Username portion of URI */
8054 host[HTTP_MAX_URI],
8055 /* Host portion of URI */
8056 resource[HTTP_MAX_URI];
8057 /* Resource portion of URI */
8058 int port; /* Port portion of URI */
8059 char super[MIME_MAX_SUPER],
8060 /* Supertype of file */
8061 type[MIME_MAX_TYPE];
8062 /* Subtype of file */
bd5510a5 8063 printer_t *printer; /* Printer */
1d2c70a6 8064
8065
b2e10895 8066 LogMessage(L_DEBUG2, "validate_job(%p[%d], %s)\n", con, con->http.fd,
dbb05cda 8067 uri->values[0].string.text);
1d2c70a6 8068
2aeb2b1d 8069 /*
8070 * Verify that the POST operation was done to a valid URI.
8071 */
8072
8073 if (strncmp(con->uri, "/classes/", 9) != 0 &&
8074 strncmp(con->uri, "/printers/", 10) != 0)
8075 {
5ea8888e 8076 LogMessage(L_ERROR, "validate_job: request on bad resource \'%s\'!",
1124e9ec 8077 con->uri);
2aeb2b1d 8078 send_ipp_error(con, IPP_NOT_AUTHORIZED);
8079 return;
8080 }
8081
1d2c70a6 8082 /*
8083 * OK, see if the client is sending the document compressed - CUPS
8084 * doesn't support compression yet...
8085 */
8086
7ebf3a09 8087 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL &&
8088 strcmp(attr->values[0].string.text, "none") == 0)
1d2c70a6 8089 {
7ebf3a09 8090 LogMessage(L_ERROR, "validate_job: Unsupported compression attribute %s!",
8091 attr->values[0].string.text);
1d2c70a6 8092 send_ipp_error(con, IPP_ATTRIBUTES);
bd84e0d1 8093 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
8094 "compression", NULL, attr->values[0].string.text);
1d2c70a6 8095 return;
8096 }
8097
8098 /*
8099 * Is it a format we support?
8100 */
8101
17b95e13 8102 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
1d2c70a6 8103 {
17b95e13 8104 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
8105 {
8106 LogMessage(L_ERROR, "validate_job: could not scan type \'%s\'!\n",
8107 format->values[0].string.text);
8108 send_ipp_error(con, IPP_BAD_REQUEST);
8109 return;
8110 }
1d2c70a6 8111
17b95e13 8112 if ((strcmp(super, "application") != 0 ||
8113 strcmp(type, "octet-stream") != 0) &&
8114 mimeType(MimeDatabase, super, type) == NULL)
8115 {
8116 LogMessage(L_ERROR, "validate_job: Unsupported format \'%s\'!\n",
8117 format->values[0].string.text);
deb855a2 8118 LogMessage(L_INFO, "Hint: Do you have the raw file printing rules enabled?");
17b95e13 8119 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
8120 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
8121 "document-format", NULL, format->values[0].string.text);
8122 return;
8123 }
1d2c70a6 8124 }
8125
8126 /*
8127 * Is the destination valid?
8128 */
8129
c0341b41 8130 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1d2c70a6 8131
bd5510a5 8132 if (ValidateDest(host, resource, &dtype, &printer) == NULL)
1d2c70a6 8133 {
8134 /*
8135 * Bad URI...
8136 */
8137
5ea8888e 8138 LogMessage(L_ERROR, "validate_job: resource name \'%s\' no good!", resource);
1d2c70a6 8139 send_ipp_error(con, IPP_NOT_FOUND);
8140 return;
8141 }
8142
e9f54388 8143 /*
8144 * Check policy...
8145 */
8146
99baf768 8147 if (!cupsdCheckPolicy(printer->op_policy_ptr, con, NULL))
e9f54388 8148 {
8149 LogMessage(L_ERROR, "validate_job: not authorized!");
8150 send_ipp_error(con, IPP_NOT_AUTHORIZED);
8151 return;
8152 }
8153
1d2c70a6 8154 /*
8155 * Everything was ok, so return OK status...
8156 */
8157
0a3ac972 8158 con->response->request.status.status_code = IPP_OK;
e31bfb6e 8159}
8160
8161
a3901bc4 8162/*
8163 * 'validate_name()' - Make sure the printer name only contains valid chars.
8164 */
8165
8166static int /* O - 0 if name is no good, 1 if name is good */
8167validate_name(const char *name) /* I - Name to check */
8168{
8169 const char *ptr; /* Pointer into name */
8170
8171
8172 /*
8173 * Scan the whole name...
8174 */
8175
8176 for (ptr = name; *ptr; ptr ++)
8177 if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
8178 return (0);
8179
8180 /*
8181 * All the characters are good; validate the length, too...
8182 */
8183
8184 return ((ptr - name) < 128);
8185}
8186
8187
e31bfb6e 8188/*
ed3e11d8 8189 * 'validate_user()' - Validate the user for the request.
8190 */
8191
8192static int /* O - 1 if permitted, 0 otherwise */
bd5510a5 8193validate_user(job_t *job, /* I - Job */
8194 client_t *con, /* I - Client connection */
ed3e11d8 8195 const char *owner, /* I - Owner of job/resource */
8196 char *username, /* O - Authenticated username */
8197 int userlen) /* I - Length of username */
8198{
ed3e11d8 8199 ipp_attribute_t *attr; /* requesting-user-name attribute */
bd5510a5 8200 printer_t *printer; /* Printer for job */
ed3e11d8 8201
8202
e6df8a90 8203 LogMessage(L_DEBUG2, "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, userlen=%d)\n",
8204 job ? job->id : 0, con->http.fd, owner ? owner : "(null)",
8205 username, userlen);
dbb05cda 8206
d588a92e 8207 /*
8208 * Validate input...
8209 */
8210
e6df8a90 8211 if (!con || !owner || !username || userlen <= 0)
d588a92e 8212 return (0);
8213
ed3e11d8 8214 /*
8215 * Get the best authenticated username that is available.
8216 */
8217
8218 if (con->username[0])
def978d5 8219 strlcpy(username, con->username, userlen);
ed3e11d8 8220 else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
def978d5 8221 strlcpy(username, attr->values[0].string.text, userlen);
ed3e11d8 8222 else
def978d5 8223 strlcpy(username, "anonymous", userlen);
ed3e11d8 8224
8225 /*
8226 * Check the username against the owner...
8227 */
8228
bd5510a5 8229 if (job->dtype & CUPS_PRINTER_CLASS)
8230 printer = FindClass(job->dest);
8231 else
8232 printer = FindPrinter(job->dest);
ed3e11d8 8233
bd5510a5 8234 if (printer)
99baf768 8235 return (cupsdCheckPolicy(printer->op_policy_ptr, con, owner));
bd5510a5 8236 else
99baf768 8237 return (cupsdCheckPolicy(DefaultPolicyPtr, con, owner));
ed3e11d8 8238}
8239
8240
8241/*
b2e10895 8242 * End of "$Id$".
e31bfb6e 8243 */