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