]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/ipp.c
Merge changes from 1.1.x into 1.2 devel.
[thirdparty/cups.git] / scheduler / ipp.c
CommitLineData
e31bfb6e 1/*
753453e4 2 * "$Id: ipp.c,v 1.127.2.3 2001/12/26 16:52:53 mike Exp $"
e31bfb6e 3 *
4 * IPP routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
d2935a0f 6 * Copyright 1997-2001 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
e31bfb6e 18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
7ebf3a09 26 * ProcessIPPRequest() - Process an incoming IPP request...
27 * accept_jobs() - Accept print jobs to a printer.
28 * add_class() - Add a class to the system.
a3e17a89 29 * add_file() - Add a file to a job.
7ebf3a09 30 * add_job_state_reasons() - Add the "job-state-reasons" attribute based
31 * upon the job and printer state...
32 * add_printer() - Add a printer to the system.
33 * add_printer_state_reasons() - Add the "printer-state-reasons" attribute
34 * based upon the printer state...
35 * add_queued_job_count() - Add the "queued-job-count" attribute for
36 * cancel_all_jobs() - Cancel all print jobs.
37 * cancel_job() - Cancel a print job.
b521f3fc 38 * check_quotas() - Check quotas for a printer and user.
962e5a9f 39 * copy_attribute() - Copy a single attribute.
7ebf3a09 40 * copy_attrs() - Copy attributes from one request to another.
41 * create_job() - Print a file to a printer or class.
e903a8f6 42 * copy_banner() - Copy a banner file to the requests directory
43 * for the specified job.
7ebf3a09 44 * copy_file() - Copy a PPD file or interface script...
45 * delete_printer() - Remove a printer or class from the system.
46 * get_default() - Get the default destination.
47 * get_devices() - Get the list of available devices on the
48 * local system.
49 * get_jobs() - Get a list of jobs for the specified printer.
50 * get_job_attrs() - Get job attributes.
51 * get_ppds() - Get the list of PPD files on the local
52 * system.
53 * get_printer_attrs() - Get printer attributes.
54 * get_printers() - Get a list of printers.
55 * hold_job() - Hold a print job.
753453e4 56 * move_job() - Move a job to a new destination.
7ebf3a09 57 * print_job() - Print a file to a printer or class.
58 * reject_jobs() - Reject print jobs to a printer.
f63a2256 59 * release_job() - Release a held print job.
60 * restart_job() - Restart an old print job.
7ebf3a09 61 * send_document() - Send a file to a printer or class.
62 * send_ipp_error() - Send an error status back to the IPP client.
63 * set_default() - Set the default destination...
9cbd98eb 64 * set_job_attrs() - Set job attributes.
7ebf3a09 65 * start_printer() - Start a printer.
66 * stop_printer() - Stop a printer.
67 * validate_job() - Validate printer options and destination.
ed3e11d8 68 * validate_user() - Validate the user for the request.
e31bfb6e 69 */
70
71/*
72 * Include necessary headers...
73 */
74
75#include "cupsd.h"
6bc08d08 76#include <pwd.h>
77#include <grp.h>
d4c438d4 78#ifdef HAVE_LIBZ
79# include <zlib.h>
80#endif /* HAVE_LIBZ */
e31bfb6e 81
82
83/*
84 * Local functions...
85 */
86
f3d580b9 87static void accept_jobs(client_t *con, ipp_attribute_t *uri);
c7fa9d06 88static void add_class(client_t *con, ipp_attribute_t *uri);
a3e17a89 89static int add_file(client_t *con, job_t *job, mime_type_t *filetype);
7ebf3a09 90static void add_job_state_reasons(client_t *con, job_t *job);
c7fa9d06 91static void add_printer(client_t *con, ipp_attribute_t *uri);
7ebf3a09 92static void add_printer_state_reasons(client_t *con, printer_t *p);
93static void add_queued_job_count(client_t *con, printer_t *p);
f3d580b9 94static void cancel_all_jobs(client_t *con, ipp_attribute_t *uri);
1d2c70a6 95static void cancel_job(client_t *con, ipp_attribute_t *uri);
b521f3fc 96static int check_quotas(client_t *con, printer_t *p);
753453e4 97static void copy_attribute(ipp_t *to, ipp_attribute_t *attr,
98 int quickcopy);
4979ce3d 99static void copy_attrs(ipp_t *to, ipp_t *from, ipp_attribute_t *req,
100 ipp_tag_t group);
b521f3fc 101static int copy_banner(client_t *con, job_t *job, const char *name);
5d99df62 102static int copy_file(const char *from, const char *to);
4979ce3d 103static void create_job(client_t *con, ipp_attribute_t *uri);
c7fa9d06 104static void delete_printer(client_t *con, ipp_attribute_t *uri);
1d2c70a6 105static void get_default(client_t *con);
bd84e0d1 106static void get_devices(client_t *con);
1d2c70a6 107static void get_jobs(client_t *con, ipp_attribute_t *uri);
108static void get_job_attrs(client_t *con, ipp_attribute_t *uri);
bd84e0d1 109static void get_ppds(client_t *con);
cbbfcc63 110static void get_printers(client_t *con, int type);
1d2c70a6 111static void get_printer_attrs(client_t *con, ipp_attribute_t *uri);
bd84e0d1 112static void hold_job(client_t *con, ipp_attribute_t *uri);
a3e17a89 113static void move_job(client_t *con, ipp_attribute_t *uri);
1d2c70a6 114static void print_job(client_t *con, ipp_attribute_t *uri);
f3d580b9 115static void reject_jobs(client_t *con, ipp_attribute_t *uri);
f63a2256 116static void release_job(client_t *con, ipp_attribute_t *uri);
bd84e0d1 117static void restart_job(client_t *con, ipp_attribute_t *uri);
118static void send_document(client_t *con, ipp_attribute_t *uri);
e31bfb6e 119static void send_ipp_error(client_t *con, ipp_status_t status);
3270670b 120static void set_default(client_t *con, ipp_attribute_t *uri);
9cbd98eb 121static void set_job_attrs(client_t *con, ipp_attribute_t *uri);
f3d580b9 122static void start_printer(client_t *con, ipp_attribute_t *uri);
123static void stop_printer(client_t *con, ipp_attribute_t *uri);
1d2c70a6 124static void validate_job(client_t *con, ipp_attribute_t *uri);
ed3e11d8 125static int validate_user(client_t *con, const char *owner, char *username,
126 int userlen);
e31bfb6e 127
128
129/*
130 * 'ProcessIPPRequest()' - Process an incoming IPP request...
131 */
132
133void
134ProcessIPPRequest(client_t *con) /* I - Client connection */
135{
136 ipp_tag_t group; /* Current group tag */
137 ipp_attribute_t *attr; /* Current attribute */
138 ipp_attribute_t *charset; /* Character set attribute */
139 ipp_attribute_t *language; /* Language attribute */
140 ipp_attribute_t *uri; /* Printer URI attribute */
c8f336b5 141 ipp_attribute_t *username; /* requesting-user-name attr */
e31bfb6e 142
143
144 DEBUG_printf(("ProcessIPPRequest(%08x)\n", con));
145 DEBUG_printf(("ProcessIPPRequest: operation_id = %04x\n",
146 con->request->request.op.operation_id));
147
148 /*
149 * First build an empty response message for this request...
150 */
151
152 con->response = ippNew();
153
7ebf3a09 154 con->response->request.status.version[0] = con->request->request.op.version[0];
155 con->response->request.status.version[1] = con->request->request.op.version[1];
e31bfb6e 156 con->response->request.status.request_id = con->request->request.op.request_id;
157
158 /*
159 * Then validate the request header and required attributes...
160 */
161
162 if (con->request->request.any.version[0] != 1)
163 {
164 /*
165 * Return an error, since we only support IPP 1.x.
166 */
167
1e437ea6 168 LogMessage(L_ERROR, "ProcessIPPRequest: bad request version (%d.%d)!",
169 con->request->request.any.version[0],
170 con->request->request.any.version[1]);
dd41ddbb 171
e31bfb6e 172 send_ipp_error(con, IPP_VERSION_NOT_SUPPORTED);
e31bfb6e 173 }
1e437ea6 174 else if (con->request->attrs == NULL)
175 {
176 LogMessage(L_ERROR, "ProcessIPPRequest: no attributes in request!");
177 send_ipp_error(con, IPP_BAD_REQUEST);
178 }
e31bfb6e 179 else
e31bfb6e 180 {
181 /*
27b6a264 182 * Make sure that the attributes are provided in the correct order and
183 * don't repeat groups...
e31bfb6e 184 */
185
27b6a264 186 for (attr = con->request->attrs, group = attr->group_tag;
187 attr != NULL;
188 attr = attr->next)
189 if (attr->group_tag < group)
190 {
191 /*
192 * Out of order; return an error...
193 */
194
5ea8888e 195 LogMessage(L_ERROR, "ProcessIPPRequest: attribute groups are out of order!");
27b6a264 196 send_ipp_error(con, IPP_BAD_REQUEST);
f3d580b9 197 break;
27b6a264 198 }
199 else
200 group = attr->group_tag;
f3d580b9 201
27b6a264 202 if (attr == NULL)
203 {
204 /*
205 * Then make sure that the first three attributes are:
206 *
207 * attributes-charset
208 * attributes-natural-language
f63a2256 209 * printer-uri/job-uri
27b6a264 210 */
e31bfb6e 211
27b6a264 212 attr = con->request->attrs;
213 if (attr != NULL && strcmp(attr->name, "attributes-charset") == 0 &&
214 attr->value_tag == IPP_TAG_CHARSET)
215 charset = attr;
216 else
217 charset = NULL;
218
17b95e13 219 if (attr)
220 attr = attr->next;
27b6a264 221 if (attr != NULL && strcmp(attr->name, "attributes-natural-language") == 0 &&
222 attr->value_tag == IPP_TAG_LANGUAGE)
223 language = attr;
224 else
225 language = NULL;
226
17b95e13 227 if ((attr = ippFindAttribute(con->request, "printer-uri", IPP_TAG_URI)) != NULL)
27b6a264 228 uri = attr;
17b95e13 229 else if ((attr = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) != NULL)
27b6a264 230 uri = attr;
231 else
232 uri = NULL;
233
17b95e13 234 if (charset)
235 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
236 "attributes-charset", NULL, charset->values[0].string.text);
237 else
238 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
239 "attributes-charset", NULL, DefaultCharset);
b9e6b836 240
17b95e13 241 if (language)
242 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
243 "attributes-natural-language", NULL,
244 language->values[0].string.text);
245 else
246 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
247 "attributes-natural-language", NULL, DefaultLanguage);
b9e6b836 248
27b6a264 249 if (charset == NULL || language == NULL ||
a148912d 250 (uri == NULL &&
251 con->request->request.op.operation_id != CUPS_GET_DEFAULT &&
252 con->request->request.op.operation_id != CUPS_GET_PRINTERS &&
253 con->request->request.op.operation_id != CUPS_GET_CLASSES &&
254 con->request->request.op.operation_id != CUPS_GET_DEVICES &&
255 con->request->request.op.operation_id != CUPS_GET_PPDS))
27b6a264 256 {
257 /*
258 * Return an error, since attributes-charset,
259 * attributes-natural-language, and printer-uri/job-uri are required
260 * for all operations.
261 */
262
bd84e0d1 263 if (charset == NULL)
5ea8888e 264 LogMessage(L_ERROR, "ProcessIPPRequest: missing attributes-charset attribute!");
bd84e0d1 265
266 if (language == NULL)
5ea8888e 267 LogMessage(L_ERROR, "ProcessIPPRequest: missing attributes-natural-language attribute!");
bd84e0d1 268
269 if (uri == NULL)
5ea8888e 270 LogMessage(L_ERROR, "ProcessIPPRequest: missing printer-uri or job-uri attribute!");
bd84e0d1 271
27b6a264 272 send_ipp_error(con, IPP_BAD_REQUEST);
273 }
274 else
275 {
27b6a264 276 /*
c8f336b5 277 * OK, all the checks pass so far; make sure requesting-user-name is
278 * not "root" from a remote host...
279 */
280
e9b863f7 281 if ((username = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
c8f336b5 282 {
283 /*
284 * Check for root user...
285 */
286
287 if (strcmp(username->values[0].string.text, "root") == 0 &&
99de6da0 288 strcasecmp(con->http.hostname, "localhost") != 0 &&
c8f336b5 289 strcmp(con->username, "root") != 0)
290 {
291 /*
292 * Remote unauthenticated user masquerading as local root...
293 */
294
295 free(username->values[0].string.text);
d74efa25 296 username->values[0].string.text = strdup(RemoteRoot);
c8f336b5 297 }
298 }
299
300 /*
301 * Then try processing the operation...
27b6a264 302 */
303
304 switch (con->request->request.op.operation_id)
305 {
306 case IPP_PRINT_JOB :
307 print_job(con, uri);
308 break;
309
310 case IPP_VALIDATE_JOB :
311 validate_job(con, uri);
312 break;
313
bd84e0d1 314 case IPP_CREATE_JOB :
315 create_job(con, uri);
316 break;
317
318 case IPP_SEND_DOCUMENT :
319 send_document(con, uri);
320 break;
321
27b6a264 322 case IPP_CANCEL_JOB :
323 cancel_job(con, uri);
324 break;
325
326 case IPP_GET_JOB_ATTRIBUTES :
327 get_job_attrs(con, uri);
328 break;
329
330 case IPP_GET_JOBS :
331 get_jobs(con, uri);
332 break;
333
334 case IPP_GET_PRINTER_ATTRIBUTES :
335 get_printer_attrs(con, uri);
336 break;
337
bd84e0d1 338 case IPP_HOLD_JOB :
339 hold_job(con, uri);
340 break;
341
f63a2256 342 case IPP_RELEASE_JOB :
343 release_job(con, uri);
344 break;
345
bd84e0d1 346 case IPP_RESTART_JOB :
347 restart_job(con, uri);
348 break;
349
27b6a264 350 case IPP_PAUSE_PRINTER :
351 stop_printer(con, uri);
352 break;
353
354 case IPP_RESUME_PRINTER :
355 start_printer(con, uri);
356 break;
357
358 case IPP_PURGE_JOBS :
359 cancel_all_jobs(con, uri);
360 break;
361
9cbd98eb 362 case IPP_SET_JOB_ATTRIBUTES :
363 set_job_attrs(con, uri);
364 break;
365
27b6a264 366 case CUPS_GET_DEFAULT :
367 get_default(con);
368 break;
369
370 case CUPS_GET_PRINTERS :
cbbfcc63 371 get_printers(con, 0);
372 break;
373
374 case CUPS_GET_CLASSES :
375 get_printers(con, CUPS_PRINTER_CLASS);
27b6a264 376 break;
e31bfb6e 377
27b6a264 378 case CUPS_ADD_PRINTER :
c7fa9d06 379 add_printer(con, uri);
27b6a264 380 break;
e31bfb6e 381
27b6a264 382 case CUPS_DELETE_PRINTER :
c7fa9d06 383 delete_printer(con, uri);
27b6a264 384 break;
e31bfb6e 385
27b6a264 386 case CUPS_ADD_CLASS :
c7fa9d06 387 add_class(con, uri);
27b6a264 388 break;
e31bfb6e 389
27b6a264 390 case CUPS_DELETE_CLASS :
c7fa9d06 391 delete_printer(con, uri);
27b6a264 392 break;
e31bfb6e 393
27b6a264 394 case CUPS_ACCEPT_JOBS :
b5cb0608 395 case IPP_ENABLE_PRINTER :
27b6a264 396 accept_jobs(con, uri);
397 break;
f3d580b9 398
27b6a264 399 case CUPS_REJECT_JOBS :
b5cb0608 400 case IPP_DISABLE_PRINTER :
27b6a264 401 reject_jobs(con, uri);
402 break;
f3d580b9 403
3270670b 404 case CUPS_SET_DEFAULT :
405 set_default(con, uri);
406 break;
407
bd84e0d1 408 case CUPS_GET_DEVICES :
409 get_devices(con);
410 break;
411
412 case CUPS_GET_PPDS :
413 get_ppds(con);
414 break;
415
a3e17a89 416 case CUPS_MOVE_JOB :
417 move_job(con, uri);
418 break;
419
27b6a264 420 default :
421 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
422 }
423 }
424 }
e31bfb6e 425 }
426
1d2c70a6 427 SendHeader(con, HTTP_OK, "application/ipp");
f937f199 428
429 con->http.data_encoding = HTTP_ENCODE_LENGTH;
430 con->http.data_remaining = ippLength(con->response);
431
432 httpPrintf(HTTP(con), "Content-Length: %d\r\n\r\n",
433 con->http.data_remaining);
1d2c70a6 434
e31bfb6e 435 FD_SET(con->http.fd, &OutputSet);
436}
437
438
f3d580b9 439/*
440 * 'accept_jobs()' - Accept print jobs to a printer.
441 */
442
443static void
444accept_jobs(client_t *con, /* I - Client connection */
445 ipp_attribute_t *uri) /* I - Printer or class URI */
446{
447 cups_ptype_t dtype; /* Destination type (printer or class) */
448 char method[HTTP_MAX_URI],
449 /* Method portion of URI */
450 username[HTTP_MAX_URI],
451 /* Username portion of URI */
452 host[HTTP_MAX_URI],
453 /* Host portion of URI */
454 resource[HTTP_MAX_URI];
455 /* Resource portion of URI */
456 int port; /* Port portion of URI */
1049abbe 457 const char *name; /* Printer name */
f3d580b9 458 printer_t *printer; /* Printer data */
459
460
dbb05cda 461 LogMessage(L_DEBUG2, "accept_jobs(%d, %s)\n", con->http.fd,
462 uri->values[0].string.text);
f3d580b9 463
464 /*
465 * Was this operation called from the correct URI?
466 */
467
468 if (strncmp(con->uri, "/admin/", 7) != 0)
469 {
5ea8888e 470 LogMessage(L_ERROR, "accept_jobs: admin request on bad resource \'%s\'!",
1124e9ec 471 con->uri);
f3d580b9 472 send_ipp_error(con, IPP_NOT_AUTHORIZED);
473 return;
474 }
475
476 /*
477 * Is the destination valid?
478 */
479
480 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
481
ed3e11d8 482 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
f3d580b9 483 {
484 /*
485 * Bad URI...
486 */
487
5ea8888e 488 LogMessage(L_ERROR, "accept_jobs: resource name \'%s\' no good!", resource);
f3d580b9 489 send_ipp_error(con, IPP_NOT_FOUND);
490 return;
491 }
492
493 /*
494 * Accept jobs sent to the printer...
495 */
496
17532ea0 497 if (dtype == CUPS_PRINTER_CLASS)
498 printer = FindClass(name);
499 else
500 printer = FindPrinter(name);
501
f3d580b9 502 printer->accepting = 1;
503 printer->state_message[0] = '\0';
504
5d99df62 505 if (dtype == CUPS_PRINTER_CLASS)
506 SaveAllClasses();
507 else
508 SaveAllPrinters();
509
5ea8888e 510 LogMessage(L_INFO, "Printer \'%s\' now accepting jobs (\'%s\').", name,
cc0561c6 511 con->username);
512
f3d580b9 513 /*
514 * Everything was ok, so return OK status...
515 */
516
517 con->response->request.status.status_code = IPP_OK;
518}
519
520
1d2c70a6 521/*
522 * 'add_class()' - Add a class to the system.
523 */
524
e31bfb6e 525static void
c7fa9d06 526add_class(client_t *con, /* I - Client connection */
527 ipp_attribute_t *uri) /* I - URI of class */
e31bfb6e 528{
3270670b 529 int i; /* Looping var */
530 char method[HTTP_MAX_URI],
531 /* Method portion of URI */
532 username[HTTP_MAX_URI],
533 /* Username portion of URI */
534 host[HTTP_MAX_URI],
535 /* Host portion of URI */
536 resource[HTTP_MAX_URI];
537 /* Resource portion of URI */
538 int port; /* Port portion of URI */
539 printer_t *pclass; /* Class */
540 cups_ptype_t dtype; /* Destination type */
1049abbe 541 const char *dest; /* Printer or class name */
3270670b 542 ipp_attribute_t *attr; /* Printer attribute */
d6f1ff9a 543 int modify; /* Non-zero if we just modified */
3270670b 544
545
dbb05cda 546 LogMessage(L_DEBUG2, "add_class(%d, %s)\n", con->http.fd,
547 uri->values[0].string.text);
548
f3d580b9 549 /*
550 * Was this operation called from the correct URI?
551 */
552
553 if (strncmp(con->uri, "/admin/", 7) != 0)
554 {
5ea8888e 555 LogMessage(L_ERROR, "add_class: admin request on bad resource \'%s\'!",
1124e9ec 556 con->uri);
f3d580b9 557 send_ipp_error(con, IPP_NOT_AUTHORIZED);
558 return;
559 }
560
3270670b 561 DEBUG_printf(("add_class(%08x, %08x)\n", con, uri));
562
563 /*
564 * Do we have a valid URI?
565 */
566
567 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
568
c305e5e4 569 if (strncmp(resource, "/classes/", 9) != 0 || strlen(resource) == 9)
3270670b 570 {
571 /*
572 * No, return an error...
573 */
574
575 send_ipp_error(con, IPP_BAD_REQUEST);
576 return;
577 }
578
579 /*
580 * See if the class already exists; if not, create a new class...
581 */
582
583 if ((pclass = FindClass(resource + 9)) == NULL)
4621b86f 584 {
585 /*
586 * Class doesn't exist; see if we have a printer of the same name...
587 */
588
e29fa28a 589 if ((pclass = FindPrinter(resource + 9)) != NULL &&
590 !(pclass->type & CUPS_PRINTER_REMOTE))
4621b86f 591 {
592 /*
593 * Yes, return an error...
594 */
595
596 send_ipp_error(con, IPP_NOT_POSSIBLE);
597 return;
598 }
4621b86f 599
e29fa28a 600 /*
601 * No, add the pclass...
602 */
603
604 pclass = AddClass(resource + 9);
d6f1ff9a 605 modify = 0;
4621b86f 606 }
d6f1ff9a 607 else if (pclass->type & CUPS_PRINTER_IMPLICIT)
608 {
609 /*
753453e4 610 * Rename the implicit class to "AnyClass" or remove it...
d6f1ff9a 611 */
612
753453e4 613 if (ImplicitAnyClasses)
614 {
615 snprintf(pclass->name, sizeof(pclass->name), "Any%s", resource + 9);
616 SortPrinters();
617 }
618 else
619 DeletePrinter(pclass);
d6f1ff9a 620
621 /*
622 * Add the class as a new local class...
623 */
624
753453e4 625 pclass = AddClass(resource + 9);
d6f1ff9a 626 modify = 0;
627 }
628 else if (pclass->type & CUPS_PRINTER_REMOTE)
629 {
630 /*
2bdd1992 631 * Rename the remote class to "Class"...
d6f1ff9a 632 */
633
2bdd1992 634 DeletePrinterFilters(pclass);
753453e4 635 snprintf(pclass->name, sizeof(pclass->name), "%s@%s", resource + 9,
d6f1ff9a 636 pclass->hostname);
2360839b 637 SetPrinterAttrs(pclass);
d6f1ff9a 638 SortPrinters();
639
640 /*
641 * Add the class as a new local class...
642 */
643
753453e4 644 pclass = AddClass(resource + 9);
d6f1ff9a 645 modify = 0;
646 }
647 else
648 modify = 1;
3270670b 649
650 /*
651 * Look for attributes and copy them over as needed...
652 */
653
654 if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
970017a4 655 {
656 strncpy(pclass->location, attr->values[0].string.text, sizeof(pclass->location) - 1);
657 pclass->location[sizeof(pclass->location) - 1] = '\0';
658 }
659
3270670b 660 if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
970017a4 661 {
662 strncpy(pclass->info, attr->values[0].string.text, sizeof(pclass->info) - 1);
663 pclass->info[sizeof(pclass->info) - 1] = '\0';
664 }
665
4621b86f 666 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
667 {
5ea8888e 668 LogMessage(L_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
4621b86f 669 pclass->name, attr->values[0].boolean, pclass->accepting);
670
671 pclass->accepting = attr->values[0].boolean;
672 }
673 if ((attr = ippFindAttribute(con->request, "printer-state", IPP_TAG_ENUM)) != NULL)
674 {
5ea8888e 675 LogMessage(L_INFO, "Setting %s printer-state to %d (was %d.)", pclass->name,
4621b86f 676 attr->values[0].integer, pclass->state);
677
678 if (pclass->state == IPP_PRINTER_STOPPED &&
679 attr->values[0].integer != IPP_PRINTER_STOPPED)
680 pclass->state = IPP_PRINTER_IDLE;
681 else if (pclass->state != IPP_PRINTER_STOPPED &&
682 attr->values[0].integer == IPP_PRINTER_STOPPED)
683 {
684 if (pclass->state == IPP_PRINTER_PROCESSING)
685 StopJob(((job_t *)pclass->job)->id);
686
687 pclass->state = IPP_PRINTER_STOPPED;
688 }
689
690 pclass->browse_time = 0;
691 }
692 if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL)
693 {
694 strncpy(pclass->state_message, attr->values[0].string.text,
695 sizeof(pclass->state_message) - 1);
696 pclass->state_message[sizeof(pclass->state_message) - 1] = '\0';
697 }
753453e4 698 if ((attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_ZERO)) != NULL &&
699 !Classification[0])
a3e17a89 700 {
701 strncpy(pclass->job_sheets[0], attr->values[0].string.text,
702 sizeof(pclass->job_sheets[0]) - 1);
703 if (attr->num_values > 1)
704 strncpy(pclass->job_sheets[1], attr->values[1].string.text,
705 sizeof(pclass->job_sheets[1]) - 1);
706 else
707 strcpy(pclass->job_sheets[1], "none");
708 }
04d756fc 709 if ((attr = ippFindAttribute(con->request, "requesting-user-name-allowed",
710 IPP_TAG_ZERO)) != NULL)
711 {
712 FreePrinterUsers(pclass);
713
714 pclass->deny_users = 0;
01a426e9 715 if (attr->value_tag == IPP_TAG_NAME &&
716 (attr->num_values > 1 ||
717 strcmp(attr->values[0].string.text, "all") != 0))
04d756fc 718 for (i = 0; i < attr->num_values; i ++)
719 AddPrinterUser(pclass, attr->values[i].string.text);
720 }
721 else if ((attr = ippFindAttribute(con->request, "requesting-user-name-denied",
722 IPP_TAG_ZERO)) != NULL)
723 {
724 FreePrinterUsers(pclass);
725
726 pclass->deny_users = 1;
01a426e9 727 if (attr->value_tag == IPP_TAG_NAME &&
728 (attr->num_values > 1 ||
729 strcmp(attr->values[0].string.text, "none") != 0))
04d756fc 730 for (i = 0; i < attr->num_values; i ++)
731 AddPrinterUser(pclass, attr->values[i].string.text);
732 }
733 if ((attr = ippFindAttribute(con->request, "job-quota-period",
734 IPP_TAG_INTEGER)) != NULL)
735 {
736 FreeQuotas(pclass);
737 pclass->quota_period = attr->values[0].integer;
738 }
739 if ((attr = ippFindAttribute(con->request, "job-k-limit",
740 IPP_TAG_INTEGER)) != NULL)
741 {
742 FreeQuotas(pclass);
743 pclass->k_limit = attr->values[0].integer;
744 }
745 if ((attr = ippFindAttribute(con->request, "job-page-limit",
746 IPP_TAG_INTEGER)) != NULL)
747 {
748 FreeQuotas(pclass);
749 pclass->page_limit = attr->values[0].integer;
750 }
3270670b 751
752 if ((attr = ippFindAttribute(con->request, "member-uris", IPP_TAG_URI)) != NULL)
753 {
754 /*
755 * Clear the printer array as needed...
756 */
757
758 if (pclass->num_printers > 0)
759 {
760 free(pclass->printers);
761 pclass->num_printers = 0;
762 }
763
764 /*
765 * Add each printer or class that is listed...
766 */
767
768 for (i = 0; i < attr->num_values; i ++)
769 {
770 /*
771 * Search for the printer or class URI...
772 */
773
774 httpSeparate(attr->values[i].string.text, method, username, host,
775 &port, resource);
776
ed3e11d8 777 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
3270670b 778 {
779 /*
780 * Bad URI...
781 */
782
5ea8888e 783 LogMessage(L_ERROR, "add_class: resource name \'%s\' no good!", resource);
3270670b 784 send_ipp_error(con, IPP_NOT_FOUND);
785 return;
786 }
787
788 /*
789 * Add it to the class...
790 */
791
792 if (dtype == CUPS_PRINTER_CLASS)
793 AddPrinterToClass(pclass, FindClass(dest));
794 else
795 AddPrinterToClass(pclass, FindPrinter(dest));
796 }
797 }
798
3270670b 799 /*
800 * Update the printer class attributes and return...
801 */
802
803 SetPrinterAttrs(pclass);
cc0561c6 804 SaveAllClasses();
47afc1d2 805 CheckJobs();
cc0561c6 806
d6f1ff9a 807 if (modify)
808 LogMessage(L_INFO, "Class \'%s\' modified by \'%s\'.", pclass->name,
809 con->username);
810 else
811 LogMessage(L_INFO, "New class \'%s\' added by \'%s\'.", pclass->name,
812 con->username);
3270670b 813
814 con->response->request.status.status_code = IPP_OK;
e31bfb6e 815}
816
817
a3e17a89 818/*
819 * 'add_file()' - Add a file to a job.
820 */
821
b521f3fc 822static int /* O - 0 on success, -1 on error */
a3e17a89 823add_file(client_t *con, /* I - Connection to client */
824 job_t *job, /* I - Job to add to */
825 mime_type_t *filetype) /* I - Type of file */
826{
827 mime_type_t **filetypes; /* New filetypes array... */
828
829
dbb05cda 830 LogMessage(L_DEBUG2, "add_file(%d, %d, %s/%s)\n", con->http.fd,
831 job->id, filetype->super, filetype->type);
832
a3e17a89 833 /*
834 * Add the file to the job...
835 */
836
837 if (job->num_files == 0)
838 filetypes = (mime_type_t **)malloc(sizeof(mime_type_t *));
839 else
840 filetypes = (mime_type_t **)realloc(job->filetypes,
841 (job->num_files + 1) *
842 sizeof(mime_type_t));
843
844 if (filetypes == NULL)
845 {
d65c1374 846 CancelJob(job->id, 1);
a3e17a89 847 LogMessage(L_ERROR, "add_file: unable to allocate memory for file types!");
848 send_ipp_error(con, IPP_INTERNAL_ERROR);
849 return (-1);
850 }
851
852 job->filetypes = filetypes;
853 job->filetypes[job->num_files] = filetype;
854
855 job->num_files ++;
856
857 return (0);
858}
859
860
7ebf3a09 861/*
862 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
863 * upon the job and printer state...
864 */
865
866static void
867add_job_state_reasons(client_t *con, /* I - Client connection */
868 job_t *job) /* I - Job info */
869{
870 printer_t *dest; /* Destination printer */
871
872
dbb05cda 873 LogMessage(L_DEBUG2, "add_job_state_reasons(%d, %d)\n", con->http.fd,
874 job->id);
875
7ebf3a09 876 switch (job->state->values[0].integer)
877 {
878 case IPP_JOB_PENDING :
879 if (job->dtype & CUPS_PRINTER_CLASS)
880 dest = FindClass(job->dest);
881 else
882 dest = FindPrinter(job->dest);
883
884 if (dest != NULL && dest->state == IPP_PRINTER_STOPPED)
885 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
886 "job-state-reasons", NULL, "printer-stopped");
887 else
888 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
889 "job-state-reasons", NULL, "none");
890 break;
891
892 case IPP_JOB_HELD :
05ca02bc 893 if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
894 ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
895 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
896 "job-state-reasons", NULL, "job-hold-until-specified");
897 else
898 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
899 "job-state-reasons", NULL, "job-incoming");
7ebf3a09 900 break;
901
902 case IPP_JOB_PROCESSING :
903 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
904 "job-state-reasons", NULL, "job-printing");
905 break;
906
907 case IPP_JOB_STOPPED :
908 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
909 "job-state-reasons", NULL, "job-stopped");
910 break;
911
912 case IPP_JOB_CANCELLED :
913 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
914 "job-state-reasons", NULL, "job-canceled-by-user");
915 break;
916
917 case IPP_JOB_ABORTED :
918 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
919 "job-state-reasons", NULL, "aborted-by-system");
920 break;
921
922 case IPP_JOB_COMPLETED :
923 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
924 "job-state-reasons", NULL, "job-completed-successfully");
925 break;
926 }
927}
928
929
1d2c70a6 930/*
931 * 'add_printer()' - Add a printer to the system.
932 */
933
e31bfb6e 934static void
3270670b 935add_printer(client_t *con, /* I - Client connection */
936 ipp_attribute_t *uri) /* I - URI of printer */
e31bfb6e 937{
04d756fc 938 int i; /* Looping var */
c7fa9d06 939 char method[HTTP_MAX_URI],
940 /* Method portion of URI */
941 username[HTTP_MAX_URI],
942 /* Username portion of URI */
943 host[HTTP_MAX_URI],
944 /* Host portion of URI */
945 resource[HTTP_MAX_URI];
946 /* Resource portion of URI */
947 int port; /* Port portion of URI */
948 printer_t *printer; /* Printer/class */
949 ipp_attribute_t *attr; /* Printer attribute */
d4c438d4 950#ifdef HAVE_LIBZ
951 gzFile fp; /* Script/PPD file */
952#else
c7fa9d06 953 FILE *fp; /* Script/PPD file */
d4c438d4 954#endif /* HAVE_LIBZ */
c7fa9d06 955 char line[1024]; /* Line from file... */
d4c438d4 956 char srcfile[1024], /* Source Script/PPD file */
957 dstfile[1024]; /* Destination Script/PPD file */
d6f1ff9a 958 int modify; /* Non-zero if we are modifying */
c7fa9d06 959
960
dbb05cda 961 LogMessage(L_DEBUG2, "add_printer(%d, %s)\n", con->http.fd,
bd917997 962 uri->values[0].string.text);
963
f3d580b9 964 /*
965 * Was this operation called from the correct URI?
966 */
967
968 if (strncmp(con->uri, "/admin/", 7) != 0)
969 {
5ea8888e 970 LogMessage(L_ERROR, "add_printer: admin request on bad resource \'%s\'!",
1124e9ec 971 con->uri);
f3d580b9 972 send_ipp_error(con, IPP_NOT_AUTHORIZED);
973 return;
974 }
975
c7fa9d06 976 /*
977 * Do we have a valid URI?
978 */
979
980 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
981
c305e5e4 982 if (strncmp(resource, "/printers/", 10) != 0 || strlen(resource) == 10)
c7fa9d06 983 {
984 /*
985 * No, return an error...
986 */
987
988 send_ipp_error(con, IPP_BAD_REQUEST);
989 return;
990 }
991
992 /*
993 * See if the printer already exists; if not, create a new printer...
994 */
995
996 if ((printer = FindPrinter(resource + 10)) == NULL)
4621b86f 997 {
998 /*
999 * Printer doesn't exist; see if we have a class of the same name...
1000 */
1001
e29fa28a 1002 if ((printer = FindClass(resource + 10)) != NULL &&
1003 !(printer->type & CUPS_PRINTER_REMOTE))
4621b86f 1004 {
1005 /*
1006 * Yes, return an error...
1007 */
1008
1009 send_ipp_error(con, IPP_NOT_POSSIBLE);
1010 return;
1011 }
4621b86f 1012
e29fa28a 1013 /*
1014 * No, add the printer...
1015 */
1016
1017 printer = AddPrinter(resource + 10);
d6f1ff9a 1018 modify = 0;
1019 }
1020 else if (printer->type & CUPS_PRINTER_IMPLICIT)
1021 {
1022 /*
753453e4 1023 * Rename the implicit printer to "AnyPrinter" or delete it...
d6f1ff9a 1024 */
1025
753453e4 1026 if (ImplicitAnyClasses)
1027 {
1028 snprintf(printer->name, sizeof(printer->name), "Any%s", resource + 10);
1029 SortPrinters();
1030 }
1031 else
1032 DeletePrinter(printer);
d6f1ff9a 1033
1034 /*
1035 * Add the printer as a new local printer...
1036 */
1037
1038 printer = AddPrinter(resource + 10);
1039 modify = 0;
1040 }
1041 else if (printer->type & CUPS_PRINTER_REMOTE)
1042 {
1043 /*
1044 * Rename the remote printer to "Printer@server"...
1045 */
1046
2bdd1992 1047 DeletePrinterFilters(printer);
04de52f8 1048 snprintf(printer->name, sizeof(printer->name), "%s@%s", resource + 10,
d6f1ff9a 1049 printer->hostname);
2360839b 1050 SetPrinterAttrs(printer);
d6f1ff9a 1051 SortPrinters();
1052
1053 /*
1054 * Add the printer as a new local printer...
1055 */
1056
1057 printer = AddPrinter(resource + 10);
1058 modify = 0;
4621b86f 1059 }
d6f1ff9a 1060 else
1061 modify = 1;
c7fa9d06 1062
1063 /*
1064 * Look for attributes and copy them over as needed...
1065 */
1066
1067 if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
970017a4 1068 {
1069 strncpy(printer->location, attr->values[0].string.text, sizeof(printer->location) - 1);
1070 printer->location[sizeof(printer->location) - 1] = '\0';
1071 }
1072
c7fa9d06 1073 if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
970017a4 1074 {
1075 strncpy(printer->info, attr->values[0].string.text, sizeof(printer->info) - 1);
1076 printer->info[sizeof(printer->info) - 1] = '\0';
1077 }
1078
c7fa9d06 1079 if ((attr = ippFindAttribute(con->request, "device-uri", IPP_TAG_URI)) != NULL)
997edb40 1080 {
753453e4 1081 ipp_attribute_t *device; /* Current device */
1082 int methodlen; /* Length of method string */
1083
1084
1085 /*
1086 * Do we have a valid device URI?
1087 */
1088
1089 httpSeparate(attr->values[0].string.text, method, username, host,
1090 &port, resource);
1091 methodlen = strlen(method);
1092
1093 if (strcmp(method, "file") != 0)
1094 {
1095 /*
1096 * See if the backend is listed as a device...
1097 */
1098
1099 for (device = ippFindAttribute(Devices, "device-uri", IPP_TAG_URI);
1100 device != NULL;
1101 device = ippFindNextAttribute(Devices, "device-uri", IPP_TAG_URI))
1102 if (strncmp(method, device->values[0].string.text, methodlen) == 0 &&
1103 (device->values[0].string.text[methodlen] == ':' ||
1104 device->values[0].string.text[methodlen] == '\0'))
1105 break;
1106
1107 if (device == NULL)
1108 {
1109 /*
1110 * Could not find device in list!
1111 */
1112
1113 LogMessage(L_ERROR, "add_printer: bad device-uri attribute \'%s\'!",
1114 attr->values[0].string.text);
1115 send_ipp_error(con, IPP_NOT_POSSIBLE);
1116 return;
1117 }
1118 }
1119
5ea8888e 1120 LogMessage(L_INFO, "Setting %s device-uri to \"%s\" (was \"%s\".)",
997edb40 1121 printer->name, attr->values[0].string.text, printer->device_uri);
1122
970017a4 1123 strncpy(printer->device_uri, attr->values[0].string.text,
1124 sizeof(printer->device_uri) - 1);
1125 printer->device_uri[sizeof(printer->device_uri) - 1] = '\0';
997edb40 1126 }
970017a4 1127
251c0599 1128 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
997edb40 1129 {
5ea8888e 1130 LogMessage(L_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
997edb40 1131 printer->name, attr->values[0].boolean, printer->accepting);
1132
251c0599 1133 printer->accepting = attr->values[0].boolean;
997edb40 1134 }
251c0599 1135 if ((attr = ippFindAttribute(con->request, "printer-state", IPP_TAG_ENUM)) != NULL)
1136 {
5ea8888e 1137 LogMessage(L_INFO, "Setting %s printer-state to %d (was %d.)", printer->name,
997edb40 1138 attr->values[0].integer, printer->state);
1139
8f137344 1140 if (printer->state == IPP_PRINTER_STOPPED &&
1141 attr->values[0].integer != IPP_PRINTER_STOPPED)
1142 printer->state = IPP_PRINTER_IDLE;
1143 else if (printer->state != IPP_PRINTER_STOPPED &&
1144 attr->values[0].integer == IPP_PRINTER_STOPPED)
1145 {
1146 if (printer->state == IPP_PRINTER_PROCESSING)
1147 StopJob(((job_t *)printer->job)->id);
1148
1149 printer->state = IPP_PRINTER_STOPPED;
1150 }
1151
251c0599 1152 printer->browse_time = 0;
1153 }
1154 if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL)
1155 {
1156 strncpy(printer->state_message, attr->values[0].string.text,
1157 sizeof(printer->state_message) - 1);
1158 printer->state_message[sizeof(printer->state_message) - 1] = '\0';
1159 }
753453e4 1160 if ((attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_ZERO)) != NULL &&
1161 !Classification[0])
a3e17a89 1162 {
1163 strncpy(printer->job_sheets[0], attr->values[0].string.text,
1164 sizeof(printer->job_sheets[0]) - 1);
1165 if (attr->num_values > 1)
1166 strncpy(printer->job_sheets[1], attr->values[1].string.text,
1167 sizeof(printer->job_sheets[1]) - 1);
1168 else
1169 strcpy(printer->job_sheets[1], "none");
1170 }
04d756fc 1171 if ((attr = ippFindAttribute(con->request, "requesting-user-name-allowed",
1172 IPP_TAG_ZERO)) != NULL)
1173 {
1174 FreePrinterUsers(printer);
1175
1176 printer->deny_users = 0;
753453e4 1177 if (attr->value_tag == IPP_TAG_NAME &&
1178 (attr->num_values > 1 ||
1179 strcmp(attr->values[0].string.text, "all") != 0))
04d756fc 1180 for (i = 0; i < attr->num_values; i ++)
1181 AddPrinterUser(printer, attr->values[i].string.text);
1182 }
1183 else if ((attr = ippFindAttribute(con->request, "requesting-user-name-denied",
1184 IPP_TAG_ZERO)) != NULL)
1185 {
1186 FreePrinterUsers(printer);
1187
1188 printer->deny_users = 1;
753453e4 1189 if (attr->value_tag == IPP_TAG_NAME &&
1190 (attr->num_values > 1 ||
1191 strcmp(attr->values[0].string.text, "none") != 0))
04d756fc 1192 for (i = 0; i < attr->num_values; i ++)
1193 AddPrinterUser(printer, attr->values[i].string.text);
1194 }
1195 if ((attr = ippFindAttribute(con->request, "job-quota-period",
1196 IPP_TAG_INTEGER)) != NULL)
1197 {
1198 FreeQuotas(printer);
1199 printer->quota_period = attr->values[0].integer;
1200 }
1201 if ((attr = ippFindAttribute(con->request, "job-k-limit",
1202 IPP_TAG_INTEGER)) != NULL)
1203 {
1204 FreeQuotas(printer);
1205 printer->k_limit = attr->values[0].integer;
1206 }
1207 if ((attr = ippFindAttribute(con->request, "job-page-limit",
1208 IPP_TAG_INTEGER)) != NULL)
1209 {
1210 FreeQuotas(printer);
1211 printer->page_limit = attr->values[0].integer;
1212 }
c7fa9d06 1213
1214 /*
1215 * See if we have all required attributes...
1216 */
1217
1218 if (printer->device_uri[0] == '\0')
251c0599 1219 strcpy(printer->device_uri, "file:/dev/null");
c7fa9d06 1220
1221 /*
1222 * See if we have an interface script or PPD file attached to the request...
1223 */
1224
d4c438d4 1225 if (con->filename[0])
a6988fb1 1226 {
1227 strncpy(srcfile, con->filename, sizeof(srcfile) - 1);
1228 srcfile[sizeof(srcfile) - 1] = '\0';
1229 }
d4c438d4 1230 else if ((attr = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME)) != NULL)
753453e4 1231 {
1232 if (strcmp(attr->values[0].string.text, "raw") == 0)
1233 strcpy(srcfile, "raw");
1234 else
1235 snprintf(srcfile, sizeof(srcfile), "%s/model/%s", DataDir,
1236 attr->values[0].string.text);
1237 }
d4c438d4 1238 else
1239 srcfile[0] = '\0';
1240
1241 LogMessage(L_DEBUG, "add_printer: srcfile = \"%s\"", srcfile);
8496650b 1242
753453e4 1243 if (strcmp(srcfile, "raw") == 0)
1244 {
1245 /*
1246 * Raw driver, remove any existing PPD or interface script files.
1247 */
1248
1249 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
1250 printer->name);
1251 unlink(dstfile);
1252
1253 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
1254 printer->name);
1255 unlink(dstfile);
1256 }
d4c438d4 1257#ifdef HAVE_LIBZ
753453e4 1258 else if (srcfile[0] && (fp = gzopen(srcfile, "rb")) != NULL)
d4c438d4 1259#else
753453e4 1260 else if (srcfile[0] && (fp = fopen(srcfile, "rb")) != NULL)
d4c438d4 1261#endif /* HAVE_LIBZ */
c7fa9d06 1262 {
1263 /*
3270670b 1264 * Yes; get the first line from it...
c7fa9d06 1265 */
1266
c7fa9d06 1267 line[0] = '\0';
d4c438d4 1268#ifdef HAVE_LIBZ
1269 gzgets(fp, line, sizeof(line));
1270 gzclose(fp);
1271#else
c7fa9d06 1272 fgets(line, sizeof(line), fp);
1273 fclose(fp);
d4c438d4 1274#endif /* HAVE_LIBZ */
c7fa9d06 1275
1276 /*
1277 * Then see what kind of file it is...
1278 */
1279
04de52f8 1280 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
d4c438d4 1281 printer->name);
c7fa9d06 1282
1283 if (strncmp(line, "*PPD-Adobe", 10) == 0)
1284 {
1285 /*
1286 * The new file is a PPD file, so remove any old interface script
1287 * that might be lying around...
1288 */
1289
d4c438d4 1290 unlink(dstfile);
c7fa9d06 1291 }
1292 else
1293 {
1294 /*
1295 * This must be an interface script, so move the file over to the
1296 * interfaces directory and make it executable...
1297 */
1298
d4c438d4 1299 if (copy_file(srcfile, dstfile))
3b41df31 1300 {
bd917997 1301 LogMessage(L_ERROR, "add_printer: Unable to copy interface script from %s to %s - %s!",
1302 srcfile, dstfile, strerror(errno));
1b918e3a 1303 send_ipp_error(con, IPP_INTERNAL_ERROR);
3b41df31 1304 return;
1305 }
1306 else
8496650b 1307 {
1308 LogMessage(L_DEBUG, "add_printer: Copied interface script successfully!");
d4c438d4 1309 chmod(dstfile, 0755);
8496650b 1310 }
c7fa9d06 1311 }
1312
04de52f8 1313 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
d4c438d4 1314 printer->name);
c7fa9d06 1315
1316 if (strncmp(line, "*PPD-Adobe", 10) == 0)
1317 {
1318 /*
1319 * The new file is a PPD file, so move the file over to the
1320 * ppd directory and make it readable by all...
1321 */
1322
d4c438d4 1323 if (copy_file(srcfile, dstfile))
3b41df31 1324 {
bd917997 1325 LogMessage(L_ERROR, "add_printer: Unable to copy PPD file from %s to %s - %s!",
1326 srcfile, dstfile, strerror(errno));
1b918e3a 1327 send_ipp_error(con, IPP_INTERNAL_ERROR);
3b41df31 1328 return;
1329 }
1330 else
8496650b 1331 {
1332 LogMessage(L_DEBUG, "add_printer: Copied PPD file successfully!");
d4c438d4 1333 chmod(dstfile, 0644);
8496650b 1334 }
c7fa9d06 1335 }
1336 else
1337 {
1338 /*
1339 * This must be an interface script, so remove any old PPD file that
1340 * may be lying around...
1341 */
1342
d4c438d4 1343 unlink(dstfile);
c7fa9d06 1344 }
1345 }
1346
4621b86f 1347 /*
1348 * Make this printer the default if there is none...
1349 */
1350
1351 if (DefaultPrinter == NULL)
1352 DefaultPrinter = printer;
1353
c7fa9d06 1354 /*
1355 * Update the printer attributes and return...
1356 */
1357
3270670b 1358 SetPrinterAttrs(printer);
cc0561c6 1359 SaveAllPrinters();
ccd04319 1360
1361 if (printer->job != NULL)
1362 {
1363 /*
1364 * Stop the current job and then restart it below...
1365 */
1366
1367 StopJob(((job_t *)printer->job)->id);
1368 }
1369
47afc1d2 1370 CheckJobs();
cc0561c6 1371
d6f1ff9a 1372 if (modify)
1373 LogMessage(L_INFO, "Printer \'%s\' modified by \'%s\'.", printer->name,
1374 con->username);
1375 else
1376 LogMessage(L_INFO, "New printer \'%s\' added by \'%s\'.", printer->name,
1377 con->username);
c7fa9d06 1378
1379 con->response->request.status.status_code = IPP_OK;
e31bfb6e 1380}
1381
1382
7ebf3a09 1383/*
1384 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
1385 * based upon the printer state...
1386 */
1387
1388static void
1389add_printer_state_reasons(client_t *con, /* I - Client connection */
1390 printer_t *p) /* I - Printer info */
1391{
dbb05cda 1392 LogMessage(L_DEBUG2, "add_printer_state_reasons(%d, %s)\n", con->http.fd,
1393 p->name);
1394
7ebf3a09 1395 switch (p->state)
1396 {
1397 case IPP_PRINTER_STOPPED :
1398 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1399 "printer-state-reasons", NULL, "paused");
1400 break;
1401
1402 default :
1403 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1404 "printer-state-reasons", NULL, "none");
1405 break;
1406 }
1407}
1408
1409
1410/*
1411 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
1412 * the specified printer or class.
1413 */
1414
1415static void
1416add_queued_job_count(client_t *con, /* I - Client connection */
1417 printer_t *p) /* I - Printer or class */
1418{
7ebf3a09 1419 int count; /* Number of jobs on destination */
1420
1421
dbb05cda 1422 LogMessage(L_DEBUG2, "add_queued_job_count(%d, %s)\n", con->http.fd,
1423 p->name);
1424
7b1b1c6e 1425 count = GetPrinterJobCount(p->name);
7ebf3a09 1426
1427 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1428 "queued-job-count", count);
1429}
1430
1431
f3d580b9 1432/*
1433 * 'cancel_all_jobs()' - Cancel all print jobs.
1434 */
1435
1436static void
1437cancel_all_jobs(client_t *con, /* I - Client connection */
1438 ipp_attribute_t *uri) /* I - Job or Printer URI */
1439{
1049abbe 1440 const char *dest; /* Destination */
f3d580b9 1441 cups_ptype_t dtype; /* Destination type */
1442 char method[HTTP_MAX_URI],
1443 /* Method portion of URI */
1444 username[HTTP_MAX_URI],
1445 /* Username portion of URI */
1446 host[HTTP_MAX_URI],
1447 /* Host portion of URI */
1448 resource[HTTP_MAX_URI];
1449 /* Resource portion of URI */
1450 int port; /* Port portion of URI */
50b9556e 1451 printer_t *printer; /* Current printer */
f3d580b9 1452
1453
dbb05cda 1454 LogMessage(L_DEBUG2, "cancel_all_jobs(%d, %s)\n", con->http.fd,
1455 uri->values[0].string.text);
f3d580b9 1456
1457 /*
1458 * Was this operation called from the correct URI?
1459 */
1460
1461 if (strncmp(con->uri, "/admin/", 7) != 0)
1462 {
5ea8888e 1463 LogMessage(L_ERROR, "cancel_all_jobs: admin request on bad resource \'%s\'!",
1124e9ec 1464 con->uri);
f3d580b9 1465 send_ipp_error(con, IPP_NOT_AUTHORIZED);
1466 return;
1467 }
1468
1469 /*
1470 * See if we have a printer URI...
1471 */
1472
1473 if (strcmp(uri->name, "printer-uri") != 0)
1474 {
5ea8888e 1475 LogMessage(L_ERROR, "cancel_all_jobs: bad %s attribute \'%s\'!",
bd84e0d1 1476 uri->name, uri->values[0].string.text);
f3d580b9 1477 send_ipp_error(con, IPP_BAD_REQUEST);
1478 return;
1479 }
1480
1481 /*
1482 * And if the destination is valid...
1483 */
1484
1485 httpSeparate(uri->values[0].string.text, method, username, host, &port,
1486 resource);
1487
ed3e11d8 1488 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
f3d580b9 1489 {
1490 /*
50b9556e 1491 * Bad URI?
f3d580b9 1492 */
1493
50b9556e 1494 if (strcmp(resource, "/printers/") != 0)
1495 {
1496 LogMessage(L_ERROR, "cancel_all_jobs: resource name \'%s\' no good!", resource);
1497 send_ipp_error(con, IPP_NOT_FOUND);
1498 return;
1499 }
f3d580b9 1500
50b9556e 1501 /*
1502 * Cancel all jobs on all printers...
1503 */
f3d580b9 1504
50b9556e 1505 for (printer = Printers; printer; printer = printer->next)
1506 {
1507 CancelJobs(printer->name);
1508 LogMessage(L_INFO, "All jobs on \'%s\' were cancelled by \'%s\'.",
1509 printer->name, con->username);
1510 }
1511 }
1512 else
1513 {
1514 /*
1515 * Cancel all of the jobs on the named printer...
1516 */
1517
1518 CancelJobs(dest);
1519 LogMessage(L_INFO, "All jobs on \'%s\' were cancelled by \'%s\'.", dest,
1520 con->username);
1521 }
f3d580b9 1522
1523 con->response->request.status.status_code = IPP_OK;
1524}
1525
1526
1d2c70a6 1527/*
1528 * 'cancel_job()' - Cancel a print job.
1529 */
1530
e31bfb6e 1531static void
1d2c70a6 1532cancel_job(client_t *con, /* I - Client connection */
1533 ipp_attribute_t *uri) /* I - Job or Printer URI */
e31bfb6e 1534{
1d2c70a6 1535 ipp_attribute_t *attr; /* Current attribute */
1536 int jobid; /* Job ID */
1537 char method[HTTP_MAX_URI],
1538 /* Method portion of URI */
1539 username[HTTP_MAX_URI],
1540 /* Username portion of URI */
1541 host[HTTP_MAX_URI],
1542 /* Host portion of URI */
1543 resource[HTTP_MAX_URI];
1544 /* Resource portion of URI */
1545 int port; /* Port portion of URI */
3b41df31 1546 job_t *job; /* Job information */
50b9556e 1547 const char *dest; /* Destination */
1548 cups_ptype_t dtype; /* Destination type (printer or class) */
1549 printer_t *printer; /* Printer data */
1d2c70a6 1550
1551
dbb05cda 1552 LogMessage(L_DEBUG2, "cancel_job(%d, %s)\n", con->http.fd,
1553 uri->values[0].string.text);
1d2c70a6 1554
2aeb2b1d 1555 /*
1556 * Verify that the POST operation was done to a valid URI.
1557 */
1558
1559 if (strncmp(con->uri, "/classes/", 9) != 0 &&
1124e9ec 1560 strncmp(con->uri, "/jobs/", 5) != 0 &&
2aeb2b1d 1561 strncmp(con->uri, "/printers/", 10) != 0)
1562 {
5ea8888e 1563 LogMessage(L_ERROR, "cancel_job: cancel request on bad resource \'%s\'!",
1124e9ec 1564 con->uri);
2aeb2b1d 1565 send_ipp_error(con, IPP_NOT_AUTHORIZED);
1566 return;
1567 }
1568
1d2c70a6 1569 /*
1570 * See if we have a job URI or a printer URI...
1571 */
1572
1573 if (strcmp(uri->name, "printer-uri") == 0)
1574 {
1575 /*
1576 * Got a printer URI; see if we also have a job-id attribute...
1577 */
1578
c0341b41 1579 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
1d2c70a6 1580 {
5ea8888e 1581 LogMessage(L_ERROR, "cancel_job: got a printer-uri attribute but no job-id!");
1d2c70a6 1582 send_ipp_error(con, IPP_BAD_REQUEST);
1583 return;
1584 }
1585
50b9556e 1586 if ((jobid = attr->values[0].integer) == 0)
1587 {
1588 /*
1589 * Find the current job on the specified printer...
1590 */
1591
1592 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1593
1594 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
1595 {
1596 /*
1597 * Bad URI...
1598 */
1599
1600 LogMessage(L_ERROR, "cancel_job: resource name \'%s\' no good!", resource);
1601 send_ipp_error(con, IPP_NOT_FOUND);
1602 return;
1603 }
1604
1605 if (dtype & CUPS_PRINTER_CLASS)
1606 printer = FindClass(dest);
1607 else
1608 printer = FindPrinter(dest);
1609
1610 /*
1611 * See if the printer is currently printing a job...
1612 */
1613
1614 if (printer->job)
1615 jobid = ((job_t *)printer->job)->id;
1616 else
1617 {
1618 /*
1619 * No, see if there are any pending jobs...
1620 */
1621
1622 for (job = Jobs; job != NULL; job = job->next)
1623 if (job->state->values[0].integer <= IPP_JOB_PROCESSING &&
1624 strcasecmp(job->dest, dest) == 0)
1625 break;
1626
1627 if (job != NULL)
1628 jobid = job->id;
1629 else
1630 {
1631 LogMessage(L_ERROR, "cancel_job: No active jobs on %s!", dest);
1632 send_ipp_error(con, IPP_NOT_POSSIBLE);
1633 return;
1634 }
1635 }
1636 }
1d2c70a6 1637 }
1638 else
1639 {
1640 /*
1641 * Got a job URI; parse it to get the job ID...
1642 */
1643
c0341b41 1644 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1d2c70a6 1645
1646 if (strncmp(resource, "/jobs/", 6) != 0)
1647 {
1648 /*
1649 * Not a valid URI!
1650 */
1651
5ea8888e 1652 LogMessage(L_ERROR, "cancel_job: bad job-uri attribute \'%s\'!",
bd84e0d1 1653 uri->values[0].string.text);
1d2c70a6 1654 send_ipp_error(con, IPP_BAD_REQUEST);
1655 return;
1656 }
1657
1658 jobid = atoi(resource + 6);
1659 }
1660
1661 /*
1662 * See if the job exists...
1663 */
1664
3b41df31 1665 if ((job = FindJob(jobid)) == NULL)
1d2c70a6 1666 {
1667 /*
1668 * Nope - return a "not found" error...
1669 */
1670
5ea8888e 1671 LogMessage(L_ERROR, "cancel_job: job #%d doesn't exist!", jobid);
1d2c70a6 1672 send_ipp_error(con, IPP_NOT_FOUND);
1673 return;
1674 }
1675
3b41df31 1676 /*
1677 * See if the job is owned by the requesting user...
1678 */
1679
ed3e11d8 1680 if (!validate_user(con, job->username, username, sizeof(username)))
3b41df31 1681 {
ed3e11d8 1682 LogMessage(L_ERROR, "cancel_job: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
1683 username, jobid, job->username);
1684 send_ipp_error(con, IPP_FORBIDDEN);
1685 return;
3b41df31 1686 }
1687
7f51c82b 1688 /*
1689 * See if the job is already completed, cancelled, or aborted; if so,
1690 * we can't cancel...
1691 */
1692
1693 if (job->state->values[0].integer >= IPP_JOB_CANCELLED)
1694 {
1695 LogMessage(L_ERROR, "cancel_job: job id %d is %s - can't cancel!",
1696 jobid,
1697 job->state->values[0].integer == IPP_JOB_CANCELLED ? "cancelled" :
1698 job->state->values[0].integer == IPP_JOB_ABORTED ? "aborted" :
1699 "completed");
1700 send_ipp_error(con, IPP_NOT_POSSIBLE);
1701 return;
1702 }
1703
1d2c70a6 1704 /*
1705 * Cancel the job and return...
1706 */
1707
d65c1374 1708 CancelJob(jobid, 0);
96df88bb 1709 CheckJobs();
1d2c70a6 1710
c8f336b5 1711 LogMessage(L_INFO, "Job %d was cancelled by \'%s\'.", jobid, username);
cc0561c6 1712
1d2c70a6 1713 con->response->request.status.status_code = IPP_OK;
e31bfb6e 1714}
1715
1716
b521f3fc 1717/*
1718 * 'check_quotas()' - Check quotas for a printer and user.
1719 */
1720
1721static int /* O - 1 if OK, 0 if not */
1722check_quotas(client_t *con, /* I - Client connection */
1723 printer_t *p) /* I - Printer or class */
1724{
1725 int i; /* Looping var */
1726 ipp_attribute_t *attr; /* Current attribute */
1727 char username[33]; /* Username */
1728 quota_t *q; /* Quota data */
1729
1730
dbb05cda 1731 LogMessage(L_DEBUG2, "check_quotas(%d, %s)\n", con->http.fd, p->name);
1732
b521f3fc 1733 /*
1734 * Check input...
1735 */
1736
1737 if (con == NULL || p == NULL)
1738 return (0);
1739
b521f3fc 1740 /*
1741 * Figure out who is printing...
1742 */
1743
1744 attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME);
1745
1746 if (con->username[0])
1747 {
1748 strncpy(username, con->username, sizeof(username) - 1);
1749 username[sizeof(username) - 1] = '\0';
1750 }
1751 else if (attr != NULL)
1752 {
1753 LogMessage(L_DEBUG, "check_quotas: requesting-user-name = \'%s\'",
1754 attr->values[0].string.text);
1755
1756 strncpy(username, attr->values[0].string.text, sizeof(username) - 1);
1757 username[sizeof(username) - 1] = '\0';
1758 }
1759 else
1760 strcpy(username, "anonymous");
1761
7b1b1c6e 1762 /*
1763 * Check global active job limits for printers and users...
1764 */
1765
1766 if (MaxJobsPerPrinter)
1767 {
1768 /*
1769 * Check if there are too many pending jobs on this printer...
1770 */
1771
1772 if (GetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
1773 {
1774 LogMessage(L_INFO, "Too many jobs for printer \"%s\"...", p->name);
1775 return (0);
1776 }
1777 }
1778
1779 if (MaxJobsPerUser)
1780 {
1781 /*
1782 * Check if there are too many pending jobs for this user...
1783 */
1784
8bc932be 1785 if (GetUserJobCount(username) >= MaxJobsPerUser)
7b1b1c6e 1786 {
1787 LogMessage(L_INFO, "Too many jobs for user \"%s\"...", username);
1788 return (0);
1789 }
1790 }
1791
b521f3fc 1792 /*
1793 * Check against users...
1794 */
1795
7b1b1c6e 1796 if (p->num_users == 0 && p->k_limit == 0 && p->page_limit == 0)
1797 return (1);
1798
b521f3fc 1799 if (p->num_users)
1800 {
1801 for (i = 0; i < p->num_users; i ++)
753453e4 1802 if (strcasecmp(username, p->users[i]) == 0)
b521f3fc 1803 break;
1804
1805 if ((i < p->num_users) == p->deny_users)
1806 {
1807 LogMessage(L_INFO, "Denying user \"%s\" access to printer \"%s\"...",
1808 username, p->name);
1809 return (0);
1810 }
1811 }
1812
1813 /*
1814 * Check quotas...
1815 */
1816
1817 if (p->k_limit || p->page_limit)
1818 {
1819 if ((q = UpdateQuota(p, username, 0, 0)) == NULL)
1820 {
1821 LogMessage(L_ERROR, "Unable to allocate quota data for user \"%s\"!",
1822 username);
1823 return (0);
1824 }
1825
1826 if ((q->k_count >= p->k_limit && p->k_limit) ||
1827 (q->page_count >= p->page_limit && p->page_limit))
1828 {
1829 LogMessage(L_INFO, "User \"%s\" is over the quota limit...",
1830 username);
1831 return (0);
1832 }
1833 }
1834
1835 /*
1836 * If we have gotten this far, we're done!
1837 */
1838
1839 return (1);
1840}
1841
1842
962e5a9f 1843/*
1844 * 'copy_attribute()' - Copy a single attribute.
1845 */
1846
1847static void
1848copy_attribute(ipp_t *to, /* O - Destination request/response */
753453e4 1849 ipp_attribute_t *attr, /* I - Attribute to copy */
1850 int quickcopy)/* I - Do a quick copy? */
962e5a9f 1851{
1852 int i; /* Looping var */
1853 ipp_attribute_t *toattr; /* Destination attribute */
1854
1855
b5cb0608 1856 LogMessage(L_DEBUG2, "copy_attribute(%p, %s)\n", to,
1857 attr->name ? attr->name : "(null)");
dbb05cda 1858
753453e4 1859 switch (attr->value_tag & ~IPP_TAG_COPY)
962e5a9f 1860 {
1861 case IPP_TAG_ZERO :
1862 ippAddSeparator(to);
1863 break;
1864
1865 case IPP_TAG_INTEGER :
1866 case IPP_TAG_ENUM :
1867 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
1868 attr->name, attr->num_values, NULL);
1869
1870 for (i = 0; i < attr->num_values; i ++)
1871 toattr->values[i].integer = attr->values[i].integer;
1872 break;
1873
1874 case IPP_TAG_BOOLEAN :
1875 toattr = ippAddBooleans(to, attr->group_tag, attr->name,
1876 attr->num_values, NULL);
1877
1878 for (i = 0; i < attr->num_values; i ++)
1879 toattr->values[i].boolean = attr->values[i].boolean;
1880 break;
1881
1882 case IPP_TAG_STRING :
1883 case IPP_TAG_TEXT :
1884 case IPP_TAG_NAME :
1885 case IPP_TAG_KEYWORD :
1886 case IPP_TAG_URI :
1887 case IPP_TAG_URISCHEME :
1888 case IPP_TAG_CHARSET :
1889 case IPP_TAG_LANGUAGE :
1890 case IPP_TAG_MIMETYPE :
b6ea8f29 1891 toattr = ippAddStrings(to, attr->group_tag,
753453e4 1892 (ipp_tag_t)(attr->value_tag | quickcopy),
1893 attr->name, attr->num_values, NULL, NULL);
962e5a9f 1894
753453e4 1895 if (quickcopy)
1896 {
1897 for (i = 0; i < attr->num_values; i ++)
1898 toattr->values[i].string.text = attr->values[i].string.text;
1899 }
1900 else
1901 {
1902 for (i = 0; i < attr->num_values; i ++)
1903 toattr->values[i].string.text = strdup(attr->values[i].string.text);
1904 }
962e5a9f 1905 break;
1906
1907 case IPP_TAG_DATE :
1908 toattr = ippAddDate(to, attr->group_tag, attr->name,
1909 attr->values[0].date);
1910 break;
1911
1912 case IPP_TAG_RESOLUTION :
1913 toattr = ippAddResolutions(to, attr->group_tag, attr->name,
1914 attr->num_values, IPP_RES_PER_INCH,
1915 NULL, NULL);
1916
1917 for (i = 0; i < attr->num_values; i ++)
1918 {
1919 toattr->values[i].resolution.xres = attr->values[i].resolution.xres;
1920 toattr->values[i].resolution.yres = attr->values[i].resolution.yres;
1921 toattr->values[i].resolution.units = attr->values[i].resolution.units;
1922 }
1923 break;
1924
1925 case IPP_TAG_RANGE :
1926 toattr = ippAddRanges(to, attr->group_tag, attr->name,
1927 attr->num_values, NULL, NULL);
1928
1929 for (i = 0; i < attr->num_values; i ++)
1930 {
1931 toattr->values[i].range.lower = attr->values[i].range.lower;
1932 toattr->values[i].range.upper = attr->values[i].range.upper;
1933 }
1934 break;
1935
1936 case IPP_TAG_TEXTLANG :
1937 case IPP_TAG_NAMELANG :
b6ea8f29 1938 toattr = ippAddStrings(to, attr->group_tag,
753453e4 1939 (ipp_tag_t)(attr->value_tag | quickcopy),
962e5a9f 1940 attr->name, attr->num_values, NULL, NULL);
1941
753453e4 1942 if (quickcopy)
1943 {
1944 for (i = 0; i < attr->num_values; i ++)
1945 {
1946 toattr->values[i].string.charset = attr->values[i].string.charset;
1947 toattr->values[i].string.text = attr->values[i].string.text;
1948 }
1949 }
1950 else
962e5a9f 1951 {
753453e4 1952 for (i = 0; i < attr->num_values; i ++)
1953 {
1954 if (!i)
1955 toattr->values[i].string.charset =
1956 strdup(attr->values[i].string.charset);
1957 else
1958 toattr->values[i].string.charset =
1959 toattr->values[0].string.charset;
1960
1961 toattr->values[i].string.text = strdup(attr->values[i].string.text);
1962 }
962e5a9f 1963 }
1964 break;
1965
1966 default :
1967 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
1968 attr->name, attr->num_values, NULL);
1969
1970 for (i = 0; i < attr->num_values; i ++)
1971 {
1972 toattr->values[i].unknown.length = attr->values[i].unknown.length;
1973
1974 if (toattr->values[i].unknown.length > 0)
1975 {
1976 if ((toattr->values[i].unknown.data = malloc(toattr->values[i].unknown.length)) == NULL)
1977 toattr->values[i].unknown.length = 0;
1978 else
1979 memcpy(toattr->values[i].unknown.data,
1980 attr->values[i].unknown.data,
1981 toattr->values[i].unknown.length);
1982 }
1983 }
1984 break; /* anti-compiler-warning-code */
1985 }
1986}
1987
1988
1d2c70a6 1989/*
1990 * 'copy_attrs()' - Copy attributes from one request to another.
1991 */
1992
e31bfb6e 1993static void
d6de4648 1994copy_attrs(ipp_t *to, /* I - Destination request */
1995 ipp_t *from, /* I - Source request */
4979ce3d 1996 ipp_attribute_t *req, /* I - Requested attributes */
1997 ipp_tag_t group) /* I - Group to copy */
e31bfb6e 1998{
1d2c70a6 1999 int i; /* Looping var */
962e5a9f 2000 ipp_attribute_t *fromattr; /* Source attribute */
1d2c70a6 2001
2002
dbb05cda 2003 LogMessage(L_DEBUG2, "copy_attrs(%p, %p, %p, %x)\n", to, from, req, group);
1d2c70a6 2004
2005 if (to == NULL || from == NULL)
2006 return;
2007
c7fa9d06 2008 if (req != NULL && strcmp(req->values[0].string.text, "all") == 0)
2009 req = NULL; /* "all" means no filter... */
2010
1d2c70a6 2011 for (fromattr = from->attrs; fromattr != NULL; fromattr = fromattr->next)
2012 {
d6de4648 2013 /*
2014 * Filter attributes as needed...
2015 */
2016
d4c438d4 2017 if (group != IPP_TAG_ZERO && fromattr->group_tag != group &&
2018 fromattr->group_tag != IPP_TAG_ZERO)
2019 continue;
2020
2021 if (req != NULL && fromattr->name != NULL)
d6de4648 2022 {
2023 for (i = 0; i < req->num_values; i ++)
2024 if (strcmp(fromattr->name, req->values[i].string.text) == 0)
2025 break;
2026
2027 if (i == req->num_values)
2028 continue;
2029 }
2030
753453e4 2031 copy_attribute(to, fromattr, IPP_TAG_COPY);
1d2c70a6 2032 }
e31bfb6e 2033}
2034
2035
bd84e0d1 2036/*
2037 * 'create_job()' - Print a file to a printer or class.
2038 */
2039
2040static void
2041create_job(client_t *con, /* I - Client connection */
2042 ipp_attribute_t *uri) /* I - Printer URI */
2043{
2044 ipp_attribute_t *attr; /* Current attribute */
1049abbe 2045 const char *dest; /* Destination */
bd84e0d1 2046 cups_ptype_t dtype; /* Destination type (printer or class) */
2047 int priority; /* Job priority */
2048 char *title; /* Job name/title */
2049 job_t *job; /* Current job */
2050 char job_uri[HTTP_MAX_URI],
2051 /* Job URI */
1049abbe 2052 printer_uri[HTTP_MAX_URI],
2053 /* Printer URI */
bd84e0d1 2054 method[HTTP_MAX_URI],
2055 /* Method portion of URI */
2056 username[HTTP_MAX_URI],
2057 /* Username portion of URI */
2058 host[HTTP_MAX_URI],
2059 /* Host portion of URI */
2060 resource[HTTP_MAX_URI];
2061 /* Resource portion of URI */
2062 int port; /* Port portion of URI */
2063 printer_t *printer; /* Printer data */
b521f3fc 2064 int kbytes; /* Size of print file */
bd84e0d1 2065
2066
dbb05cda 2067 LogMessage(L_DEBUG2, "create_job(%d, %s)\n", con->http.fd,
2068 uri->values[0].string.text);
bd84e0d1 2069
2070 /*
2071 * Verify that the POST operation was done to a valid URI.
2072 */
2073
2074 if (strncmp(con->uri, "/classes/", 9) != 0 &&
2075 strncmp(con->uri, "/printers/", 10) != 0)
2076 {
5ea8888e 2077 LogMessage(L_ERROR, "create_job: cancel request on bad resource \'%s\'!",
bd84e0d1 2078 con->uri);
2079 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2080 return;
2081 }
2082
2083 /*
2084 * Is the destination valid?
2085 */
2086
2087 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
2088
ed3e11d8 2089 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
bd84e0d1 2090 {
2091 /*
2092 * Bad URI...
2093 */
2094
5ea8888e 2095 LogMessage(L_ERROR, "create_job: resource name \'%s\' no good!", resource);
bd84e0d1 2096 send_ipp_error(con, IPP_NOT_FOUND);
2097 return;
2098 }
2099
2100 /*
2101 * See if the printer is accepting jobs...
2102 */
2103
2104 if (dtype == CUPS_PRINTER_CLASS)
1049abbe 2105 {
bd84e0d1 2106 printer = FindClass(dest);
99de6da0 2107
2108#ifdef AF_INET6
2109 if (con->http.hostaddr.addr.sa_family == AF_INET6)
2110 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/classes/%s",
2111 ServerName, ntohs(con->http.hostaddr.ipv6.sin6_port), dest);
2112 else
2113#endif /* AF_INET6 */
a6988fb1 2114 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/classes/%s",
99de6da0 2115 ServerName, ntohs(con->http.hostaddr.ipv4.sin_port), dest);
1049abbe 2116 }
bd84e0d1 2117 else
1049abbe 2118 {
bd84e0d1 2119 printer = FindPrinter(dest);
2120
99de6da0 2121#ifdef AF_INET6
2122 if (con->http.hostaddr.addr.sa_family == AF_INET6)
2123 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/printers/%s",
2124 ServerName, ntohs(con->http.hostaddr.ipv6.sin6_port), dest);
2125 else
2126#endif /* AF_INET6 */
a6988fb1 2127 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/printers/%s",
99de6da0 2128 ServerName, ntohs(con->http.hostaddr.ipv4.sin_port), dest);
1049abbe 2129 }
2130
bd84e0d1 2131 if (!printer->accepting)
2132 {
5ea8888e 2133 LogMessage(L_INFO, "create_job: destination \'%s\' is not accepting jobs.",
bd84e0d1 2134 dest);
2135 send_ipp_error(con, IPP_NOT_ACCEPTING);
2136 return;
2137 }
2138
d7845573 2139 /*
2140 * Make sure we aren't over our limit...
2141 */
2142
2143 if (NumJobs >= MaxJobs && MaxJobs)
2144 CleanJobs();
2145
2146 if (NumJobs >= MaxJobs && MaxJobs)
2147 {
2148 LogMessage(L_INFO, "create_job: too many jobs.");
2149 send_ipp_error(con, IPP_NOT_POSSIBLE);
2150 return;
2151 }
2152
b521f3fc 2153 if (!check_quotas(con, printer))
d7845573 2154 {
b521f3fc 2155 send_ipp_error(con, IPP_NOT_POSSIBLE);
2156 return;
d7845573 2157 }
d7845573 2158
bd84e0d1 2159 /*
2160 * Create the job and set things up...
2161 */
2162
2163 if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
2164 priority = attr->values[0].integer;
2165 else
1049abbe 2166 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
2167 priority = 50);
bd84e0d1 2168
2169 if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
2170 title = attr->values[0].string.text;
2171 else
1049abbe 2172 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
2173 title = "Untitled");
bd84e0d1 2174
2175 if ((job = AddJob(priority, printer->name)) == NULL)
2176 {
5ea8888e 2177 LogMessage(L_ERROR, "create_job: unable to add job for destination \'%s\'!",
bd84e0d1 2178 dest);
2179 send_ipp_error(con, IPP_INTERNAL_ERROR);
2180 return;
2181 }
2182
1e437ea6 2183 job->dtype = dtype;
2184 job->attrs = con->request;
2185 con->request = NULL;
bd84e0d1 2186
2187 strncpy(job->title, title, sizeof(job->title) - 1);
2188
17b95e13 2189 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
2190
2191 if (con->username[0])
a6988fb1 2192 {
2193 strncpy(job->username, con->username, sizeof(job->username) - 1);
2194 job->username[sizeof(job->username) - 1] = '\0';
2195 }
17b95e13 2196 else if (attr != NULL)
bd84e0d1 2197 {
5ea8888e 2198 LogMessage(L_DEBUG, "create_job: requesting-user-name = \'%s\'",
bd84e0d1 2199 attr->values[0].string.text);
2200
a6988fb1 2201 strncpy(job->username, attr->values[0].string.text,
2202 sizeof(job->username) - 1);
bd84e0d1 2203 job->username[sizeof(job->username) - 1] = '\0';
2204 }
17b95e13 2205 else
1049abbe 2206 strcpy(job->username, "anonymous");
2207
2208 if (attr == NULL)
2209 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
2210 NULL, job->username);
2211 else
2212 {
4979ce3d 2213 attr->group_tag = IPP_TAG_JOB;
1049abbe 2214 free(attr->name);
2215 attr->name = strdup("job-originating-user-name");
2216 }
bd84e0d1 2217
bb27c189 2218 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
2219 "job-originating-host-name", NULL, con->http.hostname);
7ebf3a09 2220 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
0ccccc99 2221 time(NULL));
7ebf3a09 2222 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
2223 "time-at-processing", 0);
2224 attr->value_tag = IPP_TAG_NOVALUE;
2225 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
2226 "time-at-completed", 0);
2227 attr->value_tag = IPP_TAG_NOVALUE;
2228
e903a8f6 2229 /*
2230 * Add remaining job attributes...
2231 */
2232
2233 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
2234 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
2235 "job-state", IPP_JOB_STOPPED);
aa0cb334 2236 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
2237 "job-media-sheets-completed", 0);
e903a8f6 2238 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
2239 printer_uri);
2240 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
2241 title);
2242
91606789 2243 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
2244 attr->values[0].integer = 0;
2245 else
2246 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
2247 "job-k-octets", 0);
2248
e583bc2d 2249 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
2250 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4104c7ec 2251 if (attr == NULL)
2252 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2253 "job-hold-until", NULL, "no-hold");
753453e4 2254 if (attr != NULL && strcmp(attr->values[0].string.text, "no-hold") != 0 &&
2255 !(printer->type & CUPS_PRINTER_REMOTE))
e583bc2d 2256 {
2257 /*
2258 * Hold job until specified time...
2259 */
2260
e583bc2d 2261 SetJobHoldUntil(job->id, attr->values[0].string.text);
2262 }
4b96a841 2263 else
2264 job->hold_until = time(NULL) + 60;
2265
2266 job->state->values[0].integer = IPP_JOB_HELD;
e583bc2d 2267
d11458ff 2268 if (!(printer->type & CUPS_PRINTER_REMOTE) || Classification[0])
a3e17a89 2269 {
2270 /*
2271 * Add job sheets options...
2272 */
2273
ebde520c 2274 if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) == NULL)
a3e17a89 2275 {
753453e4 2276 LogMessage(L_DEBUG, "Adding default job-sheets values \"%s,%s\"...",
2277 printer->job_sheets[0], printer->job_sheets[1]);
2278
aa0cb334 2279 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
a3e17a89 2280 2, NULL, NULL);
2281 attr->values[0].string.text = strdup(printer->job_sheets[0]);
2282 attr->values[1].string.text = strdup(printer->job_sheets[1]);
2283 }
2284
b5cb0608 2285 job->job_sheets = attr;
2286
d11458ff 2287 /*
2288 * Enforce classification level if set...
2289 */
2290
2291 if (Classification[0])
2292 {
753453e4 2293 if (ClassifyOverride)
2294 {
2295 if (strcmp(attr->values[0].string.text, "none") == 0 &&
2296 (attr->num_values == 1 ||
2297 strcmp(attr->values[1].string.text, "none") == 0))
2298 {
2299 /*
2300 * Force the leading banner to have the classification on it...
2301 */
2302
2303 free(attr->values[0].string.text);
2304 attr->values[0].string.text = strdup(Classification);
2305 }
2306 else if (attr->num_values == 2 &&
2307 strcmp(attr->values[0].string.text, attr->values[1].string.text) != 0 &&
2308 strcmp(attr->values[0].string.text, "none") != 0 &&
2309 strcmp(attr->values[1].string.text, "none") != 0)
2310 {
2311 /*
2312 * Can't put two different security markings on the same document!
2313 */
2314
2315 free(attr->values[1].string.text);
2316 attr->values[1].string.text = strdup(attr->values[0].string.text);
2317 }
2318 }
2319 else if (strcmp(attr->values[0].string.text, Classification) != 0 &&
2320 (attr->num_values == 1 ||
2321 strcmp(attr->values[1].string.text, Classification) != 0))
d11458ff 2322 {
2323 /*
2324 * Force the leading banner to have the classification on it...
2325 */
2326
2327 free(attr->values[0].string.text);
2328 attr->values[0].string.text = strdup(Classification);
2329 }
2330 }
2331
a3e17a89 2332 /*
2333 * See if we need to add the starting sheet...
2334 */
2335
b521f3fc 2336 kbytes = copy_banner(con, job, attr->values[0].string.text);
2337
2338 UpdateQuota(printer, job->username, 0, kbytes);
a3e17a89 2339 }
753453e4 2340 else if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
2341 job->sheets = attr;
e903a8f6 2342
2343 /*
2344 * Save and log the job...
2345 */
a3e17a89 2346
4979ce3d 2347 SaveJob(job->id);
2348
5ea8888e 2349 LogMessage(L_INFO, "Job %d created on \'%s\' by \'%s\'.", job->id,
bd84e0d1 2350 job->dest, job->username);
2351
2352 /*
2353 * Fill in the response info...
2354 */
2355
99de6da0 2356#ifdef AF_INET6
2357 if (con->http.hostaddr.addr.sa_family == AF_INET6)
2358 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
2359 ntohs(con->http.hostaddr.ipv6.sin6_port), job->id);
2360 else
2361#endif /* AF_INET6 */
a6988fb1 2362 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
99de6da0 2363 ntohs(con->http.hostaddr.ipv4.sin_port), job->id);
2364
bd84e0d1 2365 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
2366
2367 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
2368
1049abbe 2369 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
2370 job->state->values[0].integer);
bd84e0d1 2371
2372 con->response->request.status.status_code = IPP_OK;
2373}
2374
2375
e903a8f6 2376/*
2377 * 'copy_banner()' - Copy a banner file to the requests directory for the
2378 * specified job.
2379 */
2380
b521f3fc 2381static int /* O - Size of banner file in kbytes */
e903a8f6 2382copy_banner(client_t *con, /* I - Client connection */
2383 job_t *job, /* I - Job information */
2384 const char *name) /* I - Name of banner */
2385{
2386 int i; /* Looping var */
b521f3fc 2387 int kbytes; /* Size of banner file in kbytes */
e903a8f6 2388 char filename[1024]; /* Job filename */
2389 banner_t *banner; /* Pointer to banner */
2390 FILE *in; /* Input file */
2391 FILE *out; /* Output file */
2392 int ch; /* Character from file */
2393 char attrname[255], /* Name of attribute */
2394 *s; /* Pointer into name */
2395 ipp_attribute_t *attr; /* Attribute */
2396
2397
dbb05cda 2398 LogMessage(L_DEBUG2, "copy_banner(%d, %d, %s)\n", con->http.fd, job->id,
2399 name);
2400
e903a8f6 2401 /*
2402 * Find the banner; return if not found or "none"...
2403 */
2404
ebde520c 2405 LogMessage(L_DEBUG, "copy_banner(%p, %d, \"%s\")", con, job->id,
2406 name ? name : "(null)");
2407
e903a8f6 2408 if (name == NULL ||
2409 strcmp(name, "none") == 0 ||
2410 (banner = FindBanner(name)) == NULL)
b521f3fc 2411 return (0);
e903a8f6 2412
2413 /*
2414 * Open the banner and job files...
2415 */
2416
2417 if (add_file(con, job, banner->filetype))
b521f3fc 2418 return (0);
e903a8f6 2419
a6988fb1 2420 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
2421 job->num_files);
e903a8f6 2422 if ((out = fopen(filename, "w")) == NULL)
2423 {
2424 LogMessage(L_ERROR, "copy_banner: Unable to create banner job file %s - %s",
2425 filename, strerror(errno));
2426 job->num_files --;
b521f3fc 2427 return (0);
e903a8f6 2428 }
2429
05156f4e 2430 fchmod(fileno(out), 0640);
2431 fchown(fileno(out), User, Group);
2432
753453e4 2433 if (con->language)
2434 {
2435 /*
2436 * Try the localized banner file under the subdirectory...
2437 */
2438
2439 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
2440 con->language->language, name);
2441
2442 if (access(filename, 0) && con->language->language[2])
2443 {
2444 /*
2445 * Wasn't able to find "ll_CC" locale file; try the non-national
2446 * localization banner directory.
2447 */
2448
2449 attrname[0] = con->language->language[0];
2450 attrname[1] = con->language->language[1];
2451 attrname[2] = '\0';
2452
2453 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
2454 attrname, name);
2455 }
2456
2457 if (access(filename, 0))
2458 {
2459 /*
2460 * Use the non-localized banner file.
2461 */
2462
2463 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
2464 }
2465 }
2466 else
2467 {
2468 /*
2469 * Use the non-localized banner file.
2470 */
2471
2472 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
2473 }
2474
2475 if ((in = fopen(filename, "r")) == NULL)
e903a8f6 2476 {
2477 fclose(out);
2478 unlink(filename);
2479 LogMessage(L_ERROR, "copy_banner: Unable to open banner template file %s - %s",
2480 filename, strerror(errno));
2481 job->num_files --;
b521f3fc 2482 return (0);
e903a8f6 2483 }
2484
2485 /*
2486 * Parse the file to the end...
2487 */
2488
2489 while ((ch = getc(in)) != EOF)
2490 if (ch == '{')
2491 {
2492 /*
2493 * Get an attribute name...
2494 */
2495
2496 for (s = attrname; (ch = getc(in)) != EOF;)
753453e4 2497 if (!isalpha(ch) && ch != '-' && ch != '?')
e903a8f6 2498 break;
2499 else if (s < (attrname + sizeof(attrname) - 1))
2500 *s++ = ch;
753453e4 2501 else
2502 break;
2503
2504 *s = '\0';
e903a8f6 2505
753453e4 2506 if (ch != '}')
b31caa56 2507 {
2508 /*
753453e4 2509 * Ignore { followed by stuff that is not an attribute name...
b31caa56 2510 */
2511
2512 putc('{', out);
753453e4 2513 fputs(attrname, out);
b31caa56 2514 putc(ch, out);
2515 continue;
2516 }
2517
e903a8f6 2518 /*
2519 * See if it is defined...
2520 */
2521
753453e4 2522 if (attrname[0] == '?')
2523 s = attrname + 1;
2524 else
2525 s = attrname;
2526
2527 if (strcmp(s, "printer-name") == 0)
fd527bfb 2528 {
2529 fputs(job->dest, out);
2530 continue;
2531 }
753453e4 2532 else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
2533 {
2534 /*
2535 * See if we have a leading question mark...
2536 */
2537
2538 if (attrname[0] != '?')
2539 {
2540 /*
2541 * Nope, write to file as-is; probably a PostScript procedure...
2542 */
2543
2544 putc('{', out);
2545 fputs(attrname, out);
2546 putc('}', out);
2547 }
2548
2549 continue;
2550 }
e903a8f6 2551
2552 /*
2553 * Output value(s)...
2554 */
2555
2556 for (i = 0; i < attr->num_values; i ++)
2557 {
2558 if (i)
2559 putc(',', out);
2560
2561 switch (attr->value_tag)
2562 {
2563 case IPP_TAG_INTEGER :
2564 case IPP_TAG_ENUM :
eaafe5e0 2565 if (strncmp(attrname, "time-at-", 8) == 0)
2566 fputs(GetDateTime(attr->values[i].integer), out);
2567 else
2568 fprintf(out, "%d", attr->values[i].integer);
e903a8f6 2569 break;
2570
2571 case IPP_TAG_BOOLEAN :
2572 fprintf(out, "%d", attr->values[i].boolean);
2573 break;
2574
2575 case IPP_TAG_NOVALUE :
2576 fputs("novalue", out);
2577 break;
2578
2579 case IPP_TAG_RANGE :
2580 fprintf(out, "%d-%d", attr->values[i].range.lower,
2581 attr->values[i].range.upper);
2582 break;
2583
2584 case IPP_TAG_RESOLUTION :
2585 fprintf(out, "%dx%d%s", attr->values[i].resolution.xres,
2586 attr->values[i].resolution.yres,
2587 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
2588 "dpi" : "dpc");
2589 break;
2590
2591 case IPP_TAG_URI :
2592 case IPP_TAG_STRING :
2593 case IPP_TAG_TEXT :
2594 case IPP_TAG_NAME :
2595 case IPP_TAG_KEYWORD :
2596 case IPP_TAG_CHARSET :
2597 case IPP_TAG_LANGUAGE :
fd527bfb 2598 if (strcasecmp(banner->filetype->type, "postscript") == 0)
2599 {
2600 /*
2601 * Need to quote strings for PS banners...
2602 */
2603
2604 const char *p;
2605
2606 for (p = attr->values[i].string.text; *p; p ++)
2607 {
ecbf0864 2608 if (*p == '(' || *p == ')' || *p == '\\')
2609 {
fd527bfb 2610 putc('\\', out);
ecbf0864 2611 putc(*p, out);
2612 }
2613 else if (*p < 32 || *p > 126)
2614 fprintf(out, "\\%03o", *p);
2615 else
2616 putc(*p, out);
fd527bfb 2617 }
2618 }
2619 else
2620 fputs(attr->values[i].string.text, out);
e903a8f6 2621 break;
2622
2623 default :
2624 break; /* anti-compiler-warning-code */
2625 }
2626 }
2627 }
2628 else if (ch == '\\') /* Quoted char */
2629 putc(getc(in), out);
2630 else
2631 putc(ch, out);
2632
2633 fclose(in);
91606789 2634
b521f3fc 2635 kbytes = (ftell(out) + 1023) / 1024;
2636
91606789 2637 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
b521f3fc 2638 attr->values[0].integer += kbytes;
91606789 2639
e903a8f6 2640 fclose(out);
b521f3fc 2641
2642 return (kbytes);
e903a8f6 2643}
2644
2645
5d99df62 2646/*
2647 * 'copy_file()' - Copy a PPD file or interface script...
2648 */
2649
2650static int /* O - 0 = success, -1 = error */
2651copy_file(const char *from, /* I - Source file */
2652 const char *to) /* I - Destination file */
2653{
d4c438d4 2654#ifdef HAVE_LIBZ
2655 gzFile src; /* Source file */
2656#else
2657 int src; /* Source file */
2658#endif /* HAVE_LIBZ */
2659 int dst, /* Destination file */
2660 bytes; /* Bytes to read/write */
2661 char buffer[8192]; /* Copy buffer */
2662
2663
dbb05cda 2664 LogMessage(L_DEBUG2, "copy_file(%s, %s)\n", from, to);
2665
d4c438d4 2666#ifdef HAVE_LIBZ
8acc243c 2667 if ((src = gzopen(from, "rb")) == NULL)
d4c438d4 2668 return (-1);
2669#else
5d99df62 2670 if ((src = open(from, O_RDONLY)) < 0)
2671 return (-1);
d4c438d4 2672#endif /* HAVE_LIBZ */
5d99df62 2673
2674 if ((dst = open(to, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0)
2675 {
d4c438d4 2676#ifdef HAVE_LIBZ
2677 gzclose(src);
2678#else
5d99df62 2679 close(src);
d4c438d4 2680#endif /* HAVE_LIBZ */
5d99df62 2681 return (-1);
2682 }
2683
d4c438d4 2684#ifdef HAVE_LIBZ
2685 while ((bytes = gzread(src, buffer, sizeof(buffer))) > 0)
2686#else
5d99df62 2687 while ((bytes = read(src, buffer, sizeof(buffer))) > 0)
d4c438d4 2688#endif /* HAVE_LIBZ */
5d99df62 2689 if (write(dst, buffer, bytes) < bytes)
2690 {
d4c438d4 2691#ifdef HAVE_LIBZ
2692 gzclose(src);
2693#else
5d99df62 2694 close(src);
d4c438d4 2695#endif /* HAVE_LIBZ */
5d99df62 2696 close(dst);
2697 return (-1);
2698 }
2699
d4c438d4 2700#ifdef HAVE_LIBZ
2701 gzclose(src);
2702#else
5d99df62 2703 close(src);
d4c438d4 2704#endif /* HAVE_LIBZ */
5d99df62 2705 close(dst);
2706
2707 return (0);
2708}
2709
2710
1d2c70a6 2711/*
c7fa9d06 2712 * 'delete_printer()' - Remove a printer or class from the system.
1d2c70a6 2713 */
2714
e31bfb6e 2715static void
c7fa9d06 2716delete_printer(client_t *con, /* I - Client connection */
2717 ipp_attribute_t *uri) /* I - URI of printer or class */
e31bfb6e 2718{
1049abbe 2719 const char *dest; /* Destination */
c7fa9d06 2720 cups_ptype_t dtype; /* Destination type (printer or class) */
2721 char method[HTTP_MAX_URI],
2722 /* Method portion of URI */
2723 username[HTTP_MAX_URI],
2724 /* Username portion of URI */
2725 host[HTTP_MAX_URI],
2726 /* Host portion of URI */
2727 resource[HTTP_MAX_URI];
2728 /* Resource portion of URI */
2729 int port; /* Port portion of URI */
2730 printer_t *printer; /* Printer/class */
2731 char filename[1024]; /* Script/PPD filename */
2732
2733
dbb05cda 2734 LogMessage(L_DEBUG2, "delete_printer(%d, %s)\n", con->http.fd,
2735 uri->values[0].string.text);
2736
f3d580b9 2737 /*
2738 * Was this operation called from the correct URI?
2739 */
2740
2741 if (strncmp(con->uri, "/admin/", 7) != 0)
2742 {
5ea8888e 2743 LogMessage(L_ERROR, "delete_printer: admin request on bad resource \'%s\'!",
1124e9ec 2744 con->uri);
f3d580b9 2745 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2746 return;
2747 }
2748
3270670b 2749 DEBUG_printf(("delete_printer(%08x, %08x)\n", con, uri));
e31bfb6e 2750
f3d580b9 2751 /*
c7fa9d06 2752 * Do we have a valid URI?
f3d580b9 2753 */
2754
c7fa9d06 2755 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
2756
ed3e11d8 2757 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
f3d580b9 2758 {
c7fa9d06 2759 /*
2760 * Bad URI...
2761 */
2762
5ea8888e 2763 LogMessage(L_ERROR, "delete_printer: resource name \'%s\' no good!", resource);
c7fa9d06 2764 send_ipp_error(con, IPP_NOT_FOUND);
f3d580b9 2765 return;
2766 }
2767
c7fa9d06 2768 /*
2769 * Find the printer or class and delete it...
2770 */
2771
2772 if (dtype == CUPS_PRINTER_CLASS)
2773 printer = FindClass(dest);
2774 else
2775 printer = FindPrinter(dest);
2776
c7fa9d06 2777 /*
753453e4 2778 * Remove old jobs...
c7fa9d06 2779 */
2780
a6988fb1 2781 CancelJobs(dest);
2782
753453e4 2783 /*
2784 * Remove any old PPD or script files...
2785 */
2786
a6988fb1 2787 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot, dest);
c7fa9d06 2788 unlink(filename);
2789
a6988fb1 2790 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest);
c7fa9d06 2791 unlink(filename);
2792
cc0561c6 2793 if (dtype == CUPS_PRINTER_CLASS)
082b40d2 2794 {
5ea8888e 2795 LogMessage(L_INFO, "Class \'%s\' deleted by \'%s\'.", dest,
cc0561c6 2796 con->username);
a6988fb1 2797
2798 DeletePrinter(printer);
2799 SaveAllClasses();
082b40d2 2800 }
cc0561c6 2801 else
082b40d2 2802 {
5ea8888e 2803 LogMessage(L_INFO, "Printer \'%s\' deleted by \'%s\'.", dest,
cc0561c6 2804 con->username);
a6988fb1 2805
2806 DeletePrinter(printer);
2807 SaveAllPrinters();
082b40d2 2808 }
cc0561c6 2809
c7fa9d06 2810 /*
2811 * Return with no errors...
2812 */
2813
2814 con->response->request.status.status_code = IPP_OK;
e31bfb6e 2815}
2816
2817
1d2c70a6 2818/*
2819 * 'get_default()' - Get the default destination.
2820 */
2821
e31bfb6e 2822static void
1d2c70a6 2823get_default(client_t *con) /* I - Client connection */
e31bfb6e 2824{
dbb05cda 2825 LogMessage(L_DEBUG2, "get_default(%d)\n", con->http.fd);
1d2c70a6 2826
3da4463c 2827 if (DefaultPrinter != NULL)
2828 {
2829 copy_attrs(con->response, DefaultPrinter->attrs,
2830 ippFindAttribute(con->request, "requested-attributes",
4979ce3d 2831 IPP_TAG_KEYWORD), IPP_TAG_ZERO);
1d2c70a6 2832
3da4463c 2833 con->response->request.status.status_code = IPP_OK;
2834 }
2835 else
2836 con->response->request.status.status_code = IPP_NOT_FOUND;
e31bfb6e 2837}
2838
2839
bd84e0d1 2840/*
2841 * 'get_devices()' - Get the list of available devices on the local system.
2842 */
2843
2844static void
2845get_devices(client_t *con) /* I - Client connection */
2846{
dbb05cda 2847 LogMessage(L_DEBUG2, "get_devices(%d)\n", con->http.fd);
2848
bd84e0d1 2849 /*
2850 * Copy the device attributes to the response using the requested-attributes
2851 * attribute that may be provided by the client.
2852 */
2853
2854 copy_attrs(con->response, Devices,
2855 ippFindAttribute(con->request, "requested-attributes",
4979ce3d 2856 IPP_TAG_KEYWORD), IPP_TAG_ZERO);
bd84e0d1 2857
2858 con->response->request.status.status_code = IPP_OK;
2859}
2860
2861
1d2c70a6 2862/*
2863 * 'get_jobs()' - Get a list of jobs for the specified printer.
2864 */
2865
e31bfb6e 2866static void
1d2c70a6 2867get_jobs(client_t *con, /* I - Client connection */
2868 ipp_attribute_t *uri) /* I - Printer URI */
e31bfb6e 2869{
27d555e8 2870 ipp_attribute_t *attr, /* Current attribute */
2871 *requested; /* Requested attributes */
1049abbe 2872 const char *dest; /* Destination */
1d2c70a6 2873 cups_ptype_t dtype; /* Destination type (printer or class) */
2874 char method[HTTP_MAX_URI],
2875 /* Method portion of URI */
2876 username[HTTP_MAX_URI],
2877 /* Username portion of URI */
2878 host[HTTP_MAX_URI],
2879 /* Host portion of URI */
2880 resource[HTTP_MAX_URI];
2881 /* Resource portion of URI */
2882 int port; /* Port portion of URI */
bd84e0d1 2883 int completed; /* Completed jobs? */
1d2c70a6 2884 int limit; /* Maximum number of jobs to return */
2885 int count; /* Number of jobs that match */
2886 job_t *job; /* Current job pointer */
8a2c2126 2887 char job_uri[HTTP_MAX_URI];
1d2c70a6 2888 /* Job URI... */
1d2c70a6 2889
2890
dbb05cda 2891 LogMessage(L_DEBUG2, "get_jobs(%d, %s)\n", con->http.fd,
2892 uri->values[0].string.text);
1d2c70a6 2893
2894 /*
2895 * Is the destination valid?
2896 */
2897
c0341b41 2898 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1d2c70a6 2899
2900 if ((strncmp(resource, "/jobs", 5) == 0 && strlen(resource) <= 6) ||
2901 (strncmp(resource, "/printers", 9) == 0 && strlen(resource) <= 10))
2902 {
2903 dest = NULL;
2904 dtype = (cups_ptype_t)0;
2905 }
2906 else if (strncmp(resource, "/classes", 8) == 0 && strlen(resource) <= 9)
2907 {
2908 dest = NULL;
2909 dtype = CUPS_PRINTER_CLASS;
2910 }
ed3e11d8 2911 else if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
1d2c70a6 2912 {
2913 /*
2914 * Bad URI...
2915 */
2916
5ea8888e 2917 LogMessage(L_ERROR, "get_jobs: resource name \'%s\' no good!", resource);
1d2c70a6 2918 send_ipp_error(con, IPP_NOT_FOUND);
2919 return;
2920 }
2921
2922 /*
2923 * See if the "which-jobs" attribute have been specified; if so, return
96df88bb 2924 * right away if they specify "completed" - we don't keep old job records...
1d2c70a6 2925 */
2926
c0341b41 2927 if ((attr = ippFindAttribute(con->request, "which-jobs", IPP_TAG_KEYWORD)) != NULL &&
2928 strcmp(attr->values[0].string.text, "completed") == 0)
bd84e0d1 2929 completed = 1;
2930 else
2931 completed = 0;
1d2c70a6 2932
2933 /*
a3e17a89 2934 * See if they want to limit the number of jobs reported...
1d2c70a6 2935 */
2936
c0341b41 2937 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
1d2c70a6 2938 limit = attr->values[0].integer;
2939 else
a3e17a89 2940 limit = 1000000;
1d2c70a6 2941
2942 /*
2943 * See if we only want to see jobs for a specific user...
2944 */
2945
c0341b41 2946 if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL &&
1d2c70a6 2947 attr->values[0].boolean)
2948 {
17b95e13 2949 if (con->username[0])
a6988fb1 2950 {
2951 strncpy(username, con->username, sizeof(username) - 1);
2952 username[sizeof(username) - 1] = '\0';
2953 }
17b95e13 2954 else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
1d2c70a6 2955 {
c0341b41 2956 strncpy(username, attr->values[0].string.text, sizeof(username) - 1);
1d2c70a6 2957 username[sizeof(username) - 1] = '\0';
2958 }
17b95e13 2959 else
2960 strcpy(username, "anonymous");
1d2c70a6 2961 }
2962 else
2963 username[0] = '\0';
2964
27d555e8 2965 requested = ippFindAttribute(con->request, "requested-attributes",
2966 IPP_TAG_KEYWORD);
2967
1d2c70a6 2968 /*
2969 * OK, build a list of jobs for this printer...
2970 */
2971
2972 for (count = 0, job = Jobs; count < limit && job != NULL; job = job->next)
2973 {
2974 /*
2975 * Filter out jobs that don't match...
2976 */
2977
96df88bb 2978 DEBUG_printf(("get_jobs: job->id = %d\n", job->id));
2979
1d2c70a6 2980 if ((dest != NULL && strcmp(job->dest, dest) != 0))
2981 continue;
2982 if (job->dtype != dtype &&
2983 (username[0] == '\0' || strncmp(resource, "/jobs", 5) != 0))
2984 continue;
2985 if (username[0] != '\0' && strcmp(username, job->username) != 0)
2986 continue;
2987
1049abbe 2988 if (completed && job->state->values[0].integer <= IPP_JOB_STOPPED)
bd84e0d1 2989 continue;
1049abbe 2990 if (!completed && job->state->values[0].integer > IPP_JOB_STOPPED)
bd84e0d1 2991 continue;
2992
1d2c70a6 2993 count ++;
2994
96df88bb 2995 DEBUG_printf(("get_jobs: count = %d\n", count));
2996
1d2c70a6 2997 /*
91606789 2998 * Send the requested attributes for each job...
1d2c70a6 2999 */
3000
99de6da0 3001#ifdef AF_INET6
3002 if (con->http.hostaddr.addr.sa_family == AF_INET6)
3003 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
3004 ntohs(con->http.hostaddr.ipv6.sin6_port), job->id);
3005 else
3006#endif /* AF_INET6 */
a6988fb1 3007 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
99de6da0 3008 ntohs(con->http.hostaddr.ipv4.sin_port), job->id);
1d2c70a6 3009
27b6a264 3010 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
3011 "job-more-info", NULL, job_uri);
1d2c70a6 3012
27b6a264 3013 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
1049abbe 3014 "job-uri", NULL, job_uri);
1d2c70a6 3015
7ebf3a09 3016 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
0ccccc99 3017 "job-printer-up-time", time(NULL));
7ebf3a09 3018
1049abbe 3019 /*
3020 * Copy the job attributes to the response using the requested-attributes
3021 * attribute that may be provided by the client.
3022 */
1d2c70a6 3023
27d555e8 3024 copy_attrs(con->response, job->attrs, requested, IPP_TAG_JOB);
1d2c70a6 3025
7ebf3a09 3026 add_job_state_reasons(con, job);
3027
1d2c70a6 3028 ippAddSeparator(con->response);
3029 }
3030
27d555e8 3031 if (requested != NULL)
1d2c70a6 3032 con->response->request.status.status_code = IPP_OK_SUBST;
3033 else
3034 con->response->request.status.status_code = IPP_OK;
e31bfb6e 3035}
3036
3037
1d2c70a6 3038/*
3039 * 'get_job_attrs()' - Get job attributes.
3040 */
3041
e31bfb6e 3042static void
1d2c70a6 3043get_job_attrs(client_t *con, /* I - Client connection */
3044 ipp_attribute_t *uri) /* I - Job URI */
e31bfb6e 3045{
27d555e8 3046 ipp_attribute_t *attr, /* Current attribute */
3047 *requested; /* Requested attributes */
1d2c70a6 3048 int jobid; /* Job ID */
3049 job_t *job; /* Current job */
3050 char method[HTTP_MAX_URI],
3051 /* Method portion of URI */
3052 username[HTTP_MAX_URI],
3053 /* Username portion of URI */
3054 host[HTTP_MAX_URI],
3055 /* Host portion of URI */
3056 resource[HTTP_MAX_URI];
3057 /* Resource portion of URI */
3058 int port; /* Port portion of URI */
8a2c2126 3059 char job_uri[HTTP_MAX_URI];
782359ca 3060 /* Job URI... */
1d2c70a6 3061
3062
dbb05cda 3063 LogMessage(L_DEBUG2, "get_job_attrs(%d, %s)\n", con->http.fd,
3064 uri->values[0].string.text);
1d2c70a6 3065
3066 /*
3067 * See if we have a job URI or a printer URI...
3068 */
3069
3070 if (strcmp(uri->name, "printer-uri") == 0)
3071 {
3072 /*
3073 * Got a printer URI; see if we also have a job-id attribute...
3074 */
3075
c0341b41 3076 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
1d2c70a6 3077 {
5ea8888e 3078 LogMessage(L_ERROR, "get_job_attrs: got a printer-uri attribute but no job-id!");
1d2c70a6 3079 send_ipp_error(con, IPP_BAD_REQUEST);
3080 return;
3081 }
3082
3083 jobid = attr->values[0].integer;
3084 }
3085 else
3086 {
3087 /*
3088 * Got a job URI; parse it to get the job ID...
3089 */
3090
c0341b41 3091 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1d2c70a6 3092
3093 if (strncmp(resource, "/jobs/", 6) != 0)
3094 {
3095 /*
3096 * Not a valid URI!
3097 */
3098
5ea8888e 3099 LogMessage(L_ERROR, "get_job_attrs: bad job-uri attribute \'%s\'!\n",
bd84e0d1 3100 uri->values[0].string.text);
1d2c70a6 3101 send_ipp_error(con, IPP_BAD_REQUEST);
3102 return;
3103 }
3104
3105 jobid = atoi(resource + 6);
3106 }
3107
3108 /*
3109 * See if the job exists...
3110 */
3111
3112 if ((job = FindJob(jobid)) == NULL)
3113 {
3114 /*
3115 * Nope - return a "not found" error...
3116 */
3117
5ea8888e 3118 LogMessage(L_ERROR, "get_job_attrs: job #%d doesn't exist!", jobid);
1d2c70a6 3119 send_ipp_error(con, IPP_NOT_FOUND);
3120 return;
3121 }
3122
782359ca 3123 /*
3124 * Put out the standard attributes...
3125 */
3126
99de6da0 3127#ifdef AF_INET6
3128 if (con->http.hostaddr.addr.sa_family == AF_INET6)
3129 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d",
3130 ServerName, ntohs(con->http.hostaddr.ipv6.sin6_port),
3131 job->id);
3132 else
3133#endif /* AF_INET6 */
a6988fb1 3134 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d",
99de6da0 3135 ServerName, ntohs(con->http.hostaddr.ipv4.sin_port),
a6988fb1 3136 job->id);
782359ca 3137
782359ca 3138 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
3139
782359ca 3140 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
3141 "job-more-info", NULL, job_uri);
3142
782359ca 3143 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
3144 "job-uri", NULL, job_uri);
3145
7ebf3a09 3146 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
0ccccc99 3147 "job-printer-up-time", time(NULL));
7ebf3a09 3148
1d2c70a6 3149 /*
d6de4648 3150 * Copy the job attributes to the response using the requested-attributes
1d2c70a6 3151 * attribute that may be provided by the client.
3152 */
3153
27d555e8 3154 requested = ippFindAttribute(con->request, "requested-attributes",
3155 IPP_TAG_KEYWORD);
3156
3157 copy_attrs(con->response, job->attrs, requested, IPP_TAG_JOB);
1d2c70a6 3158
7ebf3a09 3159 add_job_state_reasons(con, job);
3160
27d555e8 3161 if (requested != NULL)
1049abbe 3162 con->response->request.status.status_code = IPP_OK_SUBST;
3163 else
3164 con->response->request.status.status_code = IPP_OK;
e31bfb6e 3165}
3166
3167
bd84e0d1 3168/*
3169 * 'get_ppds()' - Get the list of PPD files on the local system.
3170 */
3171
3172static void
3173get_ppds(client_t *con) /* I - Client connection */
3174{
dbb05cda 3175 LogMessage(L_DEBUG2, "get_ppds(%d)\n", con->http.fd);
3176
bd84e0d1 3177 /*
3178 * Copy the PPD attributes to the response using the requested-attributes
3179 * attribute that may be provided by the client.
3180 */
3181
3182 copy_attrs(con->response, PPDs,
3183 ippFindAttribute(con->request, "requested-attributes",
4979ce3d 3184 IPP_TAG_KEYWORD), IPP_TAG_ZERO);
bd84e0d1 3185
3186 con->response->request.status.status_code = IPP_OK;
3187}
3188
3189
1d2c70a6 3190/*
3191 * 'get_printer_attrs()' - Get printer attributes.
3192 */
3193
3194static void
3195get_printer_attrs(client_t *con, /* I - Client connection */
3196 ipp_attribute_t *uri) /* I - Printer URI */
3197{
1049abbe 3198 const char *dest; /* Destination */
1d2c70a6 3199 cups_ptype_t dtype; /* Destination type (printer or class) */
3200 char method[HTTP_MAX_URI],
3201 /* Method portion of URI */
3202 username[HTTP_MAX_URI],
3203 /* Username portion of URI */
3204 host[HTTP_MAX_URI],
3205 /* Host portion of URI */
3206 resource[HTTP_MAX_URI];
3207 /* Resource portion of URI */
3208 int port; /* Port portion of URI */
cbbfcc63 3209 printer_t *printer; /* Printer/class */
f3d580b9 3210 time_t curtime; /* Current time */
1d2c70a6 3211
3212
dbb05cda 3213 LogMessage(L_DEBUG2, "get_printer_attrs(%d, %s)\n", con->http.fd,
9b4f824a 3214 uri->values[0].string.text);
1d2c70a6 3215
3216 /*
3217 * Is the destination valid?
3218 */
3219
c0341b41 3220 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1d2c70a6 3221
ed3e11d8 3222 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
1d2c70a6 3223 {
3224 /*
3225 * Bad URI...
3226 */
3227
5ea8888e 3228 LogMessage(L_ERROR, "get_printer_attrs: resource name \'%s\' no good!", resource);
1d2c70a6 3229 send_ipp_error(con, IPP_NOT_FOUND);
3230 return;
3231 }
3232
1d2c70a6 3233 if (dtype == CUPS_PRINTER_CLASS)
cbbfcc63 3234 printer = FindClass(dest);
1d2c70a6 3235 else
3236 printer = FindPrinter(dest);
3237
f3d580b9 3238 curtime = time(NULL);
3239
d6de4648 3240 /*
3241 * Copy the printer attributes to the response using requested-attributes
3242 * and document-format attributes that may be provided by the client.
3243 */
3244
96df88bb 3245 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
3246 printer->state);
3247
7ebf3a09 3248 add_printer_state_reasons(con, printer);
3249
96df88bb 3250 if (printer->state_message[0])
3251 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
3252 "printer-state-message", NULL, printer->state_message);
3253
f3d580b9 3254 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
3255 printer->accepting);
3256
3257 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
a3e17a89 3258 "printer-up-time", curtime);
f3d580b9 3259 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
3260 ippTimeToDate(curtime));
3261
7ebf3a09 3262 add_queued_job_count(con, printer);
3263
a3e17a89 3264 copy_attrs(con->response, printer->attrs,
3265 ippFindAttribute(con->request, "requested-attributes",
3266 IPP_TAG_KEYWORD), IPP_TAG_ZERO);
3267
d6de4648 3268 con->response->request.status.status_code = IPP_OK;
1d2c70a6 3269}
3270
3271
3272/*
27d555e8 3273 * 'get_printers()' - Get a list of printers or classes.
7ebf3a09 3274 */
3275
3276static void
3277get_printers(client_t *con, /* I - Client connection */
3278 int type) /* I - 0 or CUPS_PRINTER_CLASS */
3279{
27d555e8 3280 ipp_attribute_t *attr, /* Current attribute */
3281 *requested; /* Requested attributes */
7ebf3a09 3282 int limit; /* Maximum number of printers to return */
3283 int count; /* Number of printers that match */
3284 printer_t *printer; /* Current printer pointer */
3285 time_t curtime; /* Current time */
a3e17a89 3286 int printer_type, /* printer-type attribute */
3287 printer_mask; /* printer-type-mask attribute */
3288 char *location; /* Location string */
753453e4 3289 char name[IPP_MAX_NAME],
3290 /* Printer name */
3291 *nameptr; /* Pointer into name */
3292 printer_t *iclass; /* Implicit class */
7ebf3a09 3293
3294
a0845ca0 3295 LogMessage(L_DEBUG2, "get_printers(%d, %x)\n", con->http.fd, type);
7ebf3a09 3296
3297 /*
a3e17a89 3298 * See if they want to limit the number of printers reported...
7ebf3a09 3299 */
3300
3301 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
3302 limit = attr->values[0].integer;
3303 else
a3e17a89 3304 limit = 10000000;
3305
3306 /*
3307 * Support filtering...
3308 */
3309
3310 if ((attr = ippFindAttribute(con->request, "printer-type", IPP_TAG_ENUM)) != NULL)
3311 printer_type = attr->values[0].integer;
3312 else
3313 printer_type = 0;
3314
3315 if ((attr = ippFindAttribute(con->request, "printer-type-mask", IPP_TAG_ENUM)) != NULL)
3316 printer_mask = attr->values[0].integer;
3317 else
3318 printer_mask = 0;
3319
3320 if ((attr = ippFindAttribute(con->request, "location", IPP_TAG_TEXT)) != NULL)
3321 location = attr->values[0].string.text;
3322 else
3323 location = NULL;
7ebf3a09 3324
27d555e8 3325 requested = ippFindAttribute(con->request, "requested-attributes",
3326 IPP_TAG_KEYWORD);
3327
7ebf3a09 3328 /*
3329 * OK, build a list of printers for this printer...
3330 */
3331
3332 curtime = time(NULL);
3333
3334 for (count = 0, printer = Printers;
3335 count < limit && printer != NULL;
3336 printer = printer->next)
a3e17a89 3337 if ((printer->type & CUPS_PRINTER_CLASS) == type &&
3338 (printer->type & printer_mask) == printer_type &&
3339 (location == NULL || strcasecmp(printer->location, location) == 0))
7ebf3a09 3340 {
753453e4 3341 /*
3342 * If HideImplicitMembers is enabled, see if this printer or class
3343 * is a member of an implicit class...
3344 */
3345
3346 if (ImplicitClasses && HideImplicitMembers &&
3347 (printer->type & CUPS_PRINTER_REMOTE))
3348 {
3349 /*
3350 * Make a copy of the printer name...
3351 *
3352 * Note: name and printer->name are both IPP_MAX_NAME characters
3353 * in size, so strcpy() is safe...
3354 */
3355
3356 strcpy(name, printer->name);
3357
3358 if ((nameptr = strchr(name, '@')) != NULL)
3359 {
3360 /*
3361 * Strip trailing @server...
3362 */
3363
3364 *nameptr = '\0';
3365
3366 /*
3367 * Find the core printer, if any...
3368 */
3369
3370 if ((iclass = FindPrinter(name)) != NULL &&
3371 (iclass->type & CUPS_PRINTER_IMPLICIT))
3372 continue;
3373 }
3374 }
3375
3376 /*
3377 * Add the group separator as needed...
3378 */
3379
972c5ace 3380 if (count > 0)
3381 ippAddSeparator(con->response);
3382
3383 count ++;
3384
7ebf3a09 3385 /*
3386 * Send the following attributes for each printer:
3387 *
3388 * printer-state
3389 * printer-state-message
3390 * printer-is-accepting-jobs
3391 * + all printer attributes
3392 */
3393
3394 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
3395 "printer-state", printer->state);
3396
3397 add_printer_state_reasons(con, printer);
3398
3399 if (printer->state_message[0])
3400 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
3401 "printer-state-message", NULL, printer->state_message);
3402
3403 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
3404 printer->accepting);
3405
3406 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
a3e17a89 3407 "printer-up-time", curtime);
7ebf3a09 3408 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
3409 ippTimeToDate(curtime));
3410
3411 add_queued_job_count(con, printer);
3412
27d555e8 3413 copy_attrs(con->response, printer->attrs, requested, IPP_TAG_ZERO);
7ebf3a09 3414 }
3415
3416 con->response->request.status.status_code = IPP_OK;
3417}
3418
3419
3420/*
3421 * 'hold_job()' - Hold a print job.
1d2c70a6 3422 */
3423
3424static void
bd84e0d1 3425hold_job(client_t *con, /* I - Client connection */
3426 ipp_attribute_t *uri) /* I - Job or Printer URI */
e31bfb6e 3427{
5856eaaf 3428 ipp_attribute_t *attr, /* Current job-hold-until */
3429 *newattr; /* New job-hold-until */
bd84e0d1 3430 int jobid; /* Job ID */
3431 char method[HTTP_MAX_URI],
e31bfb6e 3432 /* Method portion of URI */
3433 username[HTTP_MAX_URI],
3434 /* Username portion of URI */
3435 host[HTTP_MAX_URI],
3436 /* Host portion of URI */
3437 resource[HTTP_MAX_URI];
3438 /* Resource portion of URI */
3439 int port; /* Port portion of URI */
bd84e0d1 3440 job_t *job; /* Job information */
e31bfb6e 3441
3442
dbb05cda 3443 LogMessage(L_DEBUG2, "hold_job(%d, %s)\n", con->http.fd,
3444 uri->values[0].string.text);
e31bfb6e 3445
2aeb2b1d 3446 /*
3447 * Verify that the POST operation was done to a valid URI.
3448 */
3449
3450 if (strncmp(con->uri, "/classes/", 9) != 0 &&
bd84e0d1 3451 strncmp(con->uri, "/jobs/", 5) != 0 &&
2aeb2b1d 3452 strncmp(con->uri, "/printers/", 10) != 0)
3453 {
5ea8888e 3454 LogMessage(L_ERROR, "hold_job: hold request on bad resource \'%s\'!",
1124e9ec 3455 con->uri);
2aeb2b1d 3456 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3457 return;
3458 }
3459
e31bfb6e 3460 /*
bd84e0d1 3461 * See if we have a job URI or a printer URI...
e31bfb6e 3462 */
3463
bd84e0d1 3464 if (strcmp(uri->name, "printer-uri") == 0)
e31bfb6e 3465 {
4791a466 3466 /*
bd84e0d1 3467 * Got a printer URI; see if we also have a job-id attribute...
4791a466 3468 */
e31bfb6e 3469
bd84e0d1 3470 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4791a466 3471 {
5ea8888e 3472 LogMessage(L_ERROR, "hold_job: got a printer-uri attribute but no job-id!");
4791a466 3473 send_ipp_error(con, IPP_BAD_REQUEST);
3474 return;
3475 }
4791a466 3476
bd84e0d1 3477 jobid = attr->values[0].integer;
e31bfb6e 3478 }
bd84e0d1 3479 else
e31bfb6e 3480 {
782359ca 3481 /*
bd84e0d1 3482 * Got a job URI; parse it to get the job ID...
782359ca 3483 */
3484
bd84e0d1 3485 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3486
3487 if (strncmp(resource, "/jobs/", 6) != 0)
c6e90b24 3488 {
3489 /*
bd84e0d1 3490 * Not a valid URI!
3491 */
3492
5ea8888e 3493 LogMessage(L_ERROR, "hold_job: bad job-uri attribute \'%s\'!",
bd84e0d1 3494 uri->values[0].string.text);
3495 send_ipp_error(con, IPP_BAD_REQUEST);
3496 return;
3497 }
3498
3499 jobid = atoi(resource + 6);
3500 }
3501
3502 /*
3503 * See if the job exists...
3504 */
3505
3506 if ((job = FindJob(jobid)) == NULL)
3507 {
3508 /*
3509 * Nope - return a "not found" error...
3510 */
3511
5ea8888e 3512 LogMessage(L_ERROR, "hold_job: job #%d doesn't exist!", jobid);
bd84e0d1 3513 send_ipp_error(con, IPP_NOT_FOUND);
3514 return;
3515 }
3516
3517 /*
3518 * See if the job is owned by the requesting user...
3519 */
3520
ed3e11d8 3521 if (!validate_user(con, job->username, username, sizeof(username)))
bd84e0d1 3522 {
ed3e11d8 3523 LogMessage(L_ERROR, "hold_job: \"%s\" not authorized to hold job id %d owned by \"%s\"!",
3524 username, jobid, job->username);
3525 send_ipp_error(con, IPP_FORBIDDEN);
3526 return;
bd84e0d1 3527 }
3528
3529 /*
3530 * Hold the job and return...
3531 */
3532
3533 HoldJob(jobid);
3534
5856eaaf 3535 if ((newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
3536 newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
3537
e583bc2d 3538 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
3539 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
3540
5856eaaf 3541 if (attr != NULL)
e583bc2d 3542 {
5856eaaf 3543 /*
3544 * Free the old hold value and copy the new one over...
3545 */
3546
3547 free(attr->values[0].string.text);
3548
3549 if (newattr != NULL)
3550 {
3551 attr->value_tag = newattr->value_tag;
3552 attr->values[0].string.text = strdup(newattr->values[0].string.text);
3553 }
3554 else
3555 {
3556 attr->value_tag = IPP_TAG_KEYWORD;
3557 attr->values[0].string.text = strdup("indefinite");
3558 }
3559
a6988fb1 3560 /*
3561 * Hold job until specified time...
3562 */
e583bc2d 3563
a6988fb1 3564 SetJobHoldUntil(job->id, attr->values[0].string.text);
3565 }
e583bc2d 3566
c8f336b5 3567 LogMessage(L_INFO, "Job %d was held by \'%s\'.", jobid, username);
bd84e0d1 3568
3569 con->response->request.status.status_code = IPP_OK;
3570}
3571
3572
a3e17a89 3573/*
753453e4 3574 * 'move_job()' - Move a job to a new destination.
a3e17a89 3575 */
3576
3577static void
3578move_job(client_t *con, /* I - Client connection */
3579 ipp_attribute_t *uri) /* I - Job URI */
3580{
a3e17a89 3581 ipp_attribute_t *attr; /* Current attribute */
3582 int jobid; /* Job ID */
3583 job_t *job; /* Current job */
3584 const char *dest; /* Destination */
3585 cups_ptype_t dtype; /* Destination type (printer or class) */
3586 char method[HTTP_MAX_URI],
3587 /* Method portion of URI */
3588 username[HTTP_MAX_URI],
3589 /* Username portion of URI */
3590 host[HTTP_MAX_URI],
3591 /* Host portion of URI */
3592 resource[HTTP_MAX_URI];
3593 /* Resource portion of URI */
3594 int port; /* Port portion of URI */
a3e17a89 3595
3596
dbb05cda 3597 LogMessage(L_DEBUG2, "move_job(%d, %s)\n", con->http.fd,
3598 uri->values[0].string.text);
a3e17a89 3599
3600 /*
3601 * See if we have a job URI or a printer URI...
3602 */
3603
3604 if (strcmp(uri->name, "printer-uri") == 0)
3605 {
3606 /*
3607 * Got a printer URI; see if we also have a job-id attribute...
3608 */
3609
3610 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
3611 {
3612 LogMessage(L_ERROR, "move_job: got a printer-uri attribute but no job-id!");
3613 send_ipp_error(con, IPP_BAD_REQUEST);
3614 return;
3615 }
3616
3617 jobid = attr->values[0].integer;
3618 }
3619 else
3620 {
3621 /*
3622 * Got a job URI; parse it to get the job ID...
3623 */
3624
3625 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3626
3627 if (strncmp(resource, "/jobs/", 6) != 0)
3628 {
3629 /*
3630 * Not a valid URI!
3631 */
3632
3633 LogMessage(L_ERROR, "move_job: bad job-uri attribute \'%s\'!\n",
3634 uri->values[0].string.text);
3635 send_ipp_error(con, IPP_BAD_REQUEST);
3636 return;
3637 }
3638
3639 jobid = atoi(resource + 6);
3640 }
3641
3642 /*
3643 * See if the job exists...
3644 */
3645
3646 if ((job = FindJob(jobid)) == NULL)
3647 {
3648 /*
3649 * Nope - return a "not found" error...
3650 */
3651
3652 LogMessage(L_ERROR, "move_job: job #%d doesn't exist!", jobid);
3653 send_ipp_error(con, IPP_NOT_FOUND);
3654 return;
3655 }
3656
3657 /*
3658 * See if the job has been completed...
3659 */
3660
3661 if (job->state->values[0].integer > IPP_JOB_STOPPED)
3662 {
3663 /*
3664 * Return a "not-possible" error...
3665 */
3666
3667 LogMessage(L_ERROR, "move_job: job #%d is finished and cannot be altered!", jobid);
3668 send_ipp_error(con, IPP_NOT_POSSIBLE);
3669 return;
3670 }
3671
3672 /*
3673 * See if the job is owned by the requesting user...
3674 */
3675
ed3e11d8 3676 if (!validate_user(con, job->username, username, sizeof(username)))
a3e17a89 3677 {
ed3e11d8 3678 LogMessage(L_ERROR, "move_job: \"%s\" not authorized to move job id %d owned by \"%s\"!",
3679 username, jobid, job->username);
3680 send_ipp_error(con, IPP_FORBIDDEN);
3681 return;
a3e17a89 3682 }
3683
3684 if ((attr = ippFindAttribute(con->request, "job-printer-uri", IPP_TAG_URI)) == NULL)
3685 {
3686 /*
3687 * Need job-printer-uri...
3688 */
3689
3690 LogMessage(L_ERROR, "move_job: job-printer-uri attribute missing!");
3691 send_ipp_error(con, IPP_BAD_REQUEST);
3692 return;
3693 }
3694
3695 /*
3696 * Move the job to a different printer or class...
3697 */
3698
3699 httpSeparate(attr->values[0].string.text, method, username, host, &port,
3700 resource);
ed3e11d8 3701 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
a3e17a89 3702 {
3703 /*
3704 * Bad URI...
3705 */
3706
3707 LogMessage(L_ERROR, "move_job: resource name \'%s\' no good!", resource);
3708 send_ipp_error(con, IPP_NOT_FOUND);
3709 return;
3710 }
3711
3712 MoveJob(jobid, dest);
3713
3714 /*
3715 * Start jobs if possible...
3716 */
3717
3718 CheckJobs();
3719
3720 /*
3721 * Return with "everything is OK" status...
3722 */
3723
3724 con->response->request.status.status_code = IPP_OK;
3725}
3726
3727
bd84e0d1 3728/*
3729 * 'print_job()' - Print a file to a printer or class.
3730 */
3731
3732static void
3733print_job(client_t *con, /* I - Client connection */
3734 ipp_attribute_t *uri) /* I - Printer URI */
3735{
3736 ipp_attribute_t *attr; /* Current attribute */
3737 ipp_attribute_t *format; /* Document-format attribute */
1049abbe 3738 const char *dest; /* Destination */
bd84e0d1 3739 cups_ptype_t dtype; /* Destination type (printer or class) */
3740 int priority; /* Job priority */
3741 char *title; /* Job name/title */
3742 job_t *job; /* Current job */
3743 char job_uri[HTTP_MAX_URI],
3744 /* Job URI */
1049abbe 3745 printer_uri[HTTP_MAX_URI],
3746 /* Printer URI */
bd84e0d1 3747 method[HTTP_MAX_URI],
3748 /* Method portion of URI */
3749 username[HTTP_MAX_URI],
3750 /* Username portion of URI */
3751 host[HTTP_MAX_URI],
3752 /* Host portion of URI */
3753 resource[HTTP_MAX_URI],
3754 /* Resource portion of URI */
3755 filename[1024]; /* Job filename */
3756 int port; /* Port portion of URI */
3757 mime_type_t *filetype; /* Type of file */
3758 char super[MIME_MAX_SUPER],
3759 /* Supertype of file */
3760 type[MIME_MAX_TYPE],
3761 /* Subtype of file */
3762 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
3763 /* Textual name of mime type */
3764 printer_t *printer; /* Printer data */
91606789 3765 struct stat fileinfo; /* File information */
b521f3fc 3766 int kbytes; /* Size of file */
bd84e0d1 3767
3768
dbb05cda 3769 LogMessage(L_DEBUG2, "print_job(%d, %s)\n", con->http.fd,
3770 uri->values[0].string.text);
bd84e0d1 3771
3772 /*
3773 * Verify that the POST operation was done to a valid URI.
3774 */
3775
3776 if (strncmp(con->uri, "/classes/", 9) != 0 &&
3777 strncmp(con->uri, "/printers/", 10) != 0)
3778 {
5ea8888e 3779 LogMessage(L_ERROR, "print_job: cancel request on bad resource \'%s\'!",
bd84e0d1 3780 con->uri);
3781 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3782 return;
3783 }
3784
3785 /*
3786 * OK, see if the client is sending the document compressed - CUPS
3787 * doesn't support compression yet...
3788 */
3789
7ebf3a09 3790 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL &&
3791 strcmp(attr->values[0].string.text, "none") == 0)
bd84e0d1 3792 {
7ebf3a09 3793 LogMessage(L_ERROR, "print_job: Unsupported compression attribute %s!",
3794 attr->values[0].string.text);
bd84e0d1 3795 send_ipp_error(con, IPP_ATTRIBUTES);
3796 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
3797 "compression", NULL, attr->values[0].string.text);
3798 return;
3799 }
3800
3801 /*
3802 * Do we have a file to print?
3803 */
3804
3805 if (con->filename[0] == '\0')
3806 {
5ea8888e 3807 LogMessage(L_ERROR, "print_job: No file!?!");
bd84e0d1 3808 send_ipp_error(con, IPP_BAD_REQUEST);
3809 return;
3810 }
3811
3812 /*
3813 * Is it a format we support?
3814 */
3815
3816 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
3817 {
3818 /*
3819 * Grab format from client...
3820 */
3821
3822 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
3823 {
5ea8888e 3824 LogMessage(L_ERROR, "print_job: could not scan type \'%s\'!",
bd84e0d1 3825 format->values[0].string.text);
3826 send_ipp_error(con, IPP_BAD_REQUEST);
3827 return;
3828 }
3829 }
3830 else
3831 {
3832 /*
3833 * No document format attribute? Auto-type it!
3834 */
3835
3836 strcpy(super, "application");
3837 strcpy(type, "octet-stream");
3838 }
3839
3840 if (strcmp(super, "application") == 0 &&
3841 strcmp(type, "octet-stream") == 0)
3842 {
3843 /*
3844 * Auto-type the file...
3845 */
3846
5ea8888e 3847 LogMessage(L_DEBUG, "print_job: auto-typing file...");
bd84e0d1 3848
3849 filetype = mimeFileType(MimeDatabase, con->filename);
3850
3851 if (filetype != NULL)
3852 {
3853 /*
3854 * Replace the document-format attribute value with the auto-typed one.
c6e90b24 3855 */
782359ca 3856
a6988fb1 3857 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
3858 filetype->type);
4791a466 3859
3860 if (format != NULL)
3861 {
3862 free(format->values[0].string.text);
3863 format->values[0].string.text = strdup(mimetype);
3864 }
3865 else
3866 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
3867 "document-format", NULL, mimetype);
c6e90b24 3868 }
526462ee 3869 else
3870 filetype = mimeType(MimeDatabase, super, type);
e31bfb6e 3871 }
3872 else
3873 filetype = mimeType(MimeDatabase, super, type);
3874
3875 if (filetype == NULL)
3876 {
e93faa10 3877 LogMessage(L_ERROR, "print_job: Unsupported format \'%s/%s\'!",
5fa65e01 3878 super, type);
1d2c70a6 3879 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
e93faa10 3880
3881 if (format)
3882 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
3883 "document-format", NULL, format->values[0].string.text);
3884
e31bfb6e 3885 return;
3886 }
3887
5ea8888e 3888 LogMessage(L_DEBUG, "print_job: request file type is %s/%s.",
bd84e0d1 3889 filetype->super, filetype->type);
e31bfb6e 3890
3891 /*
3892 * Is the destination valid?
3893 */
3894
c0341b41 3895 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
e31bfb6e 3896
ed3e11d8 3897 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
e31bfb6e 3898 {
3899 /*
3900 * Bad URI...
3901 */
3902
5ea8888e 3903 LogMessage(L_ERROR, "print_job: resource name \'%s\' no good!", resource);
1d2c70a6 3904 send_ipp_error(con, IPP_NOT_FOUND);
e31bfb6e 3905 return;
3906 }
3907
f3d580b9 3908 /*
3909 * See if the printer is accepting jobs...
3910 */
3911
3912 if (dtype == CUPS_PRINTER_CLASS)
1049abbe 3913 {
cbbfcc63 3914 printer = FindClass(dest);
99de6da0 3915#ifdef AF_INET6
3916 if (con->http.hostaddr.addr.sa_family == AF_INET6)
3917 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/classes/%s",
3918 ServerName, ntohs(con->http.hostaddr.ipv6.sin6_port), dest);
3919 else
3920#endif /* AF_INET6 */
a6988fb1 3921 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/classes/%s",
99de6da0 3922 ServerName, ntohs(con->http.hostaddr.ipv4.sin_port), dest);
1049abbe 3923 }
f3d580b9 3924 else
1049abbe 3925 {
f3d580b9 3926 printer = FindPrinter(dest);
3927
99de6da0 3928#ifdef AF_INET6
3929 if (con->http.hostaddr.addr.sa_family == AF_INET6)
3930 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/printers/%s",
3931 ServerName, ntohs(con->http.hostaddr.ipv6.sin6_port), dest);
3932 else
3933#endif /* AF_INET6 */
a6988fb1 3934 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/printers/%s",
99de6da0 3935 ServerName, ntohs(con->http.hostaddr.ipv4.sin_port), dest);
1049abbe 3936 }
3937
cbbfcc63 3938 if (!printer->accepting)
3939 {
5ea8888e 3940 LogMessage(L_INFO, "print_job: destination \'%s\' is not accepting jobs.",
1917e0e9 3941 dest);
cbbfcc63 3942 send_ipp_error(con, IPP_NOT_ACCEPTING);
3943 return;
f3d580b9 3944 }
3945
d7845573 3946 /*
3947 * Make sure we aren't over our limit...
3948 */
3949
3950 if (NumJobs >= MaxJobs && MaxJobs)
3951 CleanJobs();
3952
3953 if (NumJobs >= MaxJobs && MaxJobs)
3954 {
3955 LogMessage(L_INFO, "print_job: too many jobs.");
3956 send_ipp_error(con, IPP_NOT_POSSIBLE);
3957 return;
3958 }
3959
b521f3fc 3960 if (!check_quotas(con, printer))
3961 {
3962 send_ipp_error(con, IPP_NOT_POSSIBLE);
3963 return;
3964 }
3965
e31bfb6e 3966 /*
3967 * Create the job and set things up...
3968 */
3969
c0341b41 3970 if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
e31bfb6e 3971 priority = attr->values[0].integer;
3972 else
1049abbe 3973 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
3974 priority = 50);
e31bfb6e 3975
27b6a264 3976 if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
3977 title = attr->values[0].string.text;
3978 else
1049abbe 3979 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
3980 title = "Untitled");
27b6a264 3981
cc0561c6 3982 if ((job = AddJob(priority, printer->name)) == NULL)
e31bfb6e 3983 {
5ea8888e 3984 LogMessage(L_ERROR, "print_job: unable to add job for destination \'%s\'!",
1917e0e9 3985 dest);
e31bfb6e 3986 send_ipp_error(con, IPP_INTERNAL_ERROR);
3987 return;
3988 }
3989
1049abbe 3990 job->dtype = dtype;
3991 job->attrs = con->request;
3992 con->request = NULL;
e31bfb6e 3993
a3e17a89 3994 /*
3995 * Copy the rest of the job info...
3996 */
3997
970017a4 3998 strncpy(job->title, title, sizeof(job->title) - 1);
3999
17b95e13 4000 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
4001
4002 if (con->username[0])
a6988fb1 4003 {
4004 strncpy(job->username, con->username, sizeof(job->username) - 1);
4005 job->username[sizeof(job->username) - 1] = '\0';
4006 }
17b95e13 4007 if (attr != NULL)
e31bfb6e 4008 {
5ea8888e 4009 LogMessage(L_DEBUG, "print_job: requesting-user-name = \'%s\'",
bd84e0d1 4010 attr->values[0].string.text);
1d2c70a6 4011
c0341b41 4012 strncpy(job->username, attr->values[0].string.text, sizeof(job->username) - 1);
e31bfb6e 4013 job->username[sizeof(job->username) - 1] = '\0';
4014 }
17b95e13 4015 else
1049abbe 4016 strcpy(job->username, "anonymous");
4017
4018 if (attr == NULL)
4019 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
4020 NULL, job->username);
4021 else
4022 {
4979ce3d 4023 attr->group_tag = IPP_TAG_JOB;
1049abbe 4024 free(attr->name);
4025 attr->name = strdup("job-originating-user-name");
4026 }
e31bfb6e 4027
1049abbe 4028 /*
4029 * Add remaining job attributes...
4030 */
4031
bb27c189 4032 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
4033 "job-originating-host-name", NULL, con->http.hostname);
1049abbe 4034 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
4035 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
cd4e71e5 4036 "job-state", IPP_JOB_PENDING);
aa0cb334 4037 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
4038 "job-media-sheets-completed", 0);
1049abbe 4039 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
4040 printer_uri);
100cd8c8 4041 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
4042 title);
1049abbe 4043
91606789 4044 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) == NULL)
4045 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
4046 "job-k-octets", 0);
4047
b521f3fc 4048 if (stat(con->filename, &fileinfo))
4049 kbytes = 0;
4050 else
4051 kbytes = (fileinfo.st_size + 1023) / 1024;
4052
4053 UpdateQuota(printer, job->username, 0, kbytes);
4054 attr->values[0].integer += kbytes;
91606789 4055
7ebf3a09 4056 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
0ccccc99 4057 time(NULL));
7ebf3a09 4058 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
4059 "time-at-processing", 0);
4060 attr->value_tag = IPP_TAG_NOVALUE;
4061 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
4062 "time-at-completed", 0);
4063 attr->value_tag = IPP_TAG_NOVALUE;
4064
e583bc2d 4065 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4066 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4104c7ec 4067 if (attr == NULL)
4068 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
4069 "job-hold-until", NULL, "no-hold");
e583bc2d 4070
753453e4 4071 if (attr != NULL && strcmp(attr->values[0].string.text, "no-hold") != 0 &&
4072 !(printer->type & CUPS_PRINTER_REMOTE))
e583bc2d 4073 {
4074 /*
4075 * Hold job until specified time...
4076 */
4077
4078 job->state->values[0].integer = IPP_JOB_HELD;
4079 SetJobHoldUntil(job->id, attr->values[0].string.text);
4080 }
4081
d11458ff 4082 if (!(printer->type & CUPS_PRINTER_REMOTE) || Classification[0])
e903a8f6 4083 {
4084 /*
4085 * Add job sheets options...
4086 */
4087
ebde520c 4088 if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) == NULL)
e903a8f6 4089 {
753453e4 4090 LogMessage(L_DEBUG, "Adding default job-sheets values \"%s,%s\"...",
4091 printer->job_sheets[0], printer->job_sheets[1]);
4092
e903a8f6 4093 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
4094 2, NULL, NULL);
4095 attr->values[0].string.text = strdup(printer->job_sheets[0]);
4096 attr->values[1].string.text = strdup(printer->job_sheets[1]);
4097 }
4098
b5cb0608 4099 job->job_sheets = attr;
4100
d11458ff 4101 /*
4102 * Enforce classification level if set...
4103 */
4104
4105 if (Classification[0])
4106 {
753453e4 4107 if (ClassifyOverride)
4108 {
4109 if (strcmp(attr->values[0].string.text, "none") == 0 &&
4110 (attr->num_values == 1 ||
4111 strcmp(attr->values[1].string.text, "none") == 0))
4112 {
4113 /*
4114 * Force the leading banner to have the classification on it...
4115 */
4116
4117 free(attr->values[0].string.text);
4118 attr->values[0].string.text = strdup(Classification);
4119 }
4120 else if (attr->num_values == 2 &&
4121 strcmp(attr->values[0].string.text, attr->values[1].string.text) != 0 &&
4122 strcmp(attr->values[0].string.text, "none") != 0 &&
4123 strcmp(attr->values[1].string.text, "none") != 0)
4124 {
4125 /*
4126 * Can't put two different security markings on the same document!
4127 */
4128
4129 free(attr->values[1].string.text);
4130 attr->values[1].string.text = strdup(attr->values[0].string.text);
4131 }
4132 }
4133 else if (strcmp(attr->values[0].string.text, Classification) != 0 &&
4134 (attr->num_values == 1 ||
4135 strcmp(attr->values[1].string.text, Classification) != 0))
d11458ff 4136 {
4137 /*
4138 * Force the leading banner to have the classification on it...
4139 */
4140
4141 free(attr->values[0].string.text);
4142 attr->values[0].string.text = strdup(Classification);
4143 }
4144 }
4145
e903a8f6 4146 /*
4147 * Add the starting sheet...
4148 */
4149
b521f3fc 4150 kbytes = copy_banner(con, job, attr->values[0].string.text);
4151
4152 UpdateQuota(printer, job->username, 0, kbytes);
e903a8f6 4153 }
753453e4 4154 else if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
4155 job->sheets = attr;
e903a8f6 4156
4157 /*
4158 * Add the job file...
4159 */
4160
4161 if (add_file(con, job, filetype))
4162 return;
4163
a6988fb1 4164 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
4165 job->num_files);
e903a8f6 4166 rename(con->filename, filename);
85ef1f5c 4167 con->filename[0] = '\0';
e903a8f6 4168
4169 /*
4170 * See if we need to add the ending sheet...
4171 */
4172
4173 if (!(printer->type & CUPS_PRINTER_REMOTE) && attr->num_values > 1)
4174 {
4175 /*
4176 * Yes...
4177 */
4178
b521f3fc 4179 kbytes = copy_banner(con, job, attr->values[1].string.text);
4180 UpdateQuota(printer, job->username, 0, kbytes);
e903a8f6 4181 }
4182
4183 /*
4184 * Log and save the job...
4185 */
4186
4187 LogMessage(L_INFO, "Job %d queued on \'%s\' by \'%s\'.", job->id,
4188 job->dest, job->username);
7a91f14b 4189 LogMessage(L_DEBUG, "Job %d hold_until = %d", job->id, job->hold_until);
e903a8f6 4190
4979ce3d 4191 SaveJob(job->id);
4192
e31bfb6e 4193 /*
4194 * Start the job if possible...
4195 */
4196
4197 CheckJobs();
4198
4199 /*
4200 * Fill in the response info...
4201 */
4202
99de6da0 4203#ifdef AF_INET6
4204 if (con->http.hostaddr.addr.sa_family == AF_INET6)
4205 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
4206 ntohs(con->http.hostaddr.ipv6.sin6_port), job->id);
4207 else
4208#endif /* AF_INET6 */
a6988fb1 4209 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
99de6da0 4210 ntohs(con->http.hostaddr.ipv4.sin_port), job->id);
4211
bd84e0d1 4212 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
e31bfb6e 4213
bd84e0d1 4214 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
e31bfb6e 4215
1049abbe 4216 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
4217 job->state->values[0].integer);
7ebf3a09 4218 add_job_state_reasons(con, job);
e31bfb6e 4219
4220 con->response->request.status.status_code = IPP_OK;
4221}
4222
4223
f3d580b9 4224/*
4225 * 'reject_jobs()' - Reject print jobs to a printer.
4226 */
4227
4228static void
4229reject_jobs(client_t *con, /* I - Client connection */
4230 ipp_attribute_t *uri) /* I - Printer or class URI */
4231{
4232 cups_ptype_t dtype; /* Destination type (printer or class) */
4233 char method[HTTP_MAX_URI],
4234 /* Method portion of URI */
4235 username[HTTP_MAX_URI],
4236 /* Username portion of URI */
4237 host[HTTP_MAX_URI],
4238 /* Host portion of URI */
4239 resource[HTTP_MAX_URI];
4240 /* Resource portion of URI */
4241 int port; /* Port portion of URI */
1049abbe 4242 const char *name; /* Printer name */
f3d580b9 4243 printer_t *printer; /* Printer data */
4244 ipp_attribute_t *attr; /* printer-state-message text */
4245
4246
dbb05cda 4247 LogMessage(L_DEBUG2, "reject_jobs(%d, %s)\n", con->http.fd,
4248 uri->values[0].string.text);
f3d580b9 4249
4250 /*
4251 * Was this operation called from the correct URI?
4252 */
4253
4254 if (strncmp(con->uri, "/admin/", 7) != 0)
4255 {
5ea8888e 4256 LogMessage(L_ERROR, "reject_jobs: admin request on bad resource \'%s\'!",
1124e9ec 4257 con->uri);
f3d580b9 4258 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4259 return;
4260 }
4261
4262 /*
4263 * Is the destination valid?
4264 */
4265
4266 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4267
ed3e11d8 4268 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
f3d580b9 4269 {
4270 /*
4271 * Bad URI...
4272 */
4273
5ea8888e 4274 LogMessage(L_ERROR, "reject_jobs: resource name \'%s\' no good!", resource);
f3d580b9 4275 send_ipp_error(con, IPP_NOT_FOUND);
4276 return;
4277 }
4278
4279 /*
4280 * Reject jobs sent to the printer...
4281 */
4282
cbbfcc63 4283 if (dtype == CUPS_PRINTER_CLASS)
4284 printer = FindClass(name);
4285 else
4286 printer = FindPrinter(name);
4287
f3d580b9 4288 printer->accepting = 0;
4289
4290 if ((attr = ippFindAttribute(con->request, "printer-state-message",
4291 IPP_TAG_TEXT)) == NULL)
4292 strcpy(printer->state_message, "Rejecting Jobs");
4293 else
970017a4 4294 {
4295 strncpy(printer->state_message, attr->values[0].string.text,
4296 sizeof(printer->state_message) - 1);
4297 printer->state_message[sizeof(printer->state_message) - 1] = '\0';
4298 }
f3d580b9 4299
5d99df62 4300 if (dtype == CUPS_PRINTER_CLASS)
4301 SaveAllClasses();
4302 else
4303 SaveAllPrinters();
4304
cc0561c6 4305 if (dtype == CUPS_PRINTER_CLASS)
5ea8888e 4306 LogMessage(L_INFO, "Class \'%s\' rejecting jobs (\'%s\').", name,
cc0561c6 4307 con->username);
4308 else
5ea8888e 4309 LogMessage(L_INFO, "Printer \'%s\' rejecting jobs (\'%s\').", name,
cc0561c6 4310 con->username);
4311
f3d580b9 4312 /*
4313 * Everything was ok, so return OK status...
4314 */
4315
4316 con->response->request.status.status_code = IPP_OK;
4317}
4318
4319
bd84e0d1 4320/*
f63a2256 4321 * 'release_job()' - Release a held print job.
4322 */
4323
4324static void
4325release_job(client_t *con, /* I - Client connection */
4326 ipp_attribute_t *uri) /* I - Job or Printer URI */
4327{
f63a2256 4328 ipp_attribute_t *attr; /* Current attribute */
4329 int jobid; /* Job ID */
4330 char method[HTTP_MAX_URI],
4331 /* Method portion of URI */
4332 username[HTTP_MAX_URI],
4333 /* Username portion of URI */
4334 host[HTTP_MAX_URI],
4335 /* Host portion of URI */
4336 resource[HTTP_MAX_URI];
4337 /* Resource portion of URI */
4338 int port; /* Port portion of URI */
4339 job_t *job; /* Job information */
f63a2256 4340
4341
dbb05cda 4342 LogMessage(L_DEBUG2, "release_job(%d, %s)\n", con->http.fd,
4343 uri->values[0].string.text);
f63a2256 4344
4345 /*
4346 * Verify that the POST operation was done to a valid URI.
4347 */
4348
4349 if (strncmp(con->uri, "/classes/", 9) != 0 &&
4350 strncmp(con->uri, "/jobs/", 5) != 0 &&
4351 strncmp(con->uri, "/printers/", 10) != 0)
4352 {
4353 LogMessage(L_ERROR, "release_job: release request on bad resource \'%s\'!",
4354 con->uri);
4355 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4356 return;
4357 }
4358
4359 /*
4360 * See if we have a job URI or a printer URI...
4361 */
4362
4363 if (strcmp(uri->name, "printer-uri") == 0)
4364 {
4365 /*
4366 * Got a printer URI; see if we also have a job-id attribute...
4367 */
4368
4369 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4370 {
4371 LogMessage(L_ERROR, "release_job: got a printer-uri attribute but no job-id!");
4372 send_ipp_error(con, IPP_BAD_REQUEST);
4373 return;
4374 }
4375
4376 jobid = attr->values[0].integer;
4377 }
4378 else
4379 {
4380 /*
4381 * Got a job URI; parse it to get the job ID...
4382 */
4383
4384 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4385
4386 if (strncmp(resource, "/jobs/", 6) != 0)
4387 {
4388 /*
4389 * Not a valid URI!
4390 */
4391
4392 LogMessage(L_ERROR, "release_job: bad job-uri attribute \'%s\'!",
4393 uri->values[0].string.text);
4394 send_ipp_error(con, IPP_BAD_REQUEST);
4395 return;
4396 }
4397
4398 jobid = atoi(resource + 6);
4399 }
4400
4401 /*
4402 * See if the job exists...
4403 */
4404
4405 if ((job = FindJob(jobid)) == NULL)
4406 {
4407 /*
4408 * Nope - return a "not found" error...
4409 */
4410
4411 LogMessage(L_ERROR, "release_job: job #%d doesn't exist!", jobid);
4412 send_ipp_error(con, IPP_NOT_FOUND);
4413 return;
4414 }
4415
4416 /*
4417 * See if job is "held"...
4418 */
4419
4420 if (job->state->values[0].integer != IPP_JOB_HELD)
4421 {
4422 /*
4423 * Nope - return a "not possible" error...
4424 */
4425
4426 LogMessage(L_ERROR, "release_job: job #%d is not held!", jobid);
4427 send_ipp_error(con, IPP_NOT_POSSIBLE);
4428 return;
4429 }
4430
4431 /*
4432 * See if the job is owned by the requesting user...
4433 */
4434
ed3e11d8 4435 if (!validate_user(con, job->username, username, sizeof(username)))
f63a2256 4436 {
ed3e11d8 4437 LogMessage(L_ERROR, "release_job: \"%s\" not authorized to release job id %d owned by \"%s\"!",
4438 username, jobid, job->username);
4439 send_ipp_error(con, IPP_FORBIDDEN);
4440 return;
f63a2256 4441 }
4442
5856eaaf 4443 /*
4444 * Reset the job-hold-until value to "no-hold"...
4445 */
4446
4447 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4448 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4449
4450 if (attr != NULL)
4451 {
4452 free(attr->values[0].string.text);
4453 attr->value_tag = IPP_TAG_KEYWORD;
4454 attr->values[0].string.text = strdup("no-hold");
4455 }
4456
f63a2256 4457 /*
4458 * Release the job and return...
4459 */
4460
4461 ReleaseJob(jobid);
4462
c8f336b5 4463 LogMessage(L_INFO, "Job %d was released by \'%s\'.", jobid, username);
f63a2256 4464
4465 con->response->request.status.status_code = IPP_OK;
4466}
4467
4468
4469/*
4470 * 'restart_job()' - Restart an old print job.
bd84e0d1 4471 */
4472
4473static void
4474restart_job(client_t *con, /* I - Client connection */
4475 ipp_attribute_t *uri) /* I - Job or Printer URI */
4476{
bd84e0d1 4477 ipp_attribute_t *attr; /* Current attribute */
4478 int jobid; /* Job ID */
4479 char method[HTTP_MAX_URI],
4480 /* Method portion of URI */
4481 username[HTTP_MAX_URI],
4482 /* Username portion of URI */
4483 host[HTTP_MAX_URI],
4484 /* Host portion of URI */
4485 resource[HTTP_MAX_URI];
4486 /* Resource portion of URI */
4487 int port; /* Port portion of URI */
4488 job_t *job; /* Job information */
bd84e0d1 4489
4490
dbb05cda 4491 LogMessage(L_DEBUG2, "restart_job(%d, %s)\n", con->http.fd,
4492 uri->values[0].string.text);
bd84e0d1 4493
4494 /*
4495 * Verify that the POST operation was done to a valid URI.
4496 */
4497
4498 if (strncmp(con->uri, "/classes/", 9) != 0 &&
4499 strncmp(con->uri, "/jobs/", 5) != 0 &&
4500 strncmp(con->uri, "/printers/", 10) != 0)
4501 {
5ea8888e 4502 LogMessage(L_ERROR, "restart_job: restart request on bad resource \'%s\'!",
bd84e0d1 4503 con->uri);
4504 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4505 return;
4506 }
4507
4508 /*
4509 * See if we have a job URI or a printer URI...
4510 */
4511
4512 if (strcmp(uri->name, "printer-uri") == 0)
4513 {
4514 /*
4515 * Got a printer URI; see if we also have a job-id attribute...
4516 */
4517
4518 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4519 {
5ea8888e 4520 LogMessage(L_ERROR, "restart_job: got a printer-uri attribute but no job-id!");
bd84e0d1 4521 send_ipp_error(con, IPP_BAD_REQUEST);
4522 return;
4523 }
4524
4525 jobid = attr->values[0].integer;
4526 }
4527 else
4528 {
4529 /*
4530 * Got a job URI; parse it to get the job ID...
4531 */
4532
4533 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4534
4535 if (strncmp(resource, "/jobs/", 6) != 0)
4536 {
4537 /*
4538 * Not a valid URI!
4539 */
4540
5ea8888e 4541 LogMessage(L_ERROR, "restart_job: bad job-uri attribute \'%s\'!",
bd84e0d1 4542 uri->values[0].string.text);
4543 send_ipp_error(con, IPP_BAD_REQUEST);
4544 return;
4545 }
4546
4547 jobid = atoi(resource + 6);
4548 }
4549
4550 /*
4551 * See if the job exists...
4552 */
4553
4554 if ((job = FindJob(jobid)) == NULL)
4555 {
4556 /*
4557 * Nope - return a "not found" error...
4558 */
4559
5ea8888e 4560 LogMessage(L_ERROR, "restart_job: job #%d doesn't exist!", jobid);
bd84e0d1 4561 send_ipp_error(con, IPP_NOT_FOUND);
4562 return;
4563 }
4564
f63a2256 4565 /*
4566 * See if job is in any of the "completed" states...
4567 */
4568
4569 if (job->state->values[0].integer <= IPP_JOB_PROCESSING)
4570 {
4571 /*
4572 * Nope - return a "not possible" error...
4573 */
4574
4575 LogMessage(L_ERROR, "restart_job: job #%d is not complete!", jobid);
4576 send_ipp_error(con, IPP_NOT_POSSIBLE);
4577 return;
4578 }
4579
4580 /*
4581 * See if we have retained the job files...
4582 */
4583
d4c438d4 4584 if (!JobFiles && job->state->values[0].integer > IPP_JOB_STOPPED)
f63a2256 4585 {
4586 /*
4587 * Nope - return a "not possible" error...
4588 */
4589
4590 LogMessage(L_ERROR, "restart_job: job #%d cannot be restarted - no files!", jobid);
4591 send_ipp_error(con, IPP_NOT_POSSIBLE);
4592 return;
4593 }
4594
bd84e0d1 4595 /*
4596 * See if the job is owned by the requesting user...
4597 */
4598
ed3e11d8 4599 if (!validate_user(con, job->username, username, sizeof(username)))
bd84e0d1 4600 {
ed3e11d8 4601 LogMessage(L_ERROR, "restart_job: \"%s\" not authorized to restart job id %d owned by \"%s\"!",
4602 username, jobid, job->username);
4603 send_ipp_error(con, IPP_FORBIDDEN);
4604 return;
bd84e0d1 4605 }
4606
4607 /*
4608 * Restart the job and return...
4609 */
4610
4611 RestartJob(jobid);
4612
c8f336b5 4613 LogMessage(L_INFO, "Job %d was restarted by \'%s\'.", jobid, username);
bd84e0d1 4614
4615 con->response->request.status.status_code = IPP_OK;
4616}
4617
4618
4619/*
4620 * 'send_document()' - Send a file to a printer or class.
4621 */
4622
4623static void
4624send_document(client_t *con, /* I - Client connection */
4625 ipp_attribute_t *uri) /* I - Printer URI */
4626{
4627 ipp_attribute_t *attr; /* Current attribute */
4628 ipp_attribute_t *format; /* Document-format attribute */
bd84e0d1 4629 int jobid; /* Job ID number */
4630 job_t *job; /* Current job */
4631 char job_uri[HTTP_MAX_URI],
4632 /* Job URI */
4633 method[HTTP_MAX_URI],
4634 /* Method portion of URI */
4635 username[HTTP_MAX_URI],
4636 /* Username portion of URI */
4637 host[HTTP_MAX_URI],
4638 /* Host portion of URI */
4639 resource[HTTP_MAX_URI];
4640 /* Resource portion of URI */
4641 int port; /* Port portion of URI */
a3e17a89 4642 mime_type_t *filetype; /* Type of file */
bd84e0d1 4643 char super[MIME_MAX_SUPER],
4644 /* Supertype of file */
4645 type[MIME_MAX_TYPE],
4646 /* Subtype of file */
4647 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
4648 /* Textual name of mime type */
e09246c8 4649 char filename[1024]; /* Job filename */
a3e17a89 4650 printer_t *printer; /* Current printer */
91606789 4651 struct stat fileinfo; /* File information */
b521f3fc 4652 int kbytes; /* Size of file */
bd84e0d1 4653
4654
dbb05cda 4655 LogMessage(L_DEBUG2, "send_document(%d, %s)\n", con->http.fd,
4656 uri->values[0].string.text);
bd84e0d1 4657
4658 /*
4659 * Verify that the POST operation was done to a valid URI.
4660 */
4661
4662 if (strncmp(con->uri, "/classes/", 9) != 0 &&
4663 strncmp(con->uri, "/jobs/", 6) != 0 &&
4664 strncmp(con->uri, "/printers/", 10) != 0)
4665 {
5ea8888e 4666 LogMessage(L_ERROR, "send_document: print request on bad resource \'%s\'!",
bd84e0d1 4667 con->uri);
4668 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4669 return;
4670 }
4671
4672 /*
4673 * See if we have a job URI or a printer URI...
4674 */
4675
4676 if (strcmp(uri->name, "printer-uri") == 0)
4677 {
4678 /*
4679 * Got a printer URI; see if we also have a job-id attribute...
4680 */
4681
4682 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4683 {
5ea8888e 4684 LogMessage(L_ERROR, "send_document: got a printer-uri attribute but no job-id!");
bd84e0d1 4685 send_ipp_error(con, IPP_BAD_REQUEST);
4686 return;
4687 }
4688
4689 jobid = attr->values[0].integer;
4690 }
4691 else
4692 {
4693 /*
4694 * Got a job URI; parse it to get the job ID...
4695 */
4696
4697 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4698
4699 if (strncmp(resource, "/jobs/", 6) != 0)
4700 {
4701 /*
4702 * Not a valid URI!
4703 */
4704
5ea8888e 4705 LogMessage(L_ERROR, "send_document: bad job-uri attribute \'%s\'!",
bd84e0d1 4706 uri->values[0].string.text);
4707 send_ipp_error(con, IPP_BAD_REQUEST);
4708 return;
4709 }
4710
4711 jobid = atoi(resource + 6);
4712 }
4713
4714 /*
4715 * See if the job exists...
4716 */
4717
4718 if ((job = FindJob(jobid)) == NULL)
4719 {
4720 /*
4721 * Nope - return a "not found" error...
4722 */
4723
5ea8888e 4724 LogMessage(L_ERROR, "send_document: job #%d doesn't exist!", jobid);
bd84e0d1 4725 send_ipp_error(con, IPP_NOT_FOUND);
4726 return;
4727 }
4728
4729 /*
4730 * See if the job is owned by the requesting user...
4731 */
4732
ed3e11d8 4733 if (!validate_user(con, job->username, username, sizeof(username)))
bd84e0d1 4734 {
ed3e11d8 4735 LogMessage(L_ERROR, "send_document: \"%s\" not authorized to send document for job id %d owned by \"%s\"!",
4736 username, jobid, job->username);
4737 send_ipp_error(con, IPP_FORBIDDEN);
4738 return;
bd84e0d1 4739 }
4740
4741 /*
4742 * OK, see if the client is sending the document compressed - CUPS
4743 * doesn't support compression yet...
4744 */
4745
7ebf3a09 4746 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL &&
4747 strcmp(attr->values[0].string.text, "none") == 0)
bd84e0d1 4748 {
7ebf3a09 4749 LogMessage(L_ERROR, "send_document: Unsupported compression attribute %s!",
4750 attr->values[0].string.text);
bd84e0d1 4751 send_ipp_error(con, IPP_ATTRIBUTES);
4752 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
4753 "compression", NULL, attr->values[0].string.text);
4754 return;
4755 }
4756
4757 /*
4758 * Do we have a file to print?
4759 */
4760
4761 if (con->filename[0] == '\0')
4762 {
5ea8888e 4763 LogMessage(L_ERROR, "send_document: No file!?!");
bd84e0d1 4764 send_ipp_error(con, IPP_BAD_REQUEST);
4765 return;
4766 }
4767
4768 /*
4769 * Is it a format we support?
4770 */
4771
4772 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
4773 {
4774 /*
4775 * Grab format from client...
4776 */
4777
4778 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
4779 {
5ea8888e 4780 LogMessage(L_ERROR, "send_document: could not scan type \'%s\'!",
bd84e0d1 4781 format->values[0].string.text);
4782 send_ipp_error(con, IPP_BAD_REQUEST);
4783 return;
4784 }
4785 }
4786 else
4787 {
4788 /*
4789 * No document format attribute? Auto-type it!
4790 */
4791
4792 strcpy(super, "application");
4793 strcpy(type, "octet-stream");
4794 }
4795
4796 if (strcmp(super, "application") == 0 &&
4797 strcmp(type, "octet-stream") == 0)
4798 {
4799 /*
4800 * Auto-type the file...
4801 */
4802
5ea8888e 4803 LogMessage(L_DEBUG, "send_document: auto-typing file...");
bd84e0d1 4804
4805 filetype = mimeFileType(MimeDatabase, con->filename);
4806
4807 if (filetype != NULL)
4808 {
4809 /*
4810 * Replace the document-format attribute value with the auto-typed one.
4811 */
4812
a6988fb1 4813 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
4814 filetype->type);
bd84e0d1 4815
4816 if (format != NULL)
4817 {
4818 free(format->values[0].string.text);
4819 format->values[0].string.text = strdup(mimetype);
4820 }
4821 else
4822 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
4823 "document-format", NULL, mimetype);
4824 }
526462ee 4825 else
4826 filetype = mimeType(MimeDatabase, super, type);
bd84e0d1 4827 }
4828 else
4829 filetype = mimeType(MimeDatabase, super, type);
4830
4831 if (filetype == NULL)
4832 {
e93faa10 4833 LogMessage(L_ERROR, "send_document: Unsupported format \'%s/%s\'!",
5fa65e01 4834 super, type);
bd84e0d1 4835 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
e93faa10 4836
4837 if (format)
4838 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
4839 "document-format", NULL, format->values[0].string.text);
4840
bd84e0d1 4841 return;
4842 }
4843
5ea8888e 4844 LogMessage(L_DEBUG, "send_document: request file type is %s/%s.",
bd84e0d1 4845 filetype->super, filetype->type);
4846
4847 /*
4848 * Add the file to the job...
4849 */
4850
a3e17a89 4851 if (add_file(con, job, filetype))
bd84e0d1 4852 return;
bd84e0d1 4853
b521f3fc 4854 if (job->dtype & CUPS_PRINTER_CLASS)
4855 printer = FindClass(job->dest);
4856 else
4857 printer = FindPrinter(job->dest);
4858
4859 if (stat(con->filename, &fileinfo))
4860 kbytes = 0;
4861 else
4862 kbytes = (fileinfo.st_size + 1023) / 1024;
4863
4864 UpdateQuota(printer, job->username, 0, kbytes);
4865
4866 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
4867 attr->values[0].integer += kbytes;
91606789 4868
a6988fb1 4869 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
4870 job->num_files);
bd84e0d1 4871 rename(con->filename, filename);
4872
bd84e0d1 4873 con->filename[0] = '\0';
4874
b4257036 4875 LogMessage(L_INFO, "File of type %s/%s queued in job #%d by \'%s\'.",
4876 filetype->super, filetype->type, job->id, job->username);
bd84e0d1 4877
1049abbe 4878 /*
4879 * Start the job if this is the last document...
4880 */
4881
4882 if ((attr = ippFindAttribute(con->request, "last-document", IPP_TAG_BOOLEAN)) != NULL &&
4883 attr->values[0].boolean)
4884 {
a3e17a89 4885 /*
4886 * See if we need to add the ending sheet...
4887 */
4888
a3e17a89 4889 if (printer != NULL && !(printer->type & CUPS_PRINTER_REMOTE) &&
ebde520c 4890 (attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL &&
e903a8f6 4891 attr->num_values > 1)
a3e17a89 4892 {
4893 /*
4894 * Yes...
4895 */
4896
b521f3fc 4897 kbytes = copy_banner(con, job, attr->values[1].string.text);
4898 UpdateQuota(printer, job->username, 0, kbytes);
a3e17a89 4899 }
4900
e583bc2d 4901 if (job->state->values[0].integer == IPP_JOB_STOPPED)
4902 job->state->values[0].integer = IPP_JOB_PENDING;
9d8d71c1 4903 else if (job->state->values[0].integer == IPP_JOB_HELD)
4904 {
4905 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4906 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4907
4908 if (attr == NULL || strcmp(attr->values[0].string.text, "no-hold") == 0)
4909 job->state->values[0].integer = IPP_JOB_PENDING;
4910 }
e583bc2d 4911
1e437ea6 4912 SaveJob(job->id);
1049abbe 4913 CheckJobs();
4914 }
4b96a841 4915 else
1e437ea6 4916 {
4b96a841 4917 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4918 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4919
4920 if (attr == NULL || strcmp(attr->values[0].string.text, "no-hold") == 0)
4921 {
4922 job->state->values[0].integer = IPP_JOB_HELD;
4923 job->hold_until = time(NULL) + 60;
4924 SaveJob(job->id);
4925 }
1e437ea6 4926 }
1049abbe 4927
bd84e0d1 4928 /*
4929 * Fill in the response info...
4930 */
4931
99de6da0 4932#ifdef AF_INET6
4933 if (con->http.hostaddr.addr.sa_family == AF_INET6)
4934 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
4935 ntohs(con->http.hostaddr.ipv6.sin6_port), job->id);
4936 else
4937#endif /* AF_INET6 */
a6988fb1 4938 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
99de6da0 4939 ntohs(con->http.hostaddr.ipv4.sin_port), job->id);
4940
1049abbe 4941 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
4942 job_uri);
bd84e0d1 4943
4944 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
4945
1049abbe 4946 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
4947 job->state->values[0].integer);
7ebf3a09 4948 add_job_state_reasons(con, job);
bd84e0d1 4949
4950 con->response->request.status.status_code = IPP_OK;
4951}
4952
4953
e31bfb6e 4954/*
4955 * 'send_ipp_error()' - Send an error status back to the IPP client.
4956 */
4957
4958static void
4959send_ipp_error(client_t *con, /* I - Client connection */
4960 ipp_status_t status) /* I - IPP status code */
4961{
dbb05cda 4962 LogMessage(L_DEBUG2, "send_ipp_error(%d, %x)\n", con->http.fd, status);
e31bfb6e 4963
dbb05cda 4964 LogMessage(L_DEBUG, "Sending error: %s", ippErrorString(status));
e31bfb6e 4965
4966 con->response->request.status.status_code = status;
dd41ddbb 4967
4968 if (ippFindAttribute(con->response, "attributes-charset", IPP_TAG_ZERO) == NULL)
4969 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
4970 "attributes-charset", NULL, DefaultCharset);
4971
4972 if (ippFindAttribute(con->response, "attributes-natural-language",
4973 IPP_TAG_ZERO) == NULL)
4974 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
4975 "attributes-natural-language", NULL, DefaultLanguage);
e31bfb6e 4976}
4977
4978
3270670b 4979/*
4980 * 'set_default()' - Set the default destination...
4981 */
4982
4983static void
4984set_default(client_t *con, /* I - Client connection */
4985 ipp_attribute_t *uri) /* I - Printer URI */
4986{
4987 cups_ptype_t dtype; /* Destination type (printer or class) */
4988 char method[HTTP_MAX_URI],
4989 /* Method portion of URI */
4990 username[HTTP_MAX_URI],
4991 /* Username portion of URI */
4992 host[HTTP_MAX_URI],
4993 /* Host portion of URI */
4994 resource[HTTP_MAX_URI];
4995 /* Resource portion of URI */
4996 int port; /* Port portion of URI */
1049abbe 4997 const char *name; /* Printer name */
3270670b 4998
4999
dbb05cda 5000 LogMessage(L_DEBUG2, "set_default(%d, %s)\n", con->http.fd,
5001 uri->values[0].string.text);
3270670b 5002
5003 /*
5004 * Was this operation called from the correct URI?
5005 */
5006
5007 if (strncmp(con->uri, "/admin/", 7) != 0)
5008 {
5ea8888e 5009 LogMessage(L_ERROR, "set_default: admin request on bad resource \'%s\'!",
1124e9ec 5010 con->uri);
3270670b 5011 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5012 return;
5013 }
5014
5015 /*
5016 * Is the destination valid?
5017 */
5018
5019 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5020
ed3e11d8 5021 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
3270670b 5022 {
5023 /*
5024 * Bad URI...
5025 */
5026
5ea8888e 5027 LogMessage(L_ERROR, "set_default: resource name \'%s\' no good!", resource);
3270670b 5028 send_ipp_error(con, IPP_NOT_FOUND);
5029 return;
5030 }
5031
5032 /*
5033 * Set it as the default...
5034 */
5035
5036 if (dtype == CUPS_PRINTER_CLASS)
5037 DefaultPrinter = FindClass(name);
5038 else
5039 DefaultPrinter = FindPrinter(name);
5040
cc0561c6 5041 SaveAllPrinters();
5042 SaveAllClasses();
5043
5ea8888e 5044 LogMessage(L_INFO, "Default destination set to \'%s\' by \'%s\'.", name,
cc0561c6 5045 con->username);
5046
3270670b 5047 /*
5048 * Everything was ok, so return OK status...
5049 */
5050
5051 con->response->request.status.status_code = IPP_OK;
5052}
5053
5054
9cbd98eb 5055/*
5056 * 'set_job_attrs()' - Set job attributes.
5057 */
5058
5059static void
be87dcd3 5060set_job_attrs(client_t *con, /* I - Client connection */
5061 ipp_attribute_t *uri) /* I - Job URI */
9cbd98eb 5062{
a3e17a89 5063 ipp_attribute_t *attr, /* Current attribute */
a3e17a89 5064 *attr2, /* Job attribute */
5065 *prev2; /* Previous job attribute */
9cbd98eb 5066 int jobid; /* Job ID */
5067 job_t *job; /* Current job */
9cbd98eb 5068 char method[HTTP_MAX_URI],
5069 /* Method portion of URI */
5070 username[HTTP_MAX_URI],
5071 /* Username portion of URI */
5072 host[HTTP_MAX_URI],
5073 /* Host portion of URI */
5074 resource[HTTP_MAX_URI];
5075 /* Resource portion of URI */
5076 int port; /* Port portion of URI */
9cbd98eb 5077
5078
dbb05cda 5079 LogMessage(L_DEBUG2, "set_job_attrs(%d, %s)\n", con->http.fd,
5080 uri->values[0].string.text);
9cbd98eb 5081
5082 /*
5083 * See if we have a job URI or a printer URI...
5084 */
5085
5086 if (strcmp(uri->name, "printer-uri") == 0)
5087 {
5088 /*
5089 * Got a printer URI; see if we also have a job-id attribute...
5090 */
5091
5092 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
5093 {
5094 LogMessage(L_ERROR, "set_job_attrs: got a printer-uri attribute but no job-id!");
5095 send_ipp_error(con, IPP_BAD_REQUEST);
5096 return;
5097 }
5098
5099 jobid = attr->values[0].integer;
5100 }
5101 else
5102 {
5103 /*
5104 * Got a job URI; parse it to get the job ID...
5105 */
5106
5107 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5108
5109 if (strncmp(resource, "/jobs/", 6) != 0)
5110 {
5111 /*
5112 * Not a valid URI!
5113 */
5114
5115 LogMessage(L_ERROR, "set_job_attrs: bad job-uri attribute \'%s\'!\n",
5116 uri->values[0].string.text);
5117 send_ipp_error(con, IPP_BAD_REQUEST);
5118 return;
5119 }
5120
5121 jobid = atoi(resource + 6);
5122 }
5123
5124 /*
5125 * See if the job exists...
5126 */
5127
5128 if ((job = FindJob(jobid)) == NULL)
5129 {
5130 /*
5131 * Nope - return a "not found" error...
5132 */
5133
5134 LogMessage(L_ERROR, "set_job_attrs: job #%d doesn't exist!", jobid);
5135 send_ipp_error(con, IPP_NOT_FOUND);
5136 return;
5137 }
5138
5139 /*
5140 * See if the job has been completed...
5141 */
5142
5143 if (job->state->values[0].integer > IPP_JOB_STOPPED)
5144 {
5145 /*
5146 * Return a "not-possible" error...
5147 */
5148
5149 LogMessage(L_ERROR, "set_job_attrs: job #%d is finished and cannot be altered!", jobid);
5150 send_ipp_error(con, IPP_NOT_POSSIBLE);
5151 return;
5152 }
5153
5154 /*
5155 * See if the job is owned by the requesting user...
5156 */
5157
ed3e11d8 5158 if (!validate_user(con, job->username, username, sizeof(username)))
9cbd98eb 5159 {
ed3e11d8 5160 LogMessage(L_ERROR, "set_job_attrs: \"%s\" not authorized to alter job id %d owned by \"%s\"!",
5161 username, jobid, job->username);
5162 send_ipp_error(con, IPP_FORBIDDEN);
5163 return;
9cbd98eb 5164 }
5165
5166 /*
5167 * See what the user wants to change.
a3e17a89 5168 */
5169
962e5a9f 5170 for (attr = con->request->attrs; attr != NULL; attr = attr->next)
9cbd98eb 5171 {
a3e17a89 5172 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
5173 continue;
9cbd98eb 5174
bb27c189 5175 if (strcmp(attr->name, "job-originating-host-name") == 0 ||
5176 strcmp(attr->name, "job-originating-user-name") == 0 ||
8bc932be 5177 strcmp(attr->name, "job-media-sheets-completed") == 0 ||
5178 strcmp(attr->name, "job-k-octets") == 0 ||
5179 strcmp(attr->name, "job-id") == 0 ||
5180 strcmp(attr->name, "job-sheets") == 0 ||
5181 strncmp(attr->name, "time-at-", 8) == 0)
bb27c189 5182 continue; /* Read-only attrs */
5183
a3e17a89 5184 if (strcmp(attr->name, "job-priority") == 0 &&
5185 attr->value_tag == IPP_TAG_INTEGER &&
5186 job->state->values[0].integer != IPP_JOB_PROCESSING)
9cbd98eb 5187 {
5188 /*
a3e17a89 5189 * Change the job priority
9cbd98eb 5190 */
5191
a3e17a89 5192 SetJobPriority(jobid, attr->values[0].integer);
9cbd98eb 5193 }
503d5043 5194 else if ((attr2 = ippFindAttribute(job->attrs, attr->name, IPP_TAG_ZERO)) != NULL)
a3e17a89 5195 {
5196 /*
962e5a9f 5197 * Some other value; first free the old value...
a3e17a89 5198 */
9cbd98eb 5199
a3e17a89 5200 for (prev2 = job->attrs->attrs; prev2 != NULL; prev2 = prev2->next)
5201 if (prev2->next == attr2)
5202 break;
9cbd98eb 5203
a3e17a89 5204 if (prev2)
962e5a9f 5205 prev2->next = attr2->next;
a3e17a89 5206 else
962e5a9f 5207 job->attrs->attrs = attr2->next;
a3e17a89 5208
5209 _ipp_free_attr(attr2);
962e5a9f 5210
5211 /*
5212 * Then copy the attribute...
5213 */
5214
753453e4 5215 copy_attribute(job->attrs, attr, 0);
962e5a9f 5216
a3e17a89 5217 /*
503d5043 5218 * See if the job-name or job-hold-until is being changed.
a3e17a89 5219 */
9cbd98eb 5220
a3e17a89 5221 if (strcmp(attr->name, "job-name") == 0)
5222 strncpy(job->title, attr->values[0].string.text, sizeof(job->title) - 1);
503d5043 5223 else if (strcmp(attr->name, "job-hold-until") == 0)
56dd1f7a 5224 {
503d5043 5225 SetJobHoldUntil(job->id, attr->values[0].string.text);
56dd1f7a 5226
262ed403 5227 if (strcmp(attr->values[0].string.text, "no-hold") == 0)
5228 ReleaseJob(job->id);
56dd1f7a 5229 else
262ed403 5230 HoldJob(job->id);
56dd1f7a 5231 }
a3e17a89 5232 }
5233 else if (attr->value_tag == IPP_TAG_DELETEATTR)
5234 {
5235 /*
5236 * Delete the attribute...
5237 */
5238
5239 for (attr2 = job->attrs->attrs, prev2 = NULL;
5240 attr2 != NULL;
5241 prev2 = attr2, attr2 = attr2->next)
5242 if (attr2->name && strcmp(attr2->name, attr->name) == 0)
5243 break;
5244
5245 if (attr2)
5246 {
5247 if (prev2)
5248 prev2->next = attr2->next;
5249 else
5250 job->attrs->attrs = attr2->next;
5251
5252 _ipp_free_attr(attr2);
5253 }
5254 }
5255 else
5256 {
5257 /*
962e5a9f 5258 * Add new option by copying it...
a3e17a89 5259 */
5260
753453e4 5261 copy_attribute(job->attrs, attr, 0);
a3e17a89 5262 }
9cbd98eb 5263 }
5264
56dd1f7a 5265 /*
5266 * Save the job...
5267 */
5268
5269 SaveJob(job->id);
5270
9cbd98eb 5271 /*
5272 * Start jobs if possible...
5273 */
5274
5275 CheckJobs();
5276
5277 /*
5278 * Return with "everything is OK" status...
5279 */
5280
5281 con->response->request.status.status_code = IPP_OK;
5282}
5283
5284
f3d580b9 5285/*
5286 * 'start_printer()' - Start a printer.
5287 */
5288
5289static void
5290start_printer(client_t *con, /* I - Client connection */
5291 ipp_attribute_t *uri) /* I - Printer URI */
5292{
5293 cups_ptype_t dtype; /* Destination type (printer or class) */
5294 char method[HTTP_MAX_URI],
5295 /* Method portion of URI */
5296 username[HTTP_MAX_URI],
5297 /* Username portion of URI */
5298 host[HTTP_MAX_URI],
5299 /* Host portion of URI */
5300 resource[HTTP_MAX_URI];
5301 /* Resource portion of URI */
5302 int port; /* Port portion of URI */
1049abbe 5303 const char *name; /* Printer name */
f3d580b9 5304 printer_t *printer; /* Printer data */
5305
3270670b 5306
dbb05cda 5307 LogMessage(L_DEBUG2, "start_printer(%d, %s)\n", con->http.fd,
5308 uri->values[0].string.text);
f3d580b9 5309
5310 /*
5311 * Was this operation called from the correct URI?
5312 */
5313
5314 if (strncmp(con->uri, "/admin/", 7) != 0)
5315 {
5ea8888e 5316 LogMessage(L_ERROR, "start_printer: admin request on bad resource \'%s\'!",
1124e9ec 5317 con->uri);
f3d580b9 5318 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5319 return;
5320 }
5321
5322 /*
5323 * Is the destination valid?
5324 */
5325
5326 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5327
ed3e11d8 5328 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
f3d580b9 5329 {
5330 /*
5331 * Bad URI...
5332 */
5333
5ea8888e 5334 LogMessage(L_ERROR, "start_printer: resource name \'%s\' no good!", resource);
f3d580b9 5335 send_ipp_error(con, IPP_NOT_FOUND);
5336 return;
5337 }
5338
5339 /*
5340 * Start the printer...
5341 */
5342
cbbfcc63 5343 if (dtype == CUPS_PRINTER_CLASS)
5344 printer = FindClass(name);
5345 else
5346 printer = FindPrinter(name);
f3d580b9 5347
5348 StartPrinter(printer);
5349
cc0561c6 5350 if (dtype == CUPS_PRINTER_CLASS)
5351 SaveAllClasses();
5352 else
5353 SaveAllPrinters();
5354
5355 if (dtype == CUPS_PRINTER_CLASS)
5ea8888e 5356 LogMessage(L_INFO, "Class \'%s\' started by \'%s\'.", name,
cc0561c6 5357 con->username);
5358 else
5ea8888e 5359 LogMessage(L_INFO, "Printer \'%s\' started by \'%s\'.", name,
cc0561c6 5360 con->username);
5361
f3d580b9 5362 printer->state_message[0] = '\0';
5363
432073bb 5364 CheckJobs();
5365
f3d580b9 5366 /*
5367 * Everything was ok, so return OK status...
5368 */
5369
5370 con->response->request.status.status_code = IPP_OK;
5371}
5372
5373
5374/*
5375 * 'stop_printer()' - Stop a printer.
5376 */
5377
5378static void
5379stop_printer(client_t *con, /* I - Client connection */
5380 ipp_attribute_t *uri) /* I - Printer URI */
5381{
5382 cups_ptype_t dtype; /* Destination type (printer or class) */
5383 char method[HTTP_MAX_URI],
5384 /* Method portion of URI */
5385 username[HTTP_MAX_URI],
5386 /* Username portion of URI */
5387 host[HTTP_MAX_URI],
5388 /* Host portion of URI */
5389 resource[HTTP_MAX_URI];
5390 /* Resource portion of URI */
5391 int port; /* Port portion of URI */
1049abbe 5392 const char *name; /* Printer name */
f3d580b9 5393 printer_t *printer; /* Printer data */
5394 ipp_attribute_t *attr; /* printer-state-message attribute */
5395
5396
dbb05cda 5397 LogMessage(L_DEBUG2, "stop_printer(%d, %s)\n", con->http.fd,
5398 uri->values[0].string.text);
f3d580b9 5399
5400 /*
5401 * Was this operation called from the correct URI?
5402 */
5403
5404 if (strncmp(con->uri, "/admin/", 7) != 0)
5405 {
5ea8888e 5406 LogMessage(L_ERROR, "stop_printer: admin request on bad resource \'%s\'!",
1124e9ec 5407 con->uri);
f3d580b9 5408 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5409 return;
5410 }
5411
5412 /*
5413 * Is the destination valid?
5414 */
5415
5416 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5417
ed3e11d8 5418 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
f3d580b9 5419 {
5420 /*
5421 * Bad URI...
5422 */
5423
5ea8888e 5424 LogMessage(L_ERROR, "stop_printer: resource name \'%s\' no good!", resource);
f3d580b9 5425 send_ipp_error(con, IPP_NOT_FOUND);
5426 return;
5427 }
5428
5429 /*
5430 * Stop the printer...
5431 */
5432
cbbfcc63 5433 if (dtype == CUPS_PRINTER_CLASS)
5434 printer = FindClass(name);
5435 else
5436 printer = FindPrinter(name);
f3d580b9 5437
5438 StopPrinter(printer);
5439
cc0561c6 5440 if (dtype == CUPS_PRINTER_CLASS)
5441 SaveAllClasses();
5442 else
5443 SaveAllPrinters();
5444
f3d580b9 5445 if ((attr = ippFindAttribute(con->request, "printer-state-message",
5446 IPP_TAG_TEXT)) == NULL)
5447 strcpy(printer->state_message, "Paused");
5448 else
970017a4 5449 {
5450 strncpy(printer->state_message, attr->values[0].string.text,
5451 sizeof(printer->state_message) - 1);
5452 printer->state_message[sizeof(printer->state_message) - 1] = '\0';
5453 }
f3d580b9 5454
cc0561c6 5455 if (dtype == CUPS_PRINTER_CLASS)
5ea8888e 5456 LogMessage(L_INFO, "Class \'%s\' stopped by \'%s\'.", name,
cc0561c6 5457 con->username);
5458 else
5ea8888e 5459 LogMessage(L_INFO, "Printer \'%s\' stopped by \'%s\'.", name,
cc0561c6 5460 con->username);
5461
f3d580b9 5462 /*
5463 * Everything was ok, so return OK status...
5464 */
5465
5466 con->response->request.status.status_code = IPP_OK;
5467}
5468
5469
1d2c70a6 5470/*
5471 * 'validate_job()' - Validate printer options and destination.
5472 */
5473
e31bfb6e 5474static void
1d2c70a6 5475validate_job(client_t *con, /* I - Client connection */
5476 ipp_attribute_t *uri) /* I - Printer URI */
e31bfb6e 5477{
1d2c70a6 5478 ipp_attribute_t *attr; /* Current attribute */
5479 ipp_attribute_t *format; /* Document-format attribute */
5480 cups_ptype_t dtype; /* Destination type (printer or class) */
5481 char method[HTTP_MAX_URI],
5482 /* Method portion of URI */
5483 username[HTTP_MAX_URI],
5484 /* Username portion of URI */
5485 host[HTTP_MAX_URI],
5486 /* Host portion of URI */
5487 resource[HTTP_MAX_URI];
5488 /* Resource portion of URI */
5489 int port; /* Port portion of URI */
5490 char super[MIME_MAX_SUPER],
5491 /* Supertype of file */
5492 type[MIME_MAX_TYPE];
5493 /* Subtype of file */
5494
5495
dbb05cda 5496 LogMessage(L_DEBUG2, "validate_job(%d, %s)\n", con->http.fd,
5497 uri->values[0].string.text);
1d2c70a6 5498
2aeb2b1d 5499 /*
5500 * Verify that the POST operation was done to a valid URI.
5501 */
5502
5503 if (strncmp(con->uri, "/classes/", 9) != 0 &&
5504 strncmp(con->uri, "/printers/", 10) != 0)
5505 {
5ea8888e 5506 LogMessage(L_ERROR, "validate_job: request on bad resource \'%s\'!",
1124e9ec 5507 con->uri);
2aeb2b1d 5508 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5509 return;
5510 }
5511
1d2c70a6 5512 /*
5513 * OK, see if the client is sending the document compressed - CUPS
5514 * doesn't support compression yet...
5515 */
5516
7ebf3a09 5517 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL &&
5518 strcmp(attr->values[0].string.text, "none") == 0)
1d2c70a6 5519 {
7ebf3a09 5520 LogMessage(L_ERROR, "validate_job: Unsupported compression attribute %s!",
5521 attr->values[0].string.text);
1d2c70a6 5522 send_ipp_error(con, IPP_ATTRIBUTES);
bd84e0d1 5523 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
5524 "compression", NULL, attr->values[0].string.text);
1d2c70a6 5525 return;
5526 }
5527
5528 /*
5529 * Is it a format we support?
5530 */
5531
17b95e13 5532 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
1d2c70a6 5533 {
17b95e13 5534 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
5535 {
5536 LogMessage(L_ERROR, "validate_job: could not scan type \'%s\'!\n",
5537 format->values[0].string.text);
5538 send_ipp_error(con, IPP_BAD_REQUEST);
5539 return;
5540 }
1d2c70a6 5541
17b95e13 5542 if ((strcmp(super, "application") != 0 ||
5543 strcmp(type, "octet-stream") != 0) &&
5544 mimeType(MimeDatabase, super, type) == NULL)
5545 {
5546 LogMessage(L_ERROR, "validate_job: Unsupported format \'%s\'!\n",
5547 format->values[0].string.text);
5548 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
5549 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
5550 "document-format", NULL, format->values[0].string.text);
5551 return;
5552 }
1d2c70a6 5553 }
5554
5555 /*
5556 * Is the destination valid?
5557 */
5558
c0341b41 5559 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1d2c70a6 5560
ed3e11d8 5561 if (ValidateDest(host, resource, &dtype) == NULL)
1d2c70a6 5562 {
5563 /*
5564 * Bad URI...
5565 */
5566
5ea8888e 5567 LogMessage(L_ERROR, "validate_job: resource name \'%s\' no good!", resource);
1d2c70a6 5568 send_ipp_error(con, IPP_NOT_FOUND);
5569 return;
5570 }
5571
5572 /*
5573 * Everything was ok, so return OK status...
5574 */
5575
5576 con->response->request.status.status_code = IPP_OK;
e31bfb6e 5577}
5578
5579
5580/*
ed3e11d8 5581 * 'validate_user()' - Validate the user for the request.
5582 */
5583
5584static int /* O - 1 if permitted, 0 otherwise */
5585validate_user(client_t *con, /* I - Client connection */
5586 const char *owner, /* I - Owner of job/resource */
5587 char *username, /* O - Authenticated username */
5588 int userlen) /* I - Length of username */
5589{
753453e4 5590 int i, j; /* Looping vars */
ed3e11d8 5591 ipp_attribute_t *attr; /* requesting-user-name attribute */
5592 struct passwd *user; /* User info */
5593 struct group *group; /* System group info */
5594
5595
dbb05cda 5596 LogMessage(L_DEBUG2, "validate_user(%d, %s, %s, %d)\n", con->http.fd,
5597 owner, username, userlen);
5598
d588a92e 5599 /*
5600 * Validate input...
5601 */
5602
5603 if (con == NULL || owner == NULL || username == NULL || userlen <= 0)
5604 return (0);
5605
ed3e11d8 5606 /*
5607 * Get the best authenticated username that is available.
5608 */
5609
5610 if (con->username[0])
5611 strncpy(username, con->username, userlen - 1);
5612 else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
5613 strncpy(username, attr->values[0].string.text, userlen - 1);
5614 else
5615 strncpy(username, "anonymous", userlen - 1);
5616
5617 username[userlen - 1] = '\0';
5618
5619 /*
5620 * Check the username against the owner...
5621 */
5622
753453e4 5623 if (strcasecmp(username, owner) != 0 && strcasecmp(username, "root") != 0)
ed3e11d8 5624 {
5625 /*
5626 * Not the owner or root; check to see if the user is a member of the
5627 * system group...
5628 */
5629
5630 user = getpwnam(username);
5631 endpwent();
5632
753453e4 5633 for (i = 0, j = 0, group = NULL; i < NumSystemGroups; i ++)
ed3e11d8 5634 {
753453e4 5635 group = getgrnam(SystemGroups[i]);
5636 endgrent();
5637
5638 if (group != NULL)
5639 {
5640 for (j = 0; group->gr_mem[j]; j ++)
5641 if (strcasecmp(username, group->gr_mem[j]) == 0)
5642 break;
5643
5644 if (group->gr_mem[j])
ed3e11d8 5645 break;
753453e4 5646 }
5647 else
5648 j = 0;
ed3e11d8 5649 }
ed3e11d8 5650
5651 if (user == NULL || group == NULL ||
753453e4 5652 (group->gr_mem[j] == NULL && group->gr_gid != user->pw_gid))
ed3e11d8 5653 {
5654 /*
5655 * Username not found, group not found, or user is not part of the
5656 * system group...
5657 */
5658
5659 return (0);
5660 }
5661 }
5662
5663 return (1);
5664}
5665
5666
5667/*
753453e4 5668 * End of "$Id: ipp.c,v 1.127.2.3 2001/12/26 16:52:53 mike Exp $".
e31bfb6e 5669 */