]> 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.71 2003/07/20 12:51:47 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(%d, %d)\n", con->http.fd,
948 job->id);
949
950 switch (job->state->values[0].integer)
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), User, 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);
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 * Create the job and set things up...
2894 */
2895
2896 if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
2897 priority = attr->values[0].integer;
2898 else
2899 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
2900 priority = 50);
2901
2902 if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
2903 title = attr->values[0].string.text;
2904 else
2905 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
2906 title = "Untitled");
2907
2908 if ((job = AddJob(priority, printer->name)) == NULL)
2909 {
2910 LogMessage(L_ERROR, "create_job: unable to add job for destination \'%s\'!",
2911 dest);
2912 send_ipp_error(con, IPP_INTERNAL_ERROR);
2913 return;
2914 }
2915
2916 job->dtype = dtype;
2917 job->attrs = con->request;
2918 con->request = NULL;
2919
2920 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
2921
2922 if (con->username[0])
2923 SetString(&job->username, con->username);
2924 else if (attr != NULL)
2925 {
2926 LogMessage(L_DEBUG, "create_job: requesting-user-name = \'%s\'",
2927 attr->values[0].string.text);
2928
2929 SetString(&job->username, attr->values[0].string.text);
2930 }
2931 else
2932 SetString(&job->username, "anonymous");
2933
2934 if (attr == NULL)
2935 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
2936 NULL, job->username);
2937 else
2938 {
2939 attr->group_tag = IPP_TAG_JOB;
2940 SetString(&attr->name, "job-originating-user-name");
2941 }
2942
2943 if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
2944 IPP_TAG_ZERO)) != NULL)
2945 {
2946 /*
2947 * Request contains a job-originating-host-name attribute; validate it...
2948 */
2949
2950 if (attr->value_tag != IPP_TAG_NAME ||
2951 attr->num_values != 1 ||
2952 strcmp(con->http.hostname, "localhost") != 0)
2953 {
2954 /*
2955 * Can't override the value if we aren't connected via localhost.
2956 * Also, we can only have 1 value and it must be a name value.
2957 */
2958
2959 int i; /* Looping var */
2960
2961 switch (attr->value_tag)
2962 {
2963 case IPP_TAG_STRING :
2964 case IPP_TAG_TEXTLANG :
2965 case IPP_TAG_NAMELANG :
2966 case IPP_TAG_TEXT :
2967 case IPP_TAG_NAME :
2968 case IPP_TAG_KEYWORD :
2969 case IPP_TAG_URI :
2970 case IPP_TAG_URISCHEME :
2971 case IPP_TAG_CHARSET :
2972 case IPP_TAG_LANGUAGE :
2973 case IPP_TAG_MIMETYPE :
2974 /*
2975 * Free old strings...
2976 */
2977
2978 for (i = 0; i < attr->num_values; i ++)
2979 {
2980 free(attr->values[i].string.text);
2981 attr->values[i].string.text = NULL;
2982 if (attr->values[i].string.charset)
2983 {
2984 free(attr->values[i].string.charset);
2985 attr->values[i].string.charset = NULL;
2986 }
2987 }
2988
2989 default :
2990 break;
2991 }
2992
2993 /*
2994 * Use the default connection hostname instead...
2995 */
2996
2997 attr->value_tag = IPP_TAG_NAME;
2998 attr->num_values = 1;
2999 attr->values[0].string.text = strdup(con->http.hostname);
3000 }
3001
3002 attr->group_tag = IPP_TAG_JOB;
3003 }
3004 else
3005 {
3006 /*
3007 * No job-originating-host-name attribute, so use the hostname from
3008 * the connection...
3009 */
3010
3011 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
3012 "job-originating-host-name", NULL, con->http.hostname);
3013 }
3014
3015 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
3016 time(NULL));
3017 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3018 "time-at-processing", 0);
3019 attr->value_tag = IPP_TAG_NOVALUE;
3020 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3021 "time-at-completed", 0);
3022 attr->value_tag = IPP_TAG_NOVALUE;
3023
3024 /*
3025 * Add remaining job attributes...
3026 */
3027
3028 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
3029 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
3030 "job-state", IPP_JOB_STOPPED);
3031 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3032 "job-media-sheets-completed", 0);
3033 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
3034 printer_uri);
3035 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
3036 title);
3037
3038 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
3039 attr->values[0].integer = 0;
3040 else
3041 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3042 "job-k-octets", 0);
3043
3044 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
3045 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
3046 if (attr == NULL)
3047 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
3048 "job-hold-until", NULL, "no-hold");
3049 if (attr != NULL && strcmp(attr->values[0].string.text, "no-hold") != 0 &&
3050 !(printer->type & CUPS_PRINTER_REMOTE))
3051 {
3052 /*
3053 * Hold job until specified time...
3054 */
3055
3056 SetJobHoldUntil(job->id, attr->values[0].string.text);
3057 }
3058 else
3059 job->hold_until = time(NULL) + 60;
3060
3061 job->state->values[0].integer = IPP_JOB_HELD;
3062
3063 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
3064 Classification)
3065 {
3066 /*
3067 * Add job sheets options...
3068 */
3069
3070 if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) == NULL)
3071 {
3072 LogMessage(L_DEBUG, "Adding default job-sheets values \"%s,%s\"...",
3073 printer->job_sheets[0], printer->job_sheets[1]);
3074
3075 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
3076 2, NULL, NULL);
3077 attr->values[0].string.text = strdup(printer->job_sheets[0]);
3078 attr->values[1].string.text = strdup(printer->job_sheets[1]);
3079 }
3080
3081 job->job_sheets = attr;
3082
3083 /*
3084 * Enforce classification level if set...
3085 */
3086
3087 if (Classification)
3088 {
3089 if (ClassifyOverride)
3090 {
3091 if (strcmp(attr->values[0].string.text, "none") == 0 &&
3092 (attr->num_values == 1 ||
3093 strcmp(attr->values[1].string.text, "none") == 0))
3094 {
3095 /*
3096 * Force the leading banner to have the classification on it...
3097 */
3098
3099 SetString(&attr->values[0].string.text, Classification);
3100 }
3101 else if (attr->num_values == 2 &&
3102 strcmp(attr->values[0].string.text, attr->values[1].string.text) != 0 &&
3103 strcmp(attr->values[0].string.text, "none") != 0 &&
3104 strcmp(attr->values[1].string.text, "none") != 0)
3105 {
3106 /*
3107 * Can't put two different security markings on the same document!
3108 */
3109
3110 SetString(&attr->values[1].string.text, attr->values[0].string.text);
3111 }
3112 }
3113 else if (strcmp(attr->values[0].string.text, Classification) != 0 &&
3114 (attr->num_values == 1 ||
3115 strcmp(attr->values[1].string.text, Classification) != 0))
3116 {
3117 /*
3118 * Force the leading banner to have the classification on it...
3119 */
3120
3121 SetString(&attr->values[0].string.text, Classification);
3122 }
3123 }
3124
3125 /*
3126 * See if we need to add the starting sheet...
3127 */
3128
3129 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
3130 {
3131 LogMessage(L_INFO, "Adding start banner page \"%s\" to job %d.",
3132 attr->values[0].string.text, job->id);
3133
3134 kbytes = copy_banner(con, job, attr->values[0].string.text);
3135
3136 UpdateQuota(printer, job->username, 0, kbytes);
3137 }
3138 }
3139 else if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
3140 job->sheets = attr;
3141
3142 /*
3143 * Save and log the job...
3144 */
3145
3146 SaveJob(job->id);
3147
3148 LogMessage(L_INFO, "Job %d created on \'%s\' by \'%s\'.", job->id,
3149 job->dest, job->username);
3150
3151 /*
3152 * Fill in the response info...
3153 */
3154
3155 #ifdef AF_INET6
3156 if (con->http.hostaddr.addr.sa_family == AF_INET6)
3157 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
3158 ntohs(con->http.hostaddr.ipv6.sin6_port), job->id);
3159 else
3160 #endif /* AF_INET6 */
3161 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
3162 ntohs(con->http.hostaddr.ipv4.sin_port), job->id);
3163
3164 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
3165
3166 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
3167
3168 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
3169 job->state->values[0].integer);
3170
3171 con->response->request.status.status_code = IPP_OK;
3172 }
3173
3174
3175 /*
3176 * 'delete_printer()' - Remove a printer or class from the system.
3177 */
3178
3179 static void
3180 delete_printer(client_t *con, /* I - Client connection */
3181 ipp_attribute_t *uri) /* I - URI of printer or class */
3182 {
3183 const char *dest; /* Destination */
3184 cups_ptype_t dtype; /* Destination type (printer or class) */
3185 char method[HTTP_MAX_URI],
3186 /* Method portion of URI */
3187 username[HTTP_MAX_URI],
3188 /* Username portion of URI */
3189 host[HTTP_MAX_URI],
3190 /* Host portion of URI */
3191 resource[HTTP_MAX_URI];
3192 /* Resource portion of URI */
3193 int port; /* Port portion of URI */
3194 printer_t *printer; /* Printer/class */
3195 char filename[1024]; /* Script/PPD filename */
3196
3197
3198 LogMessage(L_DEBUG2, "delete_printer(%d, %s)\n", con->http.fd,
3199 uri->values[0].string.text);
3200
3201 /*
3202 * Was this operation called from the correct URI?
3203 */
3204
3205 if (strncmp(con->uri, "/admin/", 7) != 0)
3206 {
3207 LogMessage(L_ERROR, "delete_printer: admin request on bad resource \'%s\'!",
3208 con->uri);
3209 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3210 return;
3211 }
3212
3213 DEBUG_printf(("delete_printer(%08x, %08x)\n", con, uri));
3214
3215 /*
3216 * Do we have a valid URI?
3217 */
3218
3219 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3220
3221 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
3222 {
3223 /*
3224 * Bad URI...
3225 */
3226
3227 LogMessage(L_ERROR, "delete_printer: resource name \'%s\' no good!", resource);
3228 send_ipp_error(con, IPP_NOT_FOUND);
3229 return;
3230 }
3231
3232 /*
3233 * Find the printer or class and delete it...
3234 */
3235
3236 if (dtype & CUPS_PRINTER_CLASS)
3237 printer = FindClass(dest);
3238 else
3239 printer = FindPrinter(dest);
3240
3241 /*
3242 * Remove old jobs...
3243 */
3244
3245 CancelJobs(dest, NULL, 1);
3246
3247 /*
3248 * Remove any old PPD or script files...
3249 */
3250
3251 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot, dest);
3252 unlink(filename);
3253
3254 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest);
3255 unlink(filename);
3256
3257 if (dtype & CUPS_PRINTER_CLASS)
3258 {
3259 LogMessage(L_INFO, "Class \'%s\' deleted by \'%s\'.", dest,
3260 con->username);
3261
3262 DeletePrinter(printer, 0);
3263 SaveAllClasses();
3264 }
3265 else
3266 {
3267 LogMessage(L_INFO, "Printer \'%s\' deleted by \'%s\'.", dest,
3268 con->username);
3269
3270 DeletePrinter(printer, 0);
3271 SaveAllPrinters();
3272 }
3273
3274 /*
3275 * Return with no errors...
3276 */
3277
3278 con->response->request.status.status_code = IPP_OK;
3279 }
3280
3281
3282 /*
3283 * 'get_default()' - Get the default destination.
3284 */
3285
3286 static void
3287 get_default(client_t *con) /* I - Client connection */
3288 {
3289 LogMessage(L_DEBUG2, "get_default(%d)\n", con->http.fd);
3290
3291 if (DefaultPrinter != NULL)
3292 {
3293 copy_attrs(con->response, DefaultPrinter->attrs,
3294 ippFindAttribute(con->request, "requested-attributes",
3295 IPP_TAG_KEYWORD), IPP_TAG_ZERO, 0);
3296
3297 con->response->request.status.status_code = IPP_OK;
3298 }
3299 else
3300 con->response->request.status.status_code = IPP_NOT_FOUND;
3301 }
3302
3303
3304 /*
3305 * 'get_devices()' - Get the list of available devices on the local system.
3306 */
3307
3308 static void
3309 get_devices(client_t *con) /* I - Client connection */
3310 {
3311 LogMessage(L_DEBUG2, "get_devices(%d)\n", con->http.fd);
3312
3313 /*
3314 * Copy the device attributes to the response using the requested-attributes
3315 * attribute that may be provided by the client.
3316 */
3317
3318 copy_attrs(con->response, Devices,
3319 ippFindAttribute(con->request, "requested-attributes",
3320 IPP_TAG_KEYWORD), IPP_TAG_ZERO, IPP_TAG_COPY);
3321
3322 con->response->request.status.status_code = IPP_OK;
3323 }
3324
3325
3326 /*
3327 * 'get_jobs()' - Get a list of jobs for the specified printer.
3328 */
3329
3330 static void
3331 get_jobs(client_t *con, /* I - Client connection */
3332 ipp_attribute_t *uri) /* I - Printer URI */
3333 {
3334 ipp_attribute_t *attr, /* Current attribute */
3335 *requested; /* Requested attributes */
3336 const char *dest; /* Destination */
3337 cups_ptype_t dtype; /* Destination type (printer or class) */
3338 cups_ptype_t dmask; /* Destination type mask */
3339 char method[HTTP_MAX_URI],
3340 /* Method portion of URI */
3341 username[HTTP_MAX_URI],
3342 /* Username portion of URI */
3343 host[HTTP_MAX_URI],
3344 /* Host portion of URI */
3345 resource[HTTP_MAX_URI];
3346 /* Resource portion of URI */
3347 int port; /* Port portion of URI */
3348 int completed; /* Completed jobs? */
3349 int limit; /* Maximum number of jobs to return */
3350 int count; /* Number of jobs that match */
3351 job_t *job; /* Current job pointer */
3352 char job_uri[HTTP_MAX_URI];
3353 /* Job URI... */
3354
3355
3356 LogMessage(L_DEBUG2, "get_jobs(%d, %s)\n", con->http.fd,
3357 uri->values[0].string.text);
3358
3359 /*
3360 * Is the destination valid?
3361 */
3362
3363 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3364
3365 if (strcmp(resource, "/") == 0 ||
3366 (strncmp(resource, "/jobs", 5) == 0 && strlen(resource) <= 6))
3367 {
3368 dest = NULL;
3369 dtype = (cups_ptype_t)0;
3370 dmask = (cups_ptype_t)0;
3371 }
3372 else if (strncmp(resource, "/printers", 9) == 0 && strlen(resource) <= 10)
3373 {
3374 dest = NULL;
3375 dtype = (cups_ptype_t)0;
3376 dmask = CUPS_PRINTER_CLASS;
3377 }
3378 else if (strncmp(resource, "/classes", 8) == 0 && strlen(resource) <= 9)
3379 {
3380 dest = NULL;
3381 dtype = CUPS_PRINTER_CLASS;
3382 dmask = CUPS_PRINTER_CLASS;
3383 }
3384 else if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
3385 {
3386 /*
3387 * Bad URI...
3388 */
3389
3390 LogMessage(L_ERROR, "get_jobs: resource name \'%s\' no good!", resource);
3391 send_ipp_error(con, IPP_NOT_FOUND);
3392 return;
3393 }
3394 else
3395 dmask = CUPS_PRINTER_CLASS;
3396
3397 /*
3398 * See if the "which-jobs" attribute have been specified...
3399 */
3400
3401 if ((attr = ippFindAttribute(con->request, "which-jobs", IPP_TAG_KEYWORD)) != NULL &&
3402 strcmp(attr->values[0].string.text, "completed") == 0)
3403 completed = 1;
3404 else
3405 completed = 0;
3406
3407 /*
3408 * See if they want to limit the number of jobs reported...
3409 */
3410
3411 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
3412 limit = attr->values[0].integer;
3413 else
3414 limit = 1000000;
3415
3416 /*
3417 * See if we only want to see jobs for a specific user...
3418 */
3419
3420 if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL &&
3421 attr->values[0].boolean)
3422 {
3423 if (con->username[0])
3424 strlcpy(username, con->username, sizeof(username));
3425 else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
3426 strlcpy(username, attr->values[0].string.text, sizeof(username));
3427 else
3428 strcpy(username, "anonymous");
3429 }
3430 else
3431 username[0] = '\0';
3432
3433 requested = ippFindAttribute(con->request, "requested-attributes",
3434 IPP_TAG_KEYWORD);
3435
3436 /*
3437 * OK, build a list of jobs for this printer...
3438 */
3439
3440 for (count = 0, job = Jobs; count < limit && job != NULL; job = job->next)
3441 {
3442 /*
3443 * Filter out jobs that don't match...
3444 */
3445
3446 DEBUG_printf(("get_jobs: job->id = %d\n", job->id));
3447
3448 if ((dest != NULL && strcmp(job->dest, dest) != 0) &&
3449 (job->printer == NULL || dest == NULL ||
3450 strcmp(job->printer->name, dest) != 0))
3451 continue;
3452 if ((job->dtype & dmask) != dtype &&
3453 (job->printer == NULL || (job->printer->type & dmask) != dtype))
3454 continue;
3455 if (username[0] != '\0' && strcmp(username, job->username) != 0)
3456 continue;
3457
3458 if (completed && job->state->values[0].integer <= IPP_JOB_STOPPED)
3459 continue;
3460 if (!completed && job->state->values[0].integer > IPP_JOB_STOPPED)
3461 continue;
3462
3463 count ++;
3464
3465 DEBUG_printf(("get_jobs: count = %d\n", count));
3466
3467 /*
3468 * Send the requested attributes for each job...
3469 */
3470
3471 #ifdef AF_INET6
3472 if (con->http.hostaddr.addr.sa_family == AF_INET6)
3473 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
3474 ntohs(con->http.hostaddr.ipv6.sin6_port), job->id);
3475 else
3476 #endif /* AF_INET6 */
3477 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
3478 ntohs(con->http.hostaddr.ipv4.sin_port), job->id);
3479
3480 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
3481 "job-more-info", NULL, job_uri);
3482
3483 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
3484 "job-uri", NULL, job_uri);
3485
3486 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
3487 "job-printer-up-time", time(NULL));
3488
3489 /*
3490 * Copy the job attributes to the response using the requested-attributes
3491 * attribute that may be provided by the client.
3492 */
3493
3494 copy_attrs(con->response, job->attrs, requested, IPP_TAG_JOB, 0);
3495
3496 add_job_state_reasons(con, job);
3497
3498 ippAddSeparator(con->response);
3499 }
3500
3501 if (requested != NULL)
3502 con->response->request.status.status_code = IPP_OK_SUBST;
3503 else
3504 con->response->request.status.status_code = IPP_OK;
3505 }
3506
3507
3508 /*
3509 * 'get_job_attrs()' - Get job attributes.
3510 */
3511
3512 static void
3513 get_job_attrs(client_t *con, /* I - Client connection */
3514 ipp_attribute_t *uri) /* I - Job URI */
3515 {
3516 ipp_attribute_t *attr, /* Current attribute */
3517 *requested; /* Requested attributes */
3518 int jobid; /* Job ID */
3519 job_t *job; /* Current job */
3520 char method[HTTP_MAX_URI],
3521 /* Method portion of URI */
3522 username[HTTP_MAX_URI],
3523 /* Username portion of URI */
3524 host[HTTP_MAX_URI],
3525 /* Host portion of URI */
3526 resource[HTTP_MAX_URI];
3527 /* Resource portion of URI */
3528 int port; /* Port portion of URI */
3529 char job_uri[HTTP_MAX_URI];
3530 /* Job URI... */
3531
3532
3533 LogMessage(L_DEBUG2, "get_job_attrs(%d, %s)\n", con->http.fd,
3534 uri->values[0].string.text);
3535
3536 /*
3537 * See if we have a job URI or a printer URI...
3538 */
3539
3540 if (strcmp(uri->name, "printer-uri") == 0)
3541 {
3542 /*
3543 * Got a printer URI; see if we also have a job-id attribute...
3544 */
3545
3546 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
3547 {
3548 LogMessage(L_ERROR, "get_job_attrs: got a printer-uri attribute but no job-id!");
3549 send_ipp_error(con, IPP_BAD_REQUEST);
3550 return;
3551 }
3552
3553 jobid = attr->values[0].integer;
3554 }
3555 else
3556 {
3557 /*
3558 * Got a job URI; parse it to get the job ID...
3559 */
3560
3561 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3562
3563 if (strncmp(resource, "/jobs/", 6) != 0)
3564 {
3565 /*
3566 * Not a valid URI!
3567 */
3568
3569 LogMessage(L_ERROR, "get_job_attrs: bad job-uri attribute \'%s\'!\n",
3570 uri->values[0].string.text);
3571 send_ipp_error(con, IPP_BAD_REQUEST);
3572 return;
3573 }
3574
3575 jobid = atoi(resource + 6);
3576 }
3577
3578 /*
3579 * See if the job exists...
3580 */
3581
3582 if ((job = FindJob(jobid)) == NULL)
3583 {
3584 /*
3585 * Nope - return a "not found" error...
3586 */
3587
3588 LogMessage(L_ERROR, "get_job_attrs: job #%d doesn't exist!", jobid);
3589 send_ipp_error(con, IPP_NOT_FOUND);
3590 return;
3591 }
3592
3593 /*
3594 * Put out the standard attributes...
3595 */
3596
3597 #ifdef AF_INET6
3598 if (con->http.hostaddr.addr.sa_family == AF_INET6)
3599 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d",
3600 ServerName, ntohs(con->http.hostaddr.ipv6.sin6_port),
3601 job->id);
3602 else
3603 #endif /* AF_INET6 */
3604 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d",
3605 ServerName, ntohs(con->http.hostaddr.ipv4.sin_port),
3606 job->id);
3607
3608 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
3609
3610 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
3611 "job-more-info", NULL, job_uri);
3612
3613 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
3614 "job-uri", NULL, job_uri);
3615
3616 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
3617 "job-printer-up-time", time(NULL));
3618
3619 /*
3620 * Copy the job attributes to the response using the requested-attributes
3621 * attribute that may be provided by the client.
3622 */
3623
3624 requested = ippFindAttribute(con->request, "requested-attributes",
3625 IPP_TAG_KEYWORD);
3626
3627 copy_attrs(con->response, job->attrs, requested, IPP_TAG_JOB, 0);
3628
3629 add_job_state_reasons(con, job);
3630
3631 if (requested != NULL)
3632 con->response->request.status.status_code = IPP_OK_SUBST;
3633 else
3634 con->response->request.status.status_code = IPP_OK;
3635 }
3636
3637
3638 /*
3639 * 'get_ppds()' - Get the list of PPD files on the local system.
3640 */
3641
3642 static void
3643 get_ppds(client_t *con) /* I - Client connection */
3644 {
3645 LogMessage(L_DEBUG2, "get_ppds(%d)\n", con->http.fd);
3646
3647 /*
3648 * Copy the PPD attributes to the response using the requested-attributes
3649 * attribute that may be provided by the client.
3650 */
3651
3652 copy_attrs(con->response, PPDs,
3653 ippFindAttribute(con->request, "requested-attributes",
3654 IPP_TAG_KEYWORD), IPP_TAG_ZERO, IPP_TAG_COPY);
3655
3656 con->response->request.status.status_code = IPP_OK;
3657 }
3658
3659
3660 /*
3661 * 'get_printer_attrs()' - Get printer attributes.
3662 */
3663
3664 static void
3665 get_printer_attrs(client_t *con, /* I - Client connection */
3666 ipp_attribute_t *uri) /* I - Printer URI */
3667 {
3668 const char *dest; /* Destination */
3669 cups_ptype_t dtype; /* Destination type (printer or class) */
3670 char method[HTTP_MAX_URI],
3671 /* Method portion of URI */
3672 username[HTTP_MAX_URI],
3673 /* Username portion of URI */
3674 host[HTTP_MAX_URI],
3675 /* Host portion of URI */
3676 resource[HTTP_MAX_URI];
3677 /* Resource portion of URI */
3678 int port; /* Port portion of URI */
3679 printer_t *printer; /* Printer/class */
3680 time_t curtime; /* Current time */
3681 int i; /* Looping var */
3682 ipp_attribute_t *requested, /* requested-attributes */
3683 *history; /* History collection */
3684 int need_history; /* Need to send history collection? */
3685
3686
3687 LogMessage(L_DEBUG2, "get_printer_attrs(%d, %s)\n", con->http.fd,
3688 uri->values[0].string.text);
3689
3690 /*
3691 * Is the destination valid?
3692 */
3693
3694 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3695
3696 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
3697 {
3698 /*
3699 * Bad URI...
3700 */
3701
3702 LogMessage(L_ERROR, "get_printer_attrs: resource name \'%s\' no good!", resource);
3703 send_ipp_error(con, IPP_NOT_FOUND);
3704 return;
3705 }
3706
3707 if (dtype & CUPS_PRINTER_CLASS)
3708 printer = FindClass(dest);
3709 else
3710 printer = FindPrinter(dest);
3711
3712 curtime = time(NULL);
3713
3714 /*
3715 * Copy the printer attributes to the response using requested-attributes
3716 * and document-format attributes that may be provided by the client.
3717 */
3718
3719 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
3720 printer->state);
3721
3722 add_printer_state_reasons(con, printer);
3723
3724 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
3725 "printer-state-message", NULL, printer->state_message);
3726
3727 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
3728 printer->accepting);
3729
3730 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3731 "printer-up-time", curtime);
3732 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3733 "printer-state-time", printer->state_time);
3734 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
3735 ippTimeToDate(curtime));
3736
3737 add_queued_job_count(con, printer);
3738
3739 requested = ippFindAttribute(con->request, "requested-attributes",
3740 IPP_TAG_KEYWORD);
3741
3742 copy_attrs(con->response, printer->attrs, requested, IPP_TAG_ZERO, 0);
3743 copy_attrs(con->response, CommonData, requested, IPP_TAG_ZERO, IPP_TAG_COPY);
3744
3745 need_history = 0;
3746
3747 if (MaxPrinterHistory > 0 && printer->num_history > 0 && requested)
3748 {
3749 for (i = 0; i < requested->num_values; i ++)
3750 if (!strcmp(requested->values[i].string.text, "all") ||
3751 !strcmp(requested->values[i].string.text, "printer-state-history"))
3752 {
3753 need_history = 1;
3754 break;
3755 }
3756 }
3757
3758 if (need_history)
3759 {
3760 history = ippAddCollections(con->response, IPP_TAG_PRINTER,
3761 "printer-state-history",
3762 printer->num_history, NULL);
3763
3764 for (i = 0; i < printer->num_history; i ++)
3765 copy_attrs(history->values[i].collection = ippNew(), printer->history[i],
3766 NULL, IPP_TAG_ZERO, 0);
3767 }
3768
3769 con->response->request.status.status_code = requested ? IPP_OK_SUBST : IPP_OK;
3770 }
3771
3772
3773 /*
3774 * 'get_printers()' - Get a list of printers or classes.
3775 */
3776
3777 static void
3778 get_printers(client_t *con, /* I - Client connection */
3779 int type) /* I - 0 or CUPS_PRINTER_CLASS */
3780 {
3781 int i; /* Looping var */
3782 ipp_attribute_t *requested, /* requested-attributes */
3783 *history, /* History collection */
3784 *attr; /* Current attribute */
3785 int need_history; /* Need to send history collection? */
3786 int limit; /* Maximum number of printers to return */
3787 int count; /* Number of printers that match */
3788 printer_t *printer; /* Current printer pointer */
3789 time_t curtime; /* Current time */
3790 int printer_type, /* printer-type attribute */
3791 printer_mask; /* printer-type-mask attribute */
3792 char *location; /* Location string */
3793 char name[IPP_MAX_NAME],
3794 /* Printer name */
3795 *nameptr; /* Pointer into name */
3796 printer_t *iclass; /* Implicit class */
3797
3798
3799 LogMessage(L_DEBUG2, "get_printers(%d, %x)\n", con->http.fd, type);
3800
3801 /*
3802 * See if they want to limit the number of printers reported...
3803 */
3804
3805 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
3806 limit = attr->values[0].integer;
3807 else
3808 limit = 10000000;
3809
3810 /*
3811 * Support filtering...
3812 */
3813
3814 if ((attr = ippFindAttribute(con->request, "printer-type", IPP_TAG_ENUM)) != NULL)
3815 printer_type = attr->values[0].integer;
3816 else
3817 printer_type = 0;
3818
3819 if ((attr = ippFindAttribute(con->request, "printer-type-mask", IPP_TAG_ENUM)) != NULL)
3820 printer_mask = attr->values[0].integer;
3821 else
3822 printer_mask = 0;
3823
3824 if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
3825 location = attr->values[0].string.text;
3826 else
3827 location = NULL;
3828
3829 requested = ippFindAttribute(con->request, "requested-attributes",
3830 IPP_TAG_KEYWORD);
3831
3832 need_history = 0;
3833
3834 if (MaxPrinterHistory > 0 && requested)
3835 {
3836 for (i = 0; i < requested->num_values; i ++)
3837 if (!strcmp(requested->values[i].string.text, "all") ||
3838 !strcmp(requested->values[i].string.text, "printer-state-history"))
3839 {
3840 need_history = 1;
3841 break;
3842 }
3843 }
3844
3845 /*
3846 * OK, build a list of printers for this printer...
3847 */
3848
3849 curtime = time(NULL);
3850
3851 for (count = 0, printer = Printers;
3852 count < limit && printer != NULL;
3853 printer = printer->next)
3854 if ((printer->type & CUPS_PRINTER_CLASS) == type &&
3855 (printer->type & printer_mask) == printer_type &&
3856 (location == NULL || printer->location == NULL ||
3857 strcasecmp(printer->location, location) == 0))
3858 {
3859 /*
3860 * If HideImplicitMembers is enabled, see if this printer or class
3861 * is a member of an implicit class...
3862 */
3863
3864 if (ImplicitClasses && HideImplicitMembers &&
3865 (printer->type & CUPS_PRINTER_REMOTE))
3866 {
3867 /*
3868 * Make a copy of the printer name...
3869 */
3870
3871 strlcpy(name, printer->name, sizeof(name));
3872
3873 if ((nameptr = strchr(name, '@')) != NULL)
3874 {
3875 /*
3876 * Strip trailing @server...
3877 */
3878
3879 *nameptr = '\0';
3880
3881 /*
3882 * Find the core printer, if any...
3883 */
3884
3885 if ((iclass = FindPrinter(name)) != NULL &&
3886 (iclass->type & CUPS_PRINTER_IMPLICIT))
3887 continue;
3888 }
3889 }
3890
3891 /*
3892 * Add the group separator as needed...
3893 */
3894
3895 if (count > 0)
3896 ippAddSeparator(con->response);
3897
3898 count ++;
3899
3900 /*
3901 * Send the following attributes for each printer:
3902 *
3903 * printer-state
3904 * printer-state-message
3905 * printer-is-accepting-jobs
3906 * + all printer attributes
3907 */
3908
3909 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
3910 "printer-state", printer->state);
3911
3912 add_printer_state_reasons(con, printer);
3913
3914 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
3915 "printer-state-message", NULL, printer->state_message);
3916
3917 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
3918 printer->accepting);
3919
3920 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3921 "printer-up-time", curtime);
3922 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3923 "printer-state-time", printer->state_time);
3924 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
3925 ippTimeToDate(curtime));
3926
3927 add_queued_job_count(con, printer);
3928
3929 copy_attrs(con->response, printer->attrs, requested, IPP_TAG_ZERO, 0);
3930
3931 copy_attrs(con->response, CommonData, requested, IPP_TAG_ZERO,
3932 IPP_TAG_COPY);
3933
3934 if (need_history && printer->num_history > 0)
3935 {
3936 history = ippAddCollections(con->response, IPP_TAG_PRINTER,
3937 "printer-state-history",
3938 printer->num_history, NULL);
3939
3940 for (i = 0; i < printer->num_history; i ++)
3941 copy_attrs(history->values[i].collection = ippNew(),
3942 printer->history[i], NULL, IPP_TAG_ZERO, 0);
3943 }
3944 }
3945
3946 con->response->request.status.status_code = requested ? IPP_OK_SUBST : IPP_OK;
3947 }
3948
3949
3950 /*
3951 * 'hold_job()' - Hold a print job.
3952 */
3953
3954 static void
3955 hold_job(client_t *con, /* I - Client connection */
3956 ipp_attribute_t *uri) /* I - Job or Printer URI */
3957 {
3958 ipp_attribute_t *attr, /* Current job-hold-until */
3959 *newattr; /* New job-hold-until */
3960 int jobid; /* Job ID */
3961 char method[HTTP_MAX_URI],
3962 /* Method portion of URI */
3963 username[HTTP_MAX_URI],
3964 /* Username portion of URI */
3965 host[HTTP_MAX_URI],
3966 /* Host portion of URI */
3967 resource[HTTP_MAX_URI];
3968 /* Resource portion of URI */
3969 int port; /* Port portion of URI */
3970 job_t *job; /* Job information */
3971
3972
3973 LogMessage(L_DEBUG2, "hold_job(%d, %s)\n", con->http.fd,
3974 uri->values[0].string.text);
3975
3976 /*
3977 * Verify that the POST operation was done to a valid URI.
3978 */
3979
3980 if (strncmp(con->uri, "/classes/", 9) != 0 &&
3981 strncmp(con->uri, "/jobs/", 5) != 0 &&
3982 strncmp(con->uri, "/printers/", 10) != 0)
3983 {
3984 LogMessage(L_ERROR, "hold_job: hold request on bad resource \'%s\'!",
3985 con->uri);
3986 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3987 return;
3988 }
3989
3990 /*
3991 * See if we have a job URI or a printer URI...
3992 */
3993
3994 if (strcmp(uri->name, "printer-uri") == 0)
3995 {
3996 /*
3997 * Got a printer URI; see if we also have a job-id attribute...
3998 */
3999
4000 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4001 {
4002 LogMessage(L_ERROR, "hold_job: got a printer-uri attribute but no job-id!");
4003 send_ipp_error(con, IPP_BAD_REQUEST);
4004 return;
4005 }
4006
4007 jobid = attr->values[0].integer;
4008 }
4009 else
4010 {
4011 /*
4012 * Got a job URI; parse it to get the job ID...
4013 */
4014
4015 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4016
4017 if (strncmp(resource, "/jobs/", 6) != 0)
4018 {
4019 /*
4020 * Not a valid URI!
4021 */
4022
4023 LogMessage(L_ERROR, "hold_job: bad job-uri attribute \'%s\'!",
4024 uri->values[0].string.text);
4025 send_ipp_error(con, IPP_BAD_REQUEST);
4026 return;
4027 }
4028
4029 jobid = atoi(resource + 6);
4030 }
4031
4032 /*
4033 * See if the job exists...
4034 */
4035
4036 if ((job = FindJob(jobid)) == NULL)
4037 {
4038 /*
4039 * Nope - return a "not found" error...
4040 */
4041
4042 LogMessage(L_ERROR, "hold_job: job #%d doesn't exist!", jobid);
4043 send_ipp_error(con, IPP_NOT_FOUND);
4044 return;
4045 }
4046
4047 /*
4048 * See if the job is owned by the requesting user...
4049 */
4050
4051 if (!validate_user(con, job->username, username, sizeof(username)))
4052 {
4053 LogMessage(L_ERROR, "hold_job: \"%s\" not authorized to hold job id %d owned by \"%s\"!",
4054 username, jobid, job->username);
4055 send_ipp_error(con, IPP_FORBIDDEN);
4056 return;
4057 }
4058
4059 /*
4060 * Hold the job and return...
4061 */
4062
4063 HoldJob(jobid);
4064
4065 if ((newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4066 newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
4067
4068 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4069 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4070
4071 if (attr != NULL)
4072 {
4073 /*
4074 * Free the old hold value and copy the new one over...
4075 */
4076
4077 free(attr->values[0].string.text);
4078
4079 if (newattr != NULL)
4080 {
4081 attr->value_tag = newattr->value_tag;
4082 attr->values[0].string.text = strdup(newattr->values[0].string.text);
4083 }
4084 else
4085 {
4086 attr->value_tag = IPP_TAG_KEYWORD;
4087 attr->values[0].string.text = strdup("indefinite");
4088 }
4089
4090 /*
4091 * Hold job until specified time...
4092 */
4093
4094 SetJobHoldUntil(job->id, attr->values[0].string.text);
4095 }
4096
4097 LogMessage(L_INFO, "Job %d was held by \'%s\'.", jobid, username);
4098
4099 con->response->request.status.status_code = IPP_OK;
4100 }
4101
4102
4103 /*
4104 * 'move_job()' - Move a job to a new destination.
4105 */
4106
4107 static void
4108 move_job(client_t *con, /* I - Client connection */
4109 ipp_attribute_t *uri) /* I - Job URI */
4110 {
4111 ipp_attribute_t *attr; /* Current attribute */
4112 int jobid; /* Job ID */
4113 job_t *job; /* Current job */
4114 const char *dest; /* Destination */
4115 cups_ptype_t dtype; /* Destination type (printer or class) */
4116 char method[HTTP_MAX_URI],
4117 /* Method portion of URI */
4118 username[HTTP_MAX_URI],
4119 /* Username portion of URI */
4120 host[HTTP_MAX_URI],
4121 /* Host portion of URI */
4122 resource[HTTP_MAX_URI];
4123 /* Resource portion of URI */
4124 int port; /* Port portion of URI */
4125
4126
4127 LogMessage(L_DEBUG2, "move_job(%d, %s)\n", con->http.fd,
4128 uri->values[0].string.text);
4129
4130 /*
4131 * See if we have a job URI or a printer URI...
4132 */
4133
4134 if (strcmp(uri->name, "printer-uri") == 0)
4135 {
4136 /*
4137 * Got a printer URI; see if we also have a job-id attribute...
4138 */
4139
4140 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4141 {
4142 LogMessage(L_ERROR, "move_job: got a printer-uri attribute but no job-id!");
4143 send_ipp_error(con, IPP_BAD_REQUEST);
4144 return;
4145 }
4146
4147 jobid = attr->values[0].integer;
4148 }
4149 else
4150 {
4151 /*
4152 * Got a job URI; parse it to get the job ID...
4153 */
4154
4155 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4156
4157 if (strncmp(resource, "/jobs/", 6) != 0)
4158 {
4159 /*
4160 * Not a valid URI!
4161 */
4162
4163 LogMessage(L_ERROR, "move_job: bad job-uri attribute \'%s\'!\n",
4164 uri->values[0].string.text);
4165 send_ipp_error(con, IPP_BAD_REQUEST);
4166 return;
4167 }
4168
4169 jobid = atoi(resource + 6);
4170 }
4171
4172 /*
4173 * See if the job exists...
4174 */
4175
4176 if ((job = FindJob(jobid)) == NULL)
4177 {
4178 /*
4179 * Nope - return a "not found" error...
4180 */
4181
4182 LogMessage(L_ERROR, "move_job: job #%d doesn't exist!", jobid);
4183 send_ipp_error(con, IPP_NOT_FOUND);
4184 return;
4185 }
4186
4187 /*
4188 * See if the job has been completed...
4189 */
4190
4191 if (job->state->values[0].integer > IPP_JOB_STOPPED)
4192 {
4193 /*
4194 * Return a "not-possible" error...
4195 */
4196
4197 LogMessage(L_ERROR, "move_job: job #%d is finished and cannot be altered!", jobid);
4198 send_ipp_error(con, IPP_NOT_POSSIBLE);
4199 return;
4200 }
4201
4202 /*
4203 * See if the job is owned by the requesting user...
4204 */
4205
4206 if (!validate_user(con, job->username, username, sizeof(username)))
4207 {
4208 LogMessage(L_ERROR, "move_job: \"%s\" not authorized to move job id %d owned by \"%s\"!",
4209 username, jobid, job->username);
4210 send_ipp_error(con, IPP_FORBIDDEN);
4211 return;
4212 }
4213
4214 if ((attr = ippFindAttribute(con->request, "job-printer-uri", IPP_TAG_URI)) == NULL)
4215 {
4216 /*
4217 * Need job-printer-uri...
4218 */
4219
4220 LogMessage(L_ERROR, "move_job: job-printer-uri attribute missing!");
4221 send_ipp_error(con, IPP_BAD_REQUEST);
4222 return;
4223 }
4224
4225 /*
4226 * Move the job to a different printer or class...
4227 */
4228
4229 httpSeparate(attr->values[0].string.text, method, username, host, &port,
4230 resource);
4231 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
4232 {
4233 /*
4234 * Bad URI...
4235 */
4236
4237 LogMessage(L_ERROR, "move_job: resource name \'%s\' no good!", resource);
4238 send_ipp_error(con, IPP_NOT_FOUND);
4239 return;
4240 }
4241
4242 MoveJob(jobid, dest);
4243
4244 /*
4245 * Start jobs if possible...
4246 */
4247
4248 CheckJobs();
4249
4250 /*
4251 * Return with "everything is OK" status...
4252 */
4253
4254 con->response->request.status.status_code = IPP_OK;
4255 }
4256
4257
4258 /*
4259 * 'ppd_add_default()' - Add a PPD default choice.
4260 */
4261
4262 static int /* O - Number of defaults */
4263 ppd_add_default(const char *option, /* I - Option name */
4264 const char *choice, /* I - Choice name */
4265 int num_defaults,
4266 /* I - Number of defaults */
4267 ppd_default_t **defaults)
4268 /* IO - Defaults */
4269 {
4270 int i; /* Looping var */
4271 ppd_default_t *temp; /* Temporary defaults array */
4272
4273
4274 /*
4275 * First check if the option already has a default value; the PPD spec
4276 * says that the first one is used...
4277 */
4278
4279 for (i = 0, temp = *defaults; i < num_defaults; i ++)
4280 if (!strcmp(option, temp[i].option))
4281 return (num_defaults);
4282
4283 /*
4284 * Now add the option...
4285 */
4286
4287 if (num_defaults == 0)
4288 temp = malloc(sizeof(ppd_default_t));
4289 else
4290 temp = realloc(*defaults, (num_defaults + 1) * sizeof(ppd_default_t));
4291
4292 if (!temp)
4293 {
4294 LogMessage(L_ERROR, "ppd_add_default: Unable to add default value for \"%s\" - %s",
4295 option, strerror(errno));
4296 return (num_defaults);
4297 }
4298
4299 *defaults = temp;
4300 temp += num_defaults;
4301
4302 strlcpy(temp->option, option, sizeof(temp->option));
4303 strlcpy(temp->choice, choice, sizeof(temp->choice));
4304
4305 return (num_defaults + 1);
4306 }
4307
4308
4309 /*
4310 * 'ppd_parse_line()' - Parse a PPD default line.
4311 */
4312
4313 static int /* O - 0 on success, -1 on failure */
4314 ppd_parse_line(const char *line, /* I - Line */
4315 char *option, /* O - Option name */
4316 int olen, /* I - Size of option name */
4317 char *choice, /* O - Choice name */
4318 int clen) /* I - Size of choice name */
4319 {
4320 /*
4321 * Verify this is a default option line...
4322 */
4323
4324 if (strncmp(line, "*Default", 8))
4325 return (-1);
4326
4327 /*
4328 * Read the option name...
4329 */
4330
4331 for (line += 8, olen --; isalnum(*line); line ++)
4332 if (olen > 0)
4333 {
4334 *option++ = *line;
4335 olen --;
4336 }
4337
4338 *option = '\0';
4339
4340 /*
4341 * Skip everything else up to the colon (:)...
4342 */
4343
4344 while (*line && *line != ':')
4345 line ++;
4346
4347 if (!*line)
4348 return (-1);
4349
4350 line ++;
4351
4352 /*
4353 * Now grab the option choice, skipping leading whitespace...
4354 */
4355
4356 while (isspace(*line))
4357 line ++;
4358
4359 for (clen --; isalnum(*line); line ++)
4360 if (clen > 0)
4361 {
4362 *choice++ = *line;
4363 clen --;
4364 }
4365
4366 *choice = '\0';
4367
4368 /*
4369 * Return with no errors...
4370 */
4371
4372 return (0);
4373 }
4374
4375
4376 /*
4377 * 'print_job()' - Print a file to a printer or class.
4378 */
4379
4380 static void
4381 print_job(client_t *con, /* I - Client connection */
4382 ipp_attribute_t *uri) /* I - Printer URI */
4383 {
4384 ipp_attribute_t *attr; /* Current attribute */
4385 ipp_attribute_t *format; /* Document-format attribute */
4386 const char *dest; /* Destination */
4387 cups_ptype_t dtype; /* Destination type (printer or class) */
4388 int priority; /* Job priority */
4389 char *title; /* Job name/title */
4390 job_t *job; /* Current job */
4391 char job_uri[HTTP_MAX_URI],
4392 /* Job URI */
4393 printer_uri[HTTP_MAX_URI],
4394 /* Printer URI */
4395 method[HTTP_MAX_URI],
4396 /* Method portion of URI */
4397 username[HTTP_MAX_URI],
4398 /* Username portion of URI */
4399 host[HTTP_MAX_URI],
4400 /* Host portion of URI */
4401 resource[HTTP_MAX_URI],
4402 /* Resource portion of URI */
4403 filename[1024]; /* Job filename */
4404 int port; /* Port portion of URI */
4405 mime_type_t *filetype; /* Type of file */
4406 char super[MIME_MAX_SUPER],
4407 /* Supertype of file */
4408 type[MIME_MAX_TYPE],
4409 /* Subtype of file */
4410 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
4411 /* Textual name of mime type */
4412 printer_t *printer; /* Printer data */
4413 struct stat fileinfo; /* File information */
4414 int kbytes; /* Size of file */
4415 int i; /* Looping var */
4416 int lowerpagerange; /* Page range bound */
4417 int compression; /* Document compression */
4418
4419
4420 LogMessage(L_DEBUG2, "print_job(%d, %s)\n", con->http.fd,
4421 uri->values[0].string.text);
4422
4423 /*
4424 * Verify that the POST operation was done to a valid URI.
4425 */
4426
4427 if (strncmp(con->uri, "/classes/", 9) != 0 &&
4428 strncmp(con->uri, "/printers/", 10) != 0)
4429 {
4430 LogMessage(L_ERROR, "print_job: cancel request on bad resource \'%s\'!",
4431 con->uri);
4432 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4433 return;
4434 }
4435
4436 /*
4437 * Validate job template attributes; for now just copies and page-ranges...
4438 */
4439
4440 if ((attr = ippFindAttribute(con->request, "copies", IPP_TAG_INTEGER)) != NULL)
4441 {
4442 if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
4443 {
4444 LogMessage(L_INFO, "print_job: bad copies value %d.",
4445 attr->values[0].integer);
4446 send_ipp_error(con, IPP_BAD_REQUEST);
4447 return;
4448 }
4449 }
4450
4451 if ((attr = ippFindAttribute(con->request, "page-ranges", IPP_TAG_RANGE)) != NULL)
4452 {
4453 for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
4454 {
4455 if (attr->values[i].range.lower < lowerpagerange ||
4456 attr->values[i].range.lower > attr->values[i].range.upper)
4457 {
4458 LogMessage(L_ERROR, "print_job: bad page-ranges values %d-%d.",
4459 attr->values[i].range.lower, attr->values[i].range.upper);
4460 send_ipp_error(con, IPP_BAD_REQUEST);
4461 return;
4462 }
4463
4464 lowerpagerange = attr->values[i].range.upper + 1;
4465 }
4466 }
4467
4468 /*
4469 * OK, see if the client is sending the document compressed - CUPS
4470 * only supports "none" and "gzip".
4471 */
4472
4473 compression = CUPS_FILE_NONE;
4474
4475 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL)
4476 {
4477 if (strcmp(attr->values[0].string.text, "none")
4478 #ifdef HAVE_LIBZ
4479 && strcmp(attr->values[0].string.text, "gzip")
4480 #endif /* HAVE_LIBZ */
4481 )
4482 {
4483 LogMessage(L_ERROR, "print_job: Unsupported compression \"%s\"!",
4484 attr->values[0].string.text);
4485 send_ipp_error(con, IPP_ATTRIBUTES);
4486 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
4487 "compression", NULL, attr->values[0].string.text);
4488 return;
4489 }
4490
4491 #ifdef HAVE_LIBZ
4492 if (!strcmp(attr->values[0].string.text, "gzip"))
4493 compression = CUPS_FILE_GZIP;
4494 #endif /* HAVE_LIBZ */
4495 }
4496
4497 /*
4498 * Do we have a file to print?
4499 */
4500
4501 if (!con->filename)
4502 {
4503 LogMessage(L_ERROR, "print_job: No file!?!");
4504 send_ipp_error(con, IPP_BAD_REQUEST);
4505 return;
4506 }
4507
4508 /*
4509 * Is it a format we support?
4510 */
4511
4512 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
4513 {
4514 /*
4515 * Grab format from client...
4516 */
4517
4518 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
4519 {
4520 LogMessage(L_ERROR, "print_job: could not scan type \'%s\'!",
4521 format->values[0].string.text);
4522 send_ipp_error(con, IPP_BAD_REQUEST);
4523 return;
4524 }
4525 }
4526 else
4527 {
4528 /*
4529 * No document format attribute? Auto-type it!
4530 */
4531
4532 strcpy(super, "application");
4533 strcpy(type, "octet-stream");
4534 }
4535
4536 if (strcmp(super, "application") == 0 &&
4537 strcmp(type, "octet-stream") == 0)
4538 {
4539 /*
4540 * Auto-type the file...
4541 */
4542
4543 LogMessage(L_DEBUG, "print_job: auto-typing file...");
4544
4545 filetype = mimeFileType(MimeDatabase, con->filename, &compression);
4546
4547 if (filetype != NULL)
4548 {
4549 /*
4550 * Replace the document-format attribute value with the auto-typed one.
4551 */
4552
4553 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
4554 filetype->type);
4555
4556 if (format != NULL)
4557 {
4558 free(format->values[0].string.text);
4559 format->values[0].string.text = strdup(mimetype);
4560 }
4561 else
4562 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
4563 "document-format", NULL, mimetype);
4564 }
4565 else
4566 filetype = mimeType(MimeDatabase, super, type);
4567 }
4568 else
4569 filetype = mimeType(MimeDatabase, super, type);
4570
4571 if (filetype == NULL)
4572 {
4573 LogMessage(L_ERROR, "print_job: Unsupported format \'%s/%s\'!",
4574 super, type);
4575 LogMessage(L_INFO, "Hint: Do you have the raw file printing rules enabled?");
4576 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
4577
4578 if (format)
4579 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
4580 "document-format", NULL, format->values[0].string.text);
4581
4582 return;
4583 }
4584
4585 LogMessage(L_DEBUG, "print_job: request file type is %s/%s.",
4586 filetype->super, filetype->type);
4587
4588 /*
4589 * Read any embedded job ticket info from PS files...
4590 */
4591
4592 if (strcasecmp(filetype->super, "application") == 0 &&
4593 strcasecmp(filetype->type, "postscript") == 0)
4594 read_ps_job_ticket(con);
4595
4596 /*
4597 * Is the destination valid?
4598 */
4599
4600 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4601
4602 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
4603 {
4604 /*
4605 * Bad URI...
4606 */
4607
4608 LogMessage(L_ERROR, "print_job: resource name \'%s\' no good!", resource);
4609 send_ipp_error(con, IPP_NOT_FOUND);
4610 return;
4611 }
4612
4613 /*
4614 * See if the printer is accepting jobs...
4615 */
4616
4617 if (dtype & CUPS_PRINTER_CLASS)
4618 {
4619 printer = FindClass(dest);
4620 #ifdef AF_INET6
4621 if (con->http.hostaddr.addr.sa_family == AF_INET6)
4622 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/classes/%s",
4623 ServerName, ntohs(con->http.hostaddr.ipv6.sin6_port), dest);
4624 else
4625 #endif /* AF_INET6 */
4626 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/classes/%s",
4627 ServerName, ntohs(con->http.hostaddr.ipv4.sin_port), dest);
4628 }
4629 else
4630 {
4631 printer = FindPrinter(dest);
4632
4633 #ifdef AF_INET6
4634 if (con->http.hostaddr.addr.sa_family == AF_INET6)
4635 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/printers/%s",
4636 ServerName, ntohs(con->http.hostaddr.ipv6.sin6_port), dest);
4637 else
4638 #endif /* AF_INET6 */
4639 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/printers/%s",
4640 ServerName, ntohs(con->http.hostaddr.ipv4.sin_port), dest);
4641 }
4642
4643 if (!printer->accepting)
4644 {
4645 LogMessage(L_INFO, "print_job: destination \'%s\' is not accepting jobs.",
4646 dest);
4647 send_ipp_error(con, IPP_NOT_ACCEPTING);
4648 return;
4649 }
4650
4651 /*
4652 * Make sure we aren't over our limit...
4653 */
4654
4655 if (NumJobs >= MaxJobs && MaxJobs)
4656 CleanJobs();
4657
4658 if (NumJobs >= MaxJobs && MaxJobs)
4659 {
4660 LogMessage(L_INFO, "print_job: too many jobs - %d jobs, max jobs is %d.",
4661 NumJobs, MaxJobs);
4662 send_ipp_error(con, IPP_NOT_POSSIBLE);
4663 return;
4664 }
4665
4666 if (!check_quotas(con, printer))
4667 {
4668 send_ipp_error(con, IPP_NOT_POSSIBLE);
4669 return;
4670 }
4671
4672 /*
4673 * Create the job and set things up...
4674 */
4675
4676 if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
4677 priority = attr->values[0].integer;
4678 else
4679 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
4680 priority = 50);
4681
4682 if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
4683 title = attr->values[0].string.text;
4684 else
4685 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
4686 title = "Untitled");
4687
4688 if ((job = AddJob(priority, printer->name)) == NULL)
4689 {
4690 LogMessage(L_ERROR, "print_job: unable to add job for destination \'%s\'!",
4691 dest);
4692 send_ipp_error(con, IPP_INTERNAL_ERROR);
4693 return;
4694 }
4695
4696 job->dtype = dtype;
4697 job->attrs = con->request;
4698 con->request = NULL;
4699
4700 /*
4701 * Copy the rest of the job info...
4702 */
4703
4704 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
4705
4706 if (con->username[0])
4707 SetString(&job->username, con->username);
4708 else if (attr != NULL)
4709 {
4710 LogMessage(L_DEBUG, "print_job: requesting-user-name = \'%s\'",
4711 attr->values[0].string.text);
4712
4713 SetString(&job->username, attr->values[0].string.text);
4714 }
4715 else
4716 SetString(&job->username, "anonymous");
4717
4718 if (attr == NULL)
4719 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
4720 NULL, job->username);
4721 else
4722 {
4723 attr->group_tag = IPP_TAG_JOB;
4724 SetString(&attr->name, "job-originating-user-name");
4725 }
4726
4727 /*
4728 * Add remaining job attributes...
4729 */
4730
4731 if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
4732 IPP_TAG_ZERO)) != NULL)
4733 {
4734 /*
4735 * Request contains a job-originating-host-name attribute; validate it...
4736 */
4737
4738 if (attr->value_tag != IPP_TAG_NAME ||
4739 attr->num_values != 1 ||
4740 strcmp(con->http.hostname, "localhost") != 0)
4741 {
4742 /*
4743 * Can't override the value if we aren't connected via localhost.
4744 * Also, we can only have 1 value and it must be a name value.
4745 */
4746
4747 int i; /* Looping var */
4748
4749 switch (attr->value_tag)
4750 {
4751 case IPP_TAG_STRING :
4752 case IPP_TAG_TEXTLANG :
4753 case IPP_TAG_NAMELANG :
4754 case IPP_TAG_TEXT :
4755 case IPP_TAG_NAME :
4756 case IPP_TAG_KEYWORD :
4757 case IPP_TAG_URI :
4758 case IPP_TAG_URISCHEME :
4759 case IPP_TAG_CHARSET :
4760 case IPP_TAG_LANGUAGE :
4761 case IPP_TAG_MIMETYPE :
4762 /*
4763 * Free old strings...
4764 */
4765
4766 for (i = 0; i < attr->num_values; i ++)
4767 {
4768 free(attr->values[i].string.text);
4769 attr->values[i].string.text = NULL;
4770 if (attr->values[i].string.charset)
4771 {
4772 free(attr->values[i].string.charset);
4773 attr->values[i].string.charset = NULL;
4774 }
4775 }
4776
4777 default :
4778 break;
4779 }
4780
4781 /*
4782 * Use the default connection hostname instead...
4783 */
4784
4785 attr->value_tag = IPP_TAG_NAME;
4786 attr->num_values = 1;
4787 attr->values[0].string.text = strdup(con->http.hostname);
4788 }
4789
4790 attr->group_tag = IPP_TAG_JOB;
4791 }
4792 else
4793 {
4794 /*
4795 * No job-originating-host-name attribute, so use the hostname from
4796 * the connection...
4797 */
4798
4799 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
4800 "job-originating-host-name", NULL, con->http.hostname);
4801 }
4802
4803 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
4804 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
4805 "job-state", IPP_JOB_PENDING);
4806 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
4807 "job-media-sheets-completed", 0);
4808 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
4809 printer_uri);
4810 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
4811 title);
4812
4813 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) == NULL)
4814 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
4815 "job-k-octets", 0);
4816
4817 if (stat(con->filename, &fileinfo))
4818 kbytes = 0;
4819 else
4820 kbytes = (fileinfo.st_size + 1023) / 1024;
4821
4822 UpdateQuota(printer, job->username, 0, kbytes);
4823 attr->values[0].integer += kbytes;
4824
4825 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
4826 time(NULL));
4827 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
4828 "time-at-processing", 0);
4829 attr->value_tag = IPP_TAG_NOVALUE;
4830 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
4831 "time-at-completed", 0);
4832 attr->value_tag = IPP_TAG_NOVALUE;
4833
4834 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4835 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4836 if (attr == NULL)
4837 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
4838 "job-hold-until", NULL, "no-hold");
4839
4840 if (attr != NULL && strcmp(attr->values[0].string.text, "no-hold") != 0 &&
4841 !(printer->type & CUPS_PRINTER_REMOTE))
4842 {
4843 /*
4844 * Hold job until specified time...
4845 */
4846
4847 job->state->values[0].integer = IPP_JOB_HELD;
4848 SetJobHoldUntil(job->id, attr->values[0].string.text);
4849 }
4850
4851 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
4852 Classification)
4853 {
4854 /*
4855 * Add job sheets options...
4856 */
4857
4858 if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) == NULL)
4859 {
4860 LogMessage(L_DEBUG, "Adding default job-sheets values \"%s,%s\"...",
4861 printer->job_sheets[0], printer->job_sheets[1]);
4862
4863 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
4864 2, NULL, NULL);
4865 attr->values[0].string.text = strdup(printer->job_sheets[0]);
4866 attr->values[1].string.text = strdup(printer->job_sheets[1]);
4867 }
4868
4869 job->job_sheets = attr;
4870
4871 /*
4872 * Enforce classification level if set...
4873 */
4874
4875 if (Classification)
4876 {
4877 if (ClassifyOverride)
4878 {
4879 if (strcmp(attr->values[0].string.text, "none") == 0 &&
4880 (attr->num_values == 1 ||
4881 strcmp(attr->values[1].string.text, "none") == 0))
4882 {
4883 /*
4884 * Force the leading banner to have the classification on it...
4885 */
4886
4887 SetString(&attr->values[0].string.text, Classification);
4888 }
4889 else if (attr->num_values == 2 &&
4890 strcmp(attr->values[0].string.text, attr->values[1].string.text) != 0 &&
4891 strcmp(attr->values[0].string.text, "none") != 0 &&
4892 strcmp(attr->values[1].string.text, "none") != 0)
4893 {
4894 /*
4895 * Can't put two different security markings on the same document!
4896 */
4897
4898 SetString(&attr->values[1].string.text, attr->values[0].string.text);
4899 }
4900 }
4901 else if (strcmp(attr->values[0].string.text, Classification) != 0 &&
4902 (attr->num_values == 1 ||
4903 strcmp(attr->values[1].string.text, Classification) != 0))
4904 {
4905 /*
4906 * Force the leading banner to have the classification on it...
4907 */
4908
4909 SetString(&attr->values[0].string.text, Classification);
4910 }
4911 }
4912
4913 /*
4914 * Add the starting sheet...
4915 */
4916
4917 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
4918 {
4919 LogMessage(L_INFO, "Adding start banner page \"%s\" to job %d.",
4920 attr->values[0].string.text, job->id);
4921
4922 kbytes = copy_banner(con, job, attr->values[0].string.text);
4923
4924 UpdateQuota(printer, job->username, 0, kbytes);
4925 }
4926 }
4927 else if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
4928 job->sheets = attr;
4929
4930 /*
4931 * Add the job file...
4932 */
4933
4934 if (add_file(con, job, filetype, compression))
4935 return;
4936
4937 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
4938 job->num_files);
4939 rename(con->filename, filename);
4940 ClearString(&con->filename);
4941
4942 /*
4943 * See if we need to add the ending sheet...
4944 */
4945
4946 if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
4947 attr->num_values > 1)
4948 {
4949 /*
4950 * Yes...
4951 */
4952
4953 LogMessage(L_INFO, "Adding end banner page \"%s\" to job %d.",
4954 attr->values[1].string.text, job->id);
4955
4956 kbytes = copy_banner(con, job, attr->values[1].string.text);
4957
4958 UpdateQuota(printer, job->username, 0, kbytes);
4959 }
4960
4961 /*
4962 * Log and save the job...
4963 */
4964
4965 LogMessage(L_INFO, "Job %d queued on \'%s\' by \'%s\'.", job->id,
4966 job->dest, job->username);
4967 LogMessage(L_DEBUG, "Job %d hold_until = %d", job->id, (int)job->hold_until);
4968
4969 SaveJob(job->id);
4970
4971 /*
4972 * Start the job if possible...
4973 */
4974
4975 CheckJobs();
4976
4977 /*
4978 * Fill in the response info...
4979 */
4980
4981 #ifdef AF_INET6
4982 if (con->http.hostaddr.addr.sa_family == AF_INET6)
4983 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
4984 ntohs(con->http.hostaddr.ipv6.sin6_port), job->id);
4985 else
4986 #endif /* AF_INET6 */
4987 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
4988 ntohs(con->http.hostaddr.ipv4.sin_port), job->id);
4989
4990 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
4991
4992 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
4993
4994 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
4995 job->state->values[0].integer);
4996 add_job_state_reasons(con, job);
4997
4998 con->response->request.status.status_code = IPP_OK;
4999 }
5000
5001
5002 /*
5003 * 'read_ps_job_ticket()' - Reads a job ticket embedded in a PS file.
5004 *
5005 * This function only gets called when printing a single PostScript
5006 * file using the Print-Job operation. It doesn't work for Create-Job +
5007 * Send-File, since the job attributes need to be set at job creation
5008 * time for banners to work. The embedded PS job ticket stuff is here
5009 * only to allow the Windows printer driver for CUPS to pass in JCL
5010 * options and IPP attributes which otherwise would be lost.
5011 *
5012 * The format of a PS job ticket is simple:
5013 *
5014 * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
5015 *
5016 * %cupsJobTicket: attr1=value1
5017 * %cupsJobTicket: attr2=value2
5018 * ...
5019 * %cupsJobTicket: attrN=valueN
5020 *
5021 * Job ticket lines must appear immediately after the first line that
5022 * specifies PostScript format (%!PS-Adobe-3.0), and CUPS will stop
5023 * looking for job ticket info when it finds a line that does not begin
5024 * with "%cupsJobTicket:".
5025 *
5026 * The maximum length of a job ticket line, including the prefix, is
5027 * 255 characters to conform with the Adobe DSC.
5028 *
5029 * Read-only attributes are rejected with a notice to the error log in
5030 * case a malicious user tries anything. Since the job ticket is read
5031 * prior to attribute validation in print_job(), job ticket attributes
5032 * will go through the same validation as IPP attributes...
5033 */
5034
5035 static void
5036 read_ps_job_ticket(client_t *con) /* I - Client connection */
5037 {
5038 cups_file_t *fp; /* File to read from */
5039 char line[256]; /* Line data */
5040 int num_options; /* Number of options */
5041 cups_option_t *options; /* Options */
5042 ipp_t *ticket; /* New attributes */
5043 ipp_attribute_t *attr, /* Current attribute */
5044 *attr2, /* Job attribute */
5045 *prev2; /* Previous job attribute */
5046
5047
5048 /*
5049 * First open the print file...
5050 */
5051
5052 if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
5053 {
5054 LogMessage(L_ERROR, "read_ps_job_ticket: Unable to open PostScript print file - %s",
5055 strerror(errno));
5056 return;
5057 }
5058
5059 /*
5060 * Skip the first line...
5061 */
5062
5063 if (cupsFileGets(fp, line, sizeof(line)) == NULL)
5064 {
5065 LogMessage(L_ERROR, "read_ps_job_ticket: Unable to read from PostScript print file - %s",
5066 strerror(errno));
5067 cupsFileClose(fp);
5068 return;
5069 }
5070
5071 if (strncmp(line, "%!PS-Adobe-", 11) != 0)
5072 {
5073 /*
5074 * Not a DSC-compliant file, so no job ticket info will be available...
5075 */
5076
5077 cupsFileClose(fp);
5078 return;
5079 }
5080
5081 /*
5082 * Read job ticket info from the file...
5083 */
5084
5085 num_options = 0;
5086 options = NULL;
5087
5088 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
5089 {
5090 /*
5091 * Stop at the first non-ticket line...
5092 */
5093
5094 if (strncmp(line, "%cupsJobTicket:", 15) != 0)
5095 break;
5096
5097 /*
5098 * Add the options to the option array...
5099 */
5100
5101 num_options = cupsParseOptions(line + 15, num_options, &options);
5102 }
5103
5104 /*
5105 * Done with the file; see if we have any options...
5106 */
5107
5108 cupsFileClose(fp);
5109
5110 if (num_options == 0)
5111 return;
5112
5113 /*
5114 * OK, convert the options to an attribute list, and apply them to
5115 * the request...
5116 */
5117
5118 ticket = ippNew();
5119 cupsEncodeOptions(ticket, num_options, options);
5120
5121 /*
5122 * See what the user wants to change.
5123 */
5124
5125 for (attr = ticket->attrs; attr != NULL; attr = attr->next)
5126 {
5127 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
5128 continue;
5129
5130 if (strcmp(attr->name, "job-originating-host-name") == 0 ||
5131 strcmp(attr->name, "job-originating-user-name") == 0 ||
5132 strcmp(attr->name, "job-media-sheets-completed") == 0 ||
5133 strcmp(attr->name, "job-k-octets") == 0 ||
5134 strcmp(attr->name, "job-id") == 0 ||
5135 strncmp(attr->name, "job-state", 9) == 0 ||
5136 strncmp(attr->name, "time-at-", 8) == 0)
5137 continue; /* Read-only attrs */
5138
5139 if ((attr2 = ippFindAttribute(con->request, attr->name, IPP_TAG_ZERO)) != NULL)
5140 {
5141 /*
5142 * Some other value; first free the old value...
5143 */
5144
5145 if (con->request->attrs == attr2)
5146 {
5147 con->request->attrs = attr2->next;
5148 prev2 = NULL;
5149 }
5150 else
5151 {
5152 for (prev2 = con->request->attrs; prev2 != NULL; prev2 = prev2->next)
5153 if (prev2->next == attr2)
5154 {
5155 prev2->next = attr2->next;
5156 break;
5157 }
5158 }
5159
5160 if (con->request->last == attr2)
5161 con->request->last = prev2;
5162
5163 _ipp_free_attr(attr2);
5164 }
5165
5166 /*
5167 * Add new option by copying it...
5168 */
5169
5170 copy_attribute(con->request, attr, 0);
5171 }
5172
5173 /*
5174 * Then free the attribute list and option array...
5175 */
5176
5177 ippDelete(ticket);
5178 cupsFreeOptions(num_options, options);
5179 }
5180
5181
5182 /*
5183 * 'reject_jobs()' - Reject print jobs to a printer.
5184 */
5185
5186 static void
5187 reject_jobs(client_t *con, /* I - Client connection */
5188 ipp_attribute_t *uri) /* I - Printer or class URI */
5189 {
5190 cups_ptype_t dtype; /* Destination type (printer or class) */
5191 char method[HTTP_MAX_URI],
5192 /* Method portion of URI */
5193 username[HTTP_MAX_URI],
5194 /* Username portion of URI */
5195 host[HTTP_MAX_URI],
5196 /* Host portion of URI */
5197 resource[HTTP_MAX_URI];
5198 /* Resource portion of URI */
5199 int port; /* Port portion of URI */
5200 const char *name; /* Printer name */
5201 printer_t *printer; /* Printer data */
5202 ipp_attribute_t *attr; /* printer-state-message text */
5203
5204
5205 LogMessage(L_DEBUG2, "reject_jobs(%d, %s)\n", con->http.fd,
5206 uri->values[0].string.text);
5207
5208 /*
5209 * Was this operation called from the correct URI?
5210 */
5211
5212 if (strncmp(con->uri, "/admin/", 7) != 0)
5213 {
5214 LogMessage(L_ERROR, "reject_jobs: admin request on bad resource \'%s\'!",
5215 con->uri);
5216 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5217 return;
5218 }
5219
5220 /*
5221 * Is the destination valid?
5222 */
5223
5224 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5225
5226 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
5227 {
5228 /*
5229 * Bad URI...
5230 */
5231
5232 LogMessage(L_ERROR, "reject_jobs: resource name \'%s\' no good!", resource);
5233 send_ipp_error(con, IPP_NOT_FOUND);
5234 return;
5235 }
5236
5237 /*
5238 * Reject jobs sent to the printer...
5239 */
5240
5241 if (dtype & CUPS_PRINTER_CLASS)
5242 printer = FindClass(name);
5243 else
5244 printer = FindPrinter(name);
5245
5246 printer->accepting = 0;
5247
5248 if ((attr = ippFindAttribute(con->request, "printer-state-message",
5249 IPP_TAG_TEXT)) == NULL)
5250 strcpy(printer->state_message, "Rejecting Jobs");
5251 else
5252 strlcpy(printer->state_message, attr->values[0].string.text,
5253 sizeof(printer->state_message));
5254
5255 AddPrinterHistory(printer);
5256
5257 if (dtype & CUPS_PRINTER_CLASS)
5258 {
5259 SaveAllClasses();
5260
5261 LogMessage(L_INFO, "Class \'%s\' rejecting jobs (\'%s\').", name,
5262 con->username);
5263 }
5264 else
5265 {
5266 SaveAllPrinters();
5267
5268 LogMessage(L_INFO, "Printer \'%s\' rejecting jobs (\'%s\').", name,
5269 con->username);
5270 }
5271
5272 /*
5273 * Everything was ok, so return OK status...
5274 */
5275
5276 con->response->request.status.status_code = IPP_OK;
5277 }
5278
5279
5280 /*
5281 * 'release_job()' - Release a held print job.
5282 */
5283
5284 static void
5285 release_job(client_t *con, /* I - Client connection */
5286 ipp_attribute_t *uri) /* I - Job or Printer URI */
5287 {
5288 ipp_attribute_t *attr; /* Current attribute */
5289 int jobid; /* Job ID */
5290 char method[HTTP_MAX_URI],
5291 /* Method portion of URI */
5292 username[HTTP_MAX_URI],
5293 /* Username portion of URI */
5294 host[HTTP_MAX_URI],
5295 /* Host portion of URI */
5296 resource[HTTP_MAX_URI];
5297 /* Resource portion of URI */
5298 int port; /* Port portion of URI */
5299 job_t *job; /* Job information */
5300
5301
5302 LogMessage(L_DEBUG2, "release_job(%d, %s)\n", con->http.fd,
5303 uri->values[0].string.text);
5304
5305 /*
5306 * Verify that the POST operation was done to a valid URI.
5307 */
5308
5309 if (strncmp(con->uri, "/classes/", 9) != 0 &&
5310 strncmp(con->uri, "/jobs/", 5) != 0 &&
5311 strncmp(con->uri, "/printers/", 10) != 0)
5312 {
5313 LogMessage(L_ERROR, "release_job: release request on bad resource \'%s\'!",
5314 con->uri);
5315 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5316 return;
5317 }
5318
5319 /*
5320 * See if we have a job URI or a printer URI...
5321 */
5322
5323 if (strcmp(uri->name, "printer-uri") == 0)
5324 {
5325 /*
5326 * Got a printer URI; see if we also have a job-id attribute...
5327 */
5328
5329 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
5330 {
5331 LogMessage(L_ERROR, "release_job: got a printer-uri attribute but no job-id!");
5332 send_ipp_error(con, IPP_BAD_REQUEST);
5333 return;
5334 }
5335
5336 jobid = attr->values[0].integer;
5337 }
5338 else
5339 {
5340 /*
5341 * Got a job URI; parse it to get the job ID...
5342 */
5343
5344 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5345
5346 if (strncmp(resource, "/jobs/", 6) != 0)
5347 {
5348 /*
5349 * Not a valid URI!
5350 */
5351
5352 LogMessage(L_ERROR, "release_job: bad job-uri attribute \'%s\'!",
5353 uri->values[0].string.text);
5354 send_ipp_error(con, IPP_BAD_REQUEST);
5355 return;
5356 }
5357
5358 jobid = atoi(resource + 6);
5359 }
5360
5361 /*
5362 * See if the job exists...
5363 */
5364
5365 if ((job = FindJob(jobid)) == NULL)
5366 {
5367 /*
5368 * Nope - return a "not found" error...
5369 */
5370
5371 LogMessage(L_ERROR, "release_job: job #%d doesn't exist!", jobid);
5372 send_ipp_error(con, IPP_NOT_FOUND);
5373 return;
5374 }
5375
5376 /*
5377 * See if job is "held"...
5378 */
5379
5380 if (job->state->values[0].integer != IPP_JOB_HELD)
5381 {
5382 /*
5383 * Nope - return a "not possible" error...
5384 */
5385
5386 LogMessage(L_ERROR, "release_job: job #%d is not held!", jobid);
5387 send_ipp_error(con, IPP_NOT_POSSIBLE);
5388 return;
5389 }
5390
5391 /*
5392 * See if the job is owned by the requesting user...
5393 */
5394
5395 if (!validate_user(con, job->username, username, sizeof(username)))
5396 {
5397 LogMessage(L_ERROR, "release_job: \"%s\" not authorized to release job id %d owned by \"%s\"!",
5398 username, jobid, job->username);
5399 send_ipp_error(con, IPP_FORBIDDEN);
5400 return;
5401 }
5402
5403 /*
5404 * Reset the job-hold-until value to "no-hold"...
5405 */
5406
5407 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
5408 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
5409
5410 if (attr != NULL)
5411 {
5412 free(attr->values[0].string.text);
5413 attr->value_tag = IPP_TAG_KEYWORD;
5414 attr->values[0].string.text = strdup("no-hold");
5415 }
5416
5417 /*
5418 * Release the job and return...
5419 */
5420
5421 ReleaseJob(jobid);
5422
5423 LogMessage(L_INFO, "Job %d was released by \'%s\'.", jobid, username);
5424
5425 con->response->request.status.status_code = IPP_OK;
5426 }
5427
5428
5429 /*
5430 * 'restart_job()' - Restart an old print job.
5431 */
5432
5433 static void
5434 restart_job(client_t *con, /* I - Client connection */
5435 ipp_attribute_t *uri) /* I - Job or Printer URI */
5436 {
5437 ipp_attribute_t *attr; /* Current attribute */
5438 int jobid; /* Job ID */
5439 char method[HTTP_MAX_URI],
5440 /* Method portion of URI */
5441 username[HTTP_MAX_URI],
5442 /* Username portion of URI */
5443 host[HTTP_MAX_URI],
5444 /* Host portion of URI */
5445 resource[HTTP_MAX_URI];
5446 /* Resource portion of URI */
5447 int port; /* Port portion of URI */
5448 job_t *job; /* Job information */
5449
5450
5451 LogMessage(L_DEBUG2, "restart_job(%d, %s)\n", con->http.fd,
5452 uri->values[0].string.text);
5453
5454 /*
5455 * Verify that the POST operation was done to a valid URI.
5456 */
5457
5458 if (strncmp(con->uri, "/classes/", 9) != 0 &&
5459 strncmp(con->uri, "/jobs/", 5) != 0 &&
5460 strncmp(con->uri, "/printers/", 10) != 0)
5461 {
5462 LogMessage(L_ERROR, "restart_job: restart request on bad resource \'%s\'!",
5463 con->uri);
5464 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5465 return;
5466 }
5467
5468 /*
5469 * See if we have a job URI or a printer URI...
5470 */
5471
5472 if (strcmp(uri->name, "printer-uri") == 0)
5473 {
5474 /*
5475 * Got a printer URI; see if we also have a job-id attribute...
5476 */
5477
5478 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
5479 {
5480 LogMessage(L_ERROR, "restart_job: got a printer-uri attribute but no job-id!");
5481 send_ipp_error(con, IPP_BAD_REQUEST);
5482 return;
5483 }
5484
5485 jobid = attr->values[0].integer;
5486 }
5487 else
5488 {
5489 /*
5490 * Got a job URI; parse it to get the job ID...
5491 */
5492
5493 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5494
5495 if (strncmp(resource, "/jobs/", 6) != 0)
5496 {
5497 /*
5498 * Not a valid URI!
5499 */
5500
5501 LogMessage(L_ERROR, "restart_job: bad job-uri attribute \'%s\'!",
5502 uri->values[0].string.text);
5503 send_ipp_error(con, IPP_BAD_REQUEST);
5504 return;
5505 }
5506
5507 jobid = atoi(resource + 6);
5508 }
5509
5510 /*
5511 * See if the job exists...
5512 */
5513
5514 if ((job = FindJob(jobid)) == NULL)
5515 {
5516 /*
5517 * Nope - return a "not found" error...
5518 */
5519
5520 LogMessage(L_ERROR, "restart_job: job #%d doesn't exist!", jobid);
5521 send_ipp_error(con, IPP_NOT_FOUND);
5522 return;
5523 }
5524
5525 /*
5526 * See if job is in any of the "completed" states...
5527 */
5528
5529 if (job->state->values[0].integer <= IPP_JOB_PROCESSING)
5530 {
5531 /*
5532 * Nope - return a "not possible" error...
5533 */
5534
5535 LogMessage(L_ERROR, "restart_job: job #%d is not complete!", jobid);
5536 send_ipp_error(con, IPP_NOT_POSSIBLE);
5537 return;
5538 }
5539
5540 /*
5541 * See if we have retained the job files...
5542 */
5543
5544 if (!JobFiles && job->state->values[0].integer > IPP_JOB_STOPPED)
5545 {
5546 /*
5547 * Nope - return a "not possible" error...
5548 */
5549
5550 LogMessage(L_ERROR, "restart_job: job #%d cannot be restarted - no files!", jobid);
5551 send_ipp_error(con, IPP_NOT_POSSIBLE);
5552 return;
5553 }
5554
5555 /*
5556 * See if the job is owned by the requesting user...
5557 */
5558
5559 if (!validate_user(con, job->username, username, sizeof(username)))
5560 {
5561 LogMessage(L_ERROR, "restart_job: \"%s\" not authorized to restart job id %d owned by \"%s\"!",
5562 username, jobid, job->username);
5563 send_ipp_error(con, IPP_FORBIDDEN);
5564 return;
5565 }
5566
5567 /*
5568 * Restart the job and return...
5569 */
5570
5571 RestartJob(jobid);
5572
5573 LogMessage(L_INFO, "Job %d was restarted by \'%s\'.", jobid, username);
5574
5575 con->response->request.status.status_code = IPP_OK;
5576 }
5577
5578
5579 /*
5580 * 'send_document()' - Send a file to a printer or class.
5581 */
5582
5583 static void
5584 send_document(client_t *con, /* I - Client connection */
5585 ipp_attribute_t *uri) /* I - Printer URI */
5586 {
5587 ipp_attribute_t *attr; /* Current attribute */
5588 ipp_attribute_t *format; /* Document-format attribute */
5589 int jobid; /* Job ID number */
5590 job_t *job; /* Current job */
5591 char job_uri[HTTP_MAX_URI],
5592 /* Job URI */
5593 method[HTTP_MAX_URI],
5594 /* Method portion of URI */
5595 username[HTTP_MAX_URI],
5596 /* Username portion of URI */
5597 host[HTTP_MAX_URI],
5598 /* Host portion of URI */
5599 resource[HTTP_MAX_URI];
5600 /* Resource portion of URI */
5601 int port; /* Port portion of URI */
5602 mime_type_t *filetype; /* Type of file */
5603 char super[MIME_MAX_SUPER],
5604 /* Supertype of file */
5605 type[MIME_MAX_TYPE],
5606 /* Subtype of file */
5607 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
5608 /* Textual name of mime type */
5609 char filename[1024]; /* Job filename */
5610 printer_t *printer; /* Current printer */
5611 struct stat fileinfo; /* File information */
5612 int kbytes; /* Size of file */
5613 int compression; /* Type of compression */
5614
5615
5616 LogMessage(L_DEBUG2, "send_document(%d, %s)\n", con->http.fd,
5617 uri->values[0].string.text);
5618
5619 /*
5620 * Verify that the POST operation was done to a valid URI.
5621 */
5622
5623 if (strncmp(con->uri, "/classes/", 9) != 0 &&
5624 strncmp(con->uri, "/jobs/", 6) != 0 &&
5625 strncmp(con->uri, "/printers/", 10) != 0)
5626 {
5627 LogMessage(L_ERROR, "send_document: print request on bad resource \'%s\'!",
5628 con->uri);
5629 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5630 return;
5631 }
5632
5633 /*
5634 * See if we have a job URI or a printer URI...
5635 */
5636
5637 if (strcmp(uri->name, "printer-uri") == 0)
5638 {
5639 /*
5640 * Got a printer URI; see if we also have a job-id attribute...
5641 */
5642
5643 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
5644 {
5645 LogMessage(L_ERROR, "send_document: got a printer-uri attribute but no job-id!");
5646 send_ipp_error(con, IPP_BAD_REQUEST);
5647 return;
5648 }
5649
5650 jobid = attr->values[0].integer;
5651 }
5652 else
5653 {
5654 /*
5655 * Got a job URI; parse it to get the job ID...
5656 */
5657
5658 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5659
5660 if (strncmp(resource, "/jobs/", 6) != 0)
5661 {
5662 /*
5663 * Not a valid URI!
5664 */
5665
5666 LogMessage(L_ERROR, "send_document: bad job-uri attribute \'%s\'!",
5667 uri->values[0].string.text);
5668 send_ipp_error(con, IPP_BAD_REQUEST);
5669 return;
5670 }
5671
5672 jobid = atoi(resource + 6);
5673 }
5674
5675 /*
5676 * See if the job exists...
5677 */
5678
5679 if ((job = FindJob(jobid)) == NULL)
5680 {
5681 /*
5682 * Nope - return a "not found" error...
5683 */
5684
5685 LogMessage(L_ERROR, "send_document: job #%d doesn't exist!", jobid);
5686 send_ipp_error(con, IPP_NOT_FOUND);
5687 return;
5688 }
5689
5690 /*
5691 * See if the job is owned by the requesting user...
5692 */
5693
5694 if (!validate_user(con, job->username, username, sizeof(username)))
5695 {
5696 LogMessage(L_ERROR, "send_document: \"%s\" not authorized to send document for job id %d owned by \"%s\"!",
5697 username, jobid, job->username);
5698 send_ipp_error(con, IPP_FORBIDDEN);
5699 return;
5700 }
5701
5702 /*
5703 * OK, see if the client is sending the document compressed - CUPS
5704 * only supports "none" and "gzip".
5705 */
5706
5707 compression = CUPS_FILE_NONE;
5708
5709 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL)
5710 {
5711 if (strcmp(attr->values[0].string.text, "none")
5712 #ifdef HAVE_LIBZ
5713 && strcmp(attr->values[0].string.text, "gzip")
5714 #endif /* HAVE_LIBZ */
5715 )
5716 {
5717 LogMessage(L_ERROR, "print_job: Unsupported compression \"%s\"!",
5718 attr->values[0].string.text);
5719 send_ipp_error(con, IPP_ATTRIBUTES);
5720 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
5721 "compression", NULL, attr->values[0].string.text);
5722 return;
5723 }
5724
5725 #ifdef HAVE_LIBZ
5726 if (!strcmp(attr->values[0].string.text, "gzip"))
5727 compression = CUPS_FILE_GZIP;
5728 #endif /* HAVE_LIBZ */
5729 }
5730
5731 /*
5732 * Do we have a file to print?
5733 */
5734
5735 if (!con->filename)
5736 {
5737 LogMessage(L_ERROR, "send_document: No file!?!");
5738 send_ipp_error(con, IPP_BAD_REQUEST);
5739 return;
5740 }
5741
5742 /*
5743 * Is it a format we support?
5744 */
5745
5746 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
5747 {
5748 /*
5749 * Grab format from client...
5750 */
5751
5752 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
5753 {
5754 LogMessage(L_ERROR, "send_document: could not scan type \'%s\'!",
5755 format->values[0].string.text);
5756 send_ipp_error(con, IPP_BAD_REQUEST);
5757 return;
5758 }
5759 }
5760 else
5761 {
5762 /*
5763 * No document format attribute? Auto-type it!
5764 */
5765
5766 strcpy(super, "application");
5767 strcpy(type, "octet-stream");
5768 }
5769
5770 if (strcmp(super, "application") == 0 &&
5771 strcmp(type, "octet-stream") == 0)
5772 {
5773 /*
5774 * Auto-type the file...
5775 */
5776
5777 LogMessage(L_DEBUG, "send_document: auto-typing file...");
5778
5779 filetype = mimeFileType(MimeDatabase, con->filename, &compression);
5780
5781 if (filetype != NULL)
5782 {
5783 /*
5784 * Replace the document-format attribute value with the auto-typed one.
5785 */
5786
5787 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
5788 filetype->type);
5789
5790 if (format != NULL)
5791 {
5792 free(format->values[0].string.text);
5793 format->values[0].string.text = strdup(mimetype);
5794 }
5795 else
5796 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
5797 "document-format", NULL, mimetype);
5798 }
5799 else
5800 filetype = mimeType(MimeDatabase, super, type);
5801 }
5802 else
5803 filetype = mimeType(MimeDatabase, super, type);
5804
5805 if (filetype == NULL)
5806 {
5807 LogMessage(L_ERROR, "send_document: Unsupported format \'%s/%s\'!",
5808 super, type);
5809 LogMessage(L_INFO, "Hint: Do you have the raw file printing rules enabled?");
5810 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
5811
5812 if (format)
5813 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
5814 "document-format", NULL, format->values[0].string.text);
5815
5816 return;
5817 }
5818
5819 LogMessage(L_DEBUG, "send_document: request file type is %s/%s.",
5820 filetype->super, filetype->type);
5821
5822 /*
5823 * Add the file to the job...
5824 */
5825
5826 if (add_file(con, job, filetype, compression))
5827 return;
5828
5829 if (job->dtype & CUPS_PRINTER_CLASS)
5830 printer = FindClass(job->dest);
5831 else
5832 printer = FindPrinter(job->dest);
5833
5834 if (stat(con->filename, &fileinfo))
5835 kbytes = 0;
5836 else
5837 kbytes = (fileinfo.st_size + 1023) / 1024;
5838
5839 UpdateQuota(printer, job->username, 0, kbytes);
5840
5841 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
5842 attr->values[0].integer += kbytes;
5843
5844 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
5845 job->num_files);
5846 rename(con->filename, filename);
5847
5848 ClearString(&con->filename);
5849
5850 LogMessage(L_INFO, "File of type %s/%s queued in job #%d by \'%s\'.",
5851 filetype->super, filetype->type, job->id, job->username);
5852
5853 /*
5854 * Start the job if this is the last document...
5855 */
5856
5857 if ((attr = ippFindAttribute(con->request, "last-document", IPP_TAG_BOOLEAN)) != NULL &&
5858 attr->values[0].boolean)
5859 {
5860 /*
5861 * See if we need to add the ending sheet...
5862 */
5863
5864 if (printer != NULL &&
5865 !(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
5866 (attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL &&
5867 attr->num_values > 1)
5868 {
5869 /*
5870 * Yes...
5871 */
5872
5873 LogMessage(L_INFO, "Adding end banner page \"%s\" to job %d.",
5874 attr->values[1].string.text, job->id);
5875
5876 kbytes = copy_banner(con, job, attr->values[1].string.text);
5877
5878 UpdateQuota(printer, job->username, 0, kbytes);
5879 }
5880
5881 if (job->state->values[0].integer == IPP_JOB_STOPPED)
5882 job->state->values[0].integer = IPP_JOB_PENDING;
5883 else if (job->state->values[0].integer == IPP_JOB_HELD)
5884 {
5885 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
5886 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
5887
5888 if (attr == NULL || strcmp(attr->values[0].string.text, "no-hold") == 0)
5889 job->state->values[0].integer = IPP_JOB_PENDING;
5890 }
5891
5892 SaveJob(job->id);
5893 CheckJobs();
5894 }
5895 else
5896 {
5897 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
5898 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
5899
5900 if (attr == NULL || strcmp(attr->values[0].string.text, "no-hold") == 0)
5901 {
5902 job->state->values[0].integer = IPP_JOB_HELD;
5903 job->hold_until = time(NULL) + 60;
5904 SaveJob(job->id);
5905 }
5906 }
5907
5908 /*
5909 * Fill in the response info...
5910 */
5911
5912 #ifdef AF_INET6
5913 if (con->http.hostaddr.addr.sa_family == AF_INET6)
5914 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
5915 ntohs(con->http.hostaddr.ipv6.sin6_port), job->id);
5916 else
5917 #endif /* AF_INET6 */
5918 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
5919 ntohs(con->http.hostaddr.ipv4.sin_port), job->id);
5920
5921 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
5922 job_uri);
5923
5924 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
5925
5926 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
5927 job->state->values[0].integer);
5928 add_job_state_reasons(con, job);
5929
5930 con->response->request.status.status_code = IPP_OK;
5931 }
5932
5933
5934 /*
5935 * 'send_ipp_error()' - Send an error status back to the IPP client.
5936 */
5937
5938 static void
5939 send_ipp_error(client_t *con, /* I - Client connection */
5940 ipp_status_t status) /* I - IPP status code */
5941 {
5942 LogMessage(L_DEBUG2, "send_ipp_error(%d, %x)\n", con->http.fd, status);
5943
5944 LogMessage(L_DEBUG, "Sending error: %s", ippErrorString(status));
5945
5946 con->response->request.status.status_code = status;
5947
5948 if (ippFindAttribute(con->response, "attributes-charset", IPP_TAG_ZERO) == NULL)
5949 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
5950 "attributes-charset", NULL, DefaultCharset);
5951
5952 if (ippFindAttribute(con->response, "attributes-natural-language",
5953 IPP_TAG_ZERO) == NULL)
5954 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
5955 "attributes-natural-language", NULL, DefaultLanguage);
5956 }
5957
5958
5959 /*
5960 * 'set_default()' - Set the default destination...
5961 */
5962
5963 static void
5964 set_default(client_t *con, /* I - Client connection */
5965 ipp_attribute_t *uri) /* I - Printer URI */
5966 {
5967 cups_ptype_t dtype; /* Destination type (printer or class) */
5968 char method[HTTP_MAX_URI],
5969 /* Method portion of URI */
5970 username[HTTP_MAX_URI],
5971 /* Username portion of URI */
5972 host[HTTP_MAX_URI],
5973 /* Host portion of URI */
5974 resource[HTTP_MAX_URI];
5975 /* Resource portion of URI */
5976 int port; /* Port portion of URI */
5977 const char *name; /* Printer name */
5978
5979
5980 LogMessage(L_DEBUG2, "set_default(%d, %s)\n", con->http.fd,
5981 uri->values[0].string.text);
5982
5983 /*
5984 * Was this operation called from the correct URI?
5985 */
5986
5987 if (strncmp(con->uri, "/admin/", 7) != 0)
5988 {
5989 LogMessage(L_ERROR, "set_default: admin request on bad resource \'%s\'!",
5990 con->uri);
5991 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5992 return;
5993 }
5994
5995 /*
5996 * Is the destination valid?
5997 */
5998
5999 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6000
6001 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
6002 {
6003 /*
6004 * Bad URI...
6005 */
6006
6007 LogMessage(L_ERROR, "set_default: resource name \'%s\' no good!", resource);
6008 send_ipp_error(con, IPP_NOT_FOUND);
6009 return;
6010 }
6011
6012 /*
6013 * Set it as the default...
6014 */
6015
6016 if (dtype & CUPS_PRINTER_CLASS)
6017 DefaultPrinter = FindClass(name);
6018 else
6019 DefaultPrinter = FindPrinter(name);
6020
6021 SaveAllPrinters();
6022 SaveAllClasses();
6023
6024 LogMessage(L_INFO, "Default destination set to \'%s\' by \'%s\'.", name,
6025 con->username);
6026
6027 /*
6028 * Everything was ok, so return OK status...
6029 */
6030
6031 con->response->request.status.status_code = IPP_OK;
6032 }
6033
6034
6035 /*
6036 * 'set_job_attrs()' - Set job attributes.
6037 */
6038
6039 static void
6040 set_job_attrs(client_t *con, /* I - Client connection */
6041 ipp_attribute_t *uri) /* I - Job URI */
6042 {
6043 ipp_attribute_t *attr, /* Current attribute */
6044 *attr2, /* Job attribute */
6045 *prev2; /* Previous job attribute */
6046 int jobid; /* Job ID */
6047 job_t *job; /* Current job */
6048 char method[HTTP_MAX_URI],
6049 /* Method portion of URI */
6050 username[HTTP_MAX_URI],
6051 /* Username portion of URI */
6052 host[HTTP_MAX_URI],
6053 /* Host portion of URI */
6054 resource[HTTP_MAX_URI];
6055 /* Resource portion of URI */
6056 int port; /* Port portion of URI */
6057
6058
6059 LogMessage(L_DEBUG2, "set_job_attrs(%d, %s)\n", con->http.fd,
6060 uri->values[0].string.text);
6061
6062 /*
6063 * Start with "everything is OK" status...
6064 */
6065
6066 con->response->request.status.status_code = IPP_OK;
6067
6068 /*
6069 * See if we have a job URI or a printer URI...
6070 */
6071
6072 if (strcmp(uri->name, "printer-uri") == 0)
6073 {
6074 /*
6075 * Got a printer URI; see if we also have a job-id attribute...
6076 */
6077
6078 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
6079 {
6080 LogMessage(L_ERROR, "set_job_attrs: got a printer-uri attribute but no job-id!");
6081 send_ipp_error(con, IPP_BAD_REQUEST);
6082 return;
6083 }
6084
6085 jobid = attr->values[0].integer;
6086 }
6087 else
6088 {
6089 /*
6090 * Got a job URI; parse it to get the job ID...
6091 */
6092
6093 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6094
6095 if (strncmp(resource, "/jobs/", 6) != 0)
6096 {
6097 /*
6098 * Not a valid URI!
6099 */
6100
6101 LogMessage(L_ERROR, "set_job_attrs: bad job-uri attribute \'%s\'!\n",
6102 uri->values[0].string.text);
6103 send_ipp_error(con, IPP_BAD_REQUEST);
6104 return;
6105 }
6106
6107 jobid = atoi(resource + 6);
6108 }
6109
6110 /*
6111 * See if the job exists...
6112 */
6113
6114 if ((job = FindJob(jobid)) == NULL)
6115 {
6116 /*
6117 * Nope - return a "not found" error...
6118 */
6119
6120 LogMessage(L_ERROR, "set_job_attrs: job #%d doesn't exist!", jobid);
6121 send_ipp_error(con, IPP_NOT_FOUND);
6122 return;
6123 }
6124
6125 /*
6126 * See if the job has been completed...
6127 */
6128
6129 if (job->state->values[0].integer > IPP_JOB_STOPPED)
6130 {
6131 /*
6132 * Return a "not-possible" error...
6133 */
6134
6135 LogMessage(L_ERROR, "set_job_attrs: job #%d is finished and cannot be altered!", jobid);
6136 send_ipp_error(con, IPP_NOT_POSSIBLE);
6137 return;
6138 }
6139
6140 /*
6141 * See if the job is owned by the requesting user...
6142 */
6143
6144 if (!validate_user(con, job->username, username, sizeof(username)))
6145 {
6146 LogMessage(L_ERROR, "set_job_attrs: \"%s\" not authorized to alter job id %d owned by \"%s\"!",
6147 username, jobid, job->username);
6148 send_ipp_error(con, IPP_FORBIDDEN);
6149 return;
6150 }
6151
6152 /*
6153 * See what the user wants to change.
6154 */
6155
6156 for (attr = con->request->attrs; attr != NULL; attr = attr->next)
6157 {
6158 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
6159 continue;
6160
6161 if (!strcmp(attr->name, "attributes-charset") ||
6162 !strcmp(attr->name, "attributes-natural-language") ||
6163 !strcmp(attr->name, "document-compression") ||
6164 !strcmp(attr->name, "document-format") ||
6165 !strcmp(attr->name, "job-detailed-status-messages") ||
6166 !strcmp(attr->name, "job-document-access-errors") ||
6167 !strcmp(attr->name, "job-id") ||
6168 !strcmp(attr->name, "job-k-octets") ||
6169 !strcmp(attr->name, "job-originating-host-name") ||
6170 !strcmp(attr->name, "job-originating-user-name") ||
6171 !strcmp(attr->name, "job-printer-up-time") ||
6172 !strcmp(attr->name, "job-printer-uri") ||
6173 !strcmp(attr->name, "job-sheets") ||
6174 !strcmp(attr->name, "job-state-message") ||
6175 !strcmp(attr->name, "job-state-reasons") ||
6176 !strcmp(attr->name, "job-uri") ||
6177 !strcmp(attr->name, "number-of-documents") ||
6178 !strcmp(attr->name, "number-of-intervening-jobs") ||
6179 !strcmp(attr->name, "output-device-assigned") ||
6180 !strncmp(attr->name, "date-time-at-", 13) ||
6181 !strncmp(attr->name, "job-impressions", 15) ||
6182 !strncmp(attr->name, "job-k-octets", 12) ||
6183 !strncmp(attr->name, "job-media-sheets", 16) ||
6184 !strncmp(attr->name, "time-at-", 8))
6185 {
6186 /*
6187 * Read-only attrs!
6188 */
6189
6190 send_ipp_error(con, IPP_ATTRIBUTES_NOT_SETTABLE);
6191
6192 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
6193 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
6194
6195 continue;
6196 }
6197
6198 if (!strcmp(attr->name, "job-priority"))
6199 {
6200 /*
6201 * Change the job priority...
6202 */
6203
6204 if (attr->value_tag != IPP_TAG_INTEGER)
6205 {
6206 send_ipp_error(con, IPP_REQUEST_VALUE);
6207
6208 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
6209 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
6210 }
6211 else if (job->state->values[0].integer >= IPP_JOB_PROCESSING)
6212 {
6213 send_ipp_error(con, IPP_NOT_POSSIBLE);
6214 return;
6215 }
6216 else if (con->response->request.status.status_code == IPP_OK)
6217 SetJobPriority(jobid, attr->values[0].integer);
6218 }
6219 else if (!strcmp(attr->name, "job-state"))
6220 {
6221 /*
6222 * Change the job state...
6223 */
6224
6225 if (attr->value_tag != IPP_TAG_ENUM)
6226 {
6227 send_ipp_error(con, IPP_REQUEST_VALUE);
6228
6229 if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
6230 attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
6231 }
6232 else
6233 {
6234 switch (attr->values[0].integer)
6235 {
6236 case IPP_JOB_PENDING :
6237 case IPP_JOB_HELD :
6238 if (job->state->values[0].integer > IPP_JOB_HELD)
6239 {
6240 send_ipp_error(con, IPP_NOT_POSSIBLE);
6241 return;
6242 }
6243 else if (con->response->request.status.status_code == IPP_OK)
6244 job->state->values[0].integer = attr->values[0].integer;
6245 break;
6246
6247 case IPP_JOB_PROCESSING :
6248 case IPP_JOB_STOPPED :
6249 if (job->state->values[0].integer != attr->values[0].integer)
6250 {
6251 send_ipp_error(con, IPP_NOT_POSSIBLE);
6252 return;
6253 }
6254 break;
6255
6256 case IPP_JOB_CANCELLED :
6257 case IPP_JOB_ABORTED :
6258 case IPP_JOB_COMPLETED :
6259 if (job->state->values[0].integer > IPP_JOB_PROCESSING)
6260 {
6261 send_ipp_error(con, IPP_NOT_POSSIBLE);
6262 return;
6263 }
6264 else if (con->response->request.status.status_code == IPP_OK)
6265 {
6266 CancelJob(job->id, 0);
6267
6268 if (JobHistory)
6269 {
6270 job->state->values[0].integer = attr->values[0].integer;
6271 SaveJob(job->id);
6272 }
6273 }
6274 break;
6275 }
6276 }
6277 }
6278 else if (con->response->request.status.status_code != IPP_OK)
6279 continue;
6280 else if ((attr2 = ippFindAttribute(job->attrs, attr->name, IPP_TAG_ZERO)) != NULL)
6281 {
6282 /*
6283 * Some other value; first free the old value...
6284 */
6285
6286 if (job->attrs->attrs == attr2)
6287 {
6288 job->attrs->attrs = attr2->next;
6289 prev2 = NULL;
6290 }
6291 else
6292 {
6293 for (prev2 = job->attrs->attrs; prev2 != NULL; prev2 = prev2->next)
6294 if (prev2->next == attr2)
6295 {
6296 prev2->next = attr2->next;
6297 break;
6298 }
6299 }
6300
6301 if (job->attrs->last == attr2)
6302 job->attrs->last = prev2;
6303
6304 _ipp_free_attr(attr2);
6305
6306 /*
6307 * Then copy the attribute...
6308 */
6309
6310 copy_attribute(job->attrs, attr, 0);
6311
6312 /*
6313 * See if the job-name or job-hold-until is being changed.
6314 */
6315
6316 if (strcmp(attr->name, "job-hold-until") == 0)
6317 {
6318 SetJobHoldUntil(job->id, attr->values[0].string.text);
6319
6320 if (strcmp(attr->values[0].string.text, "no-hold") == 0)
6321 ReleaseJob(job->id);
6322 else
6323 HoldJob(job->id);
6324 }
6325 }
6326 else if (attr->value_tag == IPP_TAG_DELETEATTR)
6327 {
6328 /*
6329 * Delete the attribute...
6330 */
6331
6332 for (attr2 = job->attrs->attrs, prev2 = NULL;
6333 attr2 != NULL;
6334 prev2 = attr2, attr2 = attr2->next)
6335 if (attr2->name && strcmp(attr2->name, attr->name) == 0)
6336 break;
6337
6338 if (attr2)
6339 {
6340 if (prev2)
6341 prev2->next = attr2->next;
6342 else
6343 job->attrs->attrs = attr2->next;
6344
6345 if (attr2 == job->attrs->last)
6346 job->attrs->last = prev2;
6347
6348 _ipp_free_attr(attr2);
6349 }
6350 }
6351 else
6352 {
6353 /*
6354 * Add new option by copying it...
6355 */
6356
6357 copy_attribute(job->attrs, attr, 0);
6358 }
6359 }
6360
6361 /*
6362 * Save the job...
6363 */
6364
6365 SaveJob(job->id);
6366
6367 /*
6368 * Start jobs if possible...
6369 */
6370
6371 CheckJobs();
6372 }
6373
6374
6375 /*
6376 * 'start_printer()' - Start a printer.
6377 */
6378
6379 static void
6380 start_printer(client_t *con, /* I - Client connection */
6381 ipp_attribute_t *uri) /* I - Printer URI */
6382 {
6383 cups_ptype_t dtype; /* Destination type (printer or class) */
6384 char method[HTTP_MAX_URI],
6385 /* Method portion of URI */
6386 username[HTTP_MAX_URI],
6387 /* Username portion of URI */
6388 host[HTTP_MAX_URI],
6389 /* Host portion of URI */
6390 resource[HTTP_MAX_URI];
6391 /* Resource portion of URI */
6392 int port; /* Port portion of URI */
6393 const char *name; /* Printer name */
6394 printer_t *printer; /* Printer data */
6395
6396
6397 LogMessage(L_DEBUG2, "start_printer(%d, %s)\n", con->http.fd,
6398 uri->values[0].string.text);
6399
6400 /*
6401 * Was this operation called from the correct URI?
6402 */
6403
6404 if (strncmp(con->uri, "/admin/", 7) != 0)
6405 {
6406 LogMessage(L_ERROR, "start_printer: admin request on bad resource \'%s\'!",
6407 con->uri);
6408 send_ipp_error(con, IPP_NOT_AUTHORIZED);
6409 return;
6410 }
6411
6412 /*
6413 * Is the destination valid?
6414 */
6415
6416 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6417
6418 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
6419 {
6420 /*
6421 * Bad URI...
6422 */
6423
6424 LogMessage(L_ERROR, "start_printer: resource name \'%s\' no good!", resource);
6425 send_ipp_error(con, IPP_NOT_FOUND);
6426 return;
6427 }
6428
6429 /*
6430 * Start the printer...
6431 */
6432
6433 if (dtype & CUPS_PRINTER_CLASS)
6434 printer = FindClass(name);
6435 else
6436 printer = FindPrinter(name);
6437
6438 printer->state_message[0] = '\0';
6439
6440 StartPrinter(printer, 1);
6441
6442 if (dtype & CUPS_PRINTER_CLASS)
6443 LogMessage(L_INFO, "Class \'%s\' started by \'%s\'.", name,
6444 con->username);
6445 LogMessage(L_INFO, "Printer \'%s\' started by \'%s\'.", name,
6446 con->username);
6447
6448 CheckJobs();
6449
6450 /*
6451 * Everything was ok, so return OK status...
6452 */
6453
6454 con->response->request.status.status_code = IPP_OK;
6455 }
6456
6457
6458 /*
6459 * 'stop_printer()' - Stop a printer.
6460 */
6461
6462 static void
6463 stop_printer(client_t *con, /* I - Client connection */
6464 ipp_attribute_t *uri) /* I - Printer URI */
6465 {
6466 cups_ptype_t dtype; /* Destination type (printer or class) */
6467 char method[HTTP_MAX_URI],
6468 /* Method portion of URI */
6469 username[HTTP_MAX_URI],
6470 /* Username portion of URI */
6471 host[HTTP_MAX_URI],
6472 /* Host portion of URI */
6473 resource[HTTP_MAX_URI];
6474 /* Resource portion of URI */
6475 int port; /* Port portion of URI */
6476 const char *name; /* Printer name */
6477 printer_t *printer; /* Printer data */
6478 ipp_attribute_t *attr; /* printer-state-message attribute */
6479
6480
6481 LogMessage(L_DEBUG2, "stop_printer(%d, %s)\n", con->http.fd,
6482 uri->values[0].string.text);
6483
6484 /*
6485 * Was this operation called from the correct URI?
6486 */
6487
6488 if (strncmp(con->uri, "/admin/", 7) != 0)
6489 {
6490 LogMessage(L_ERROR, "stop_printer: admin request on bad resource \'%s\'!",
6491 con->uri);
6492 send_ipp_error(con, IPP_NOT_AUTHORIZED);
6493 return;
6494 }
6495
6496 /*
6497 * Is the destination valid?
6498 */
6499
6500 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6501
6502 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
6503 {
6504 /*
6505 * Bad URI...
6506 */
6507
6508 LogMessage(L_ERROR, "stop_printer: resource name \'%s\' no good!", resource);
6509 send_ipp_error(con, IPP_NOT_FOUND);
6510 return;
6511 }
6512
6513 /*
6514 * Stop the printer...
6515 */
6516
6517 if (dtype & CUPS_PRINTER_CLASS)
6518 printer = FindClass(name);
6519 else
6520 printer = FindPrinter(name);
6521
6522 if ((attr = ippFindAttribute(con->request, "printer-state-message",
6523 IPP_TAG_TEXT)) == NULL)
6524 strcpy(printer->state_message, "Paused");
6525 else
6526 {
6527 strlcpy(printer->state_message, attr->values[0].string.text,
6528 sizeof(printer->state_message));
6529 }
6530
6531 StopPrinter(printer, 1);
6532
6533 if (dtype & CUPS_PRINTER_CLASS)
6534 LogMessage(L_INFO, "Class \'%s\' stopped by \'%s\'.", name,
6535 con->username);
6536 else
6537 LogMessage(L_INFO, "Printer \'%s\' stopped by \'%s\'.", name,
6538 con->username);
6539
6540 /*
6541 * Everything was ok, so return OK status...
6542 */
6543
6544 con->response->request.status.status_code = IPP_OK;
6545 }
6546
6547
6548 /*
6549 * 'validate_job()' - Validate printer options and destination.
6550 */
6551
6552 static void
6553 validate_job(client_t *con, /* I - Client connection */
6554 ipp_attribute_t *uri) /* I - Printer URI */
6555 {
6556 ipp_attribute_t *attr; /* Current attribute */
6557 ipp_attribute_t *format; /* Document-format attribute */
6558 cups_ptype_t dtype; /* Destination type (printer or class) */
6559 char method[HTTP_MAX_URI],
6560 /* Method portion of URI */
6561 username[HTTP_MAX_URI],
6562 /* Username portion of URI */
6563 host[HTTP_MAX_URI],
6564 /* Host portion of URI */
6565 resource[HTTP_MAX_URI];
6566 /* Resource portion of URI */
6567 int port; /* Port portion of URI */
6568 char super[MIME_MAX_SUPER],
6569 /* Supertype of file */
6570 type[MIME_MAX_TYPE];
6571 /* Subtype of file */
6572
6573
6574 LogMessage(L_DEBUG2, "validate_job(%d, %s)\n", con->http.fd,
6575 uri->values[0].string.text);
6576
6577 /*
6578 * Verify that the POST operation was done to a valid URI.
6579 */
6580
6581 if (strncmp(con->uri, "/classes/", 9) != 0 &&
6582 strncmp(con->uri, "/printers/", 10) != 0)
6583 {
6584 LogMessage(L_ERROR, "validate_job: request on bad resource \'%s\'!",
6585 con->uri);
6586 send_ipp_error(con, IPP_NOT_AUTHORIZED);
6587 return;
6588 }
6589
6590 /*
6591 * OK, see if the client is sending the document compressed - CUPS
6592 * doesn't support compression yet...
6593 */
6594
6595 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL &&
6596 strcmp(attr->values[0].string.text, "none") == 0)
6597 {
6598 LogMessage(L_ERROR, "validate_job: Unsupported compression attribute %s!",
6599 attr->values[0].string.text);
6600 send_ipp_error(con, IPP_ATTRIBUTES);
6601 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
6602 "compression", NULL, attr->values[0].string.text);
6603 return;
6604 }
6605
6606 /*
6607 * Is it a format we support?
6608 */
6609
6610 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
6611 {
6612 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
6613 {
6614 LogMessage(L_ERROR, "validate_job: could not scan type \'%s\'!\n",
6615 format->values[0].string.text);
6616 send_ipp_error(con, IPP_BAD_REQUEST);
6617 return;
6618 }
6619
6620 if ((strcmp(super, "application") != 0 ||
6621 strcmp(type, "octet-stream") != 0) &&
6622 mimeType(MimeDatabase, super, type) == NULL)
6623 {
6624 LogMessage(L_ERROR, "validate_job: Unsupported format \'%s\'!\n",
6625 format->values[0].string.text);
6626 LogMessage(L_INFO, "Hint: Do you have the raw file printing rules enabled?");
6627 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
6628 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
6629 "document-format", NULL, format->values[0].string.text);
6630 return;
6631 }
6632 }
6633
6634 /*
6635 * Is the destination valid?
6636 */
6637
6638 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
6639
6640 if (ValidateDest(host, resource, &dtype) == NULL)
6641 {
6642 /*
6643 * Bad URI...
6644 */
6645
6646 LogMessage(L_ERROR, "validate_job: resource name \'%s\' no good!", resource);
6647 send_ipp_error(con, IPP_NOT_FOUND);
6648 return;
6649 }
6650
6651 /*
6652 * Everything was ok, so return OK status...
6653 */
6654
6655 con->response->request.status.status_code = IPP_OK;
6656 }
6657
6658
6659 /*
6660 * 'validate_user()' - Validate the user for the request.
6661 */
6662
6663 static int /* O - 1 if permitted, 0 otherwise */
6664 validate_user(client_t *con, /* I - Client connection */
6665 const char *owner, /* I - Owner of job/resource */
6666 char *username, /* O - Authenticated username */
6667 int userlen) /* I - Length of username */
6668 {
6669 int i, j; /* Looping vars */
6670 ipp_attribute_t *attr; /* requesting-user-name attribute */
6671 struct passwd *user; /* User info */
6672 struct group *group; /* System group info */
6673 char junk[33]; /* MD5 password (not used) */
6674
6675
6676 LogMessage(L_DEBUG2, "validate_user(%d, %s, %s, %d)\n", con->http.fd,
6677 owner, username, userlen);
6678
6679 /*
6680 * Validate input...
6681 */
6682
6683 if (con == NULL || owner == NULL || username == NULL || userlen <= 0)
6684 return (0);
6685
6686 /*
6687 * Get the best authenticated username that is available.
6688 */
6689
6690 if (con->username[0])
6691 strlcpy(username, con->username, userlen);
6692 else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
6693 strlcpy(username, attr->values[0].string.text, userlen);
6694 else
6695 strlcpy(username, "anonymous", userlen);
6696
6697 /*
6698 * Check the username against the owner...
6699 */
6700
6701 if (strcasecmp(username, owner) != 0 && strcasecmp(username, "root") != 0)
6702 {
6703 /*
6704 * Not the owner or root; check to see if the user is a member of the
6705 * system group...
6706 */
6707
6708 user = getpwnam(username);
6709 endpwent();
6710
6711 for (i = 0, j = 0, group = NULL; i < NumSystemGroups; i ++)
6712 {
6713 group = getgrnam(SystemGroups[i]);
6714 endgrent();
6715
6716 if (group != NULL)
6717 {
6718 for (j = 0; group->gr_mem[j]; j ++)
6719 if (strcasecmp(username, group->gr_mem[j]) == 0)
6720 break;
6721
6722 if (group->gr_mem[j])
6723 break;
6724 }
6725 else
6726 j = 0;
6727 }
6728
6729 if (user == NULL || group == NULL ||
6730 (group->gr_mem[j] == NULL && group->gr_gid != user->pw_gid))
6731 {
6732 /*
6733 * Username not found, group not found, or user is not part of the
6734 * system group... Check for a user and group in the MD5 password
6735 * file...
6736 */
6737
6738 for (i = 0; i < NumSystemGroups; i ++)
6739 if (GetMD5Passwd(username, SystemGroups[i], junk) != NULL)
6740 return (1);
6741
6742 /*
6743 * Nope, not an MD5 user, either. Return 0 indicating no-go...
6744 */
6745
6746 return (0);
6747 }
6748 }
6749
6750 return (1);
6751 }
6752
6753
6754 /*
6755 * End of "$Id: ipp.c,v 1.127.2.71 2003/07/20 12:51:47 mike Exp $".
6756 */