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