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