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