1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 #include <stdio.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <locale.h>
34
35 #include "lp.h"
36 #include "access.h"
37 #include "filters.h"
38 #include "msgs.h"
39
40 #define WHO_AM_I I_AM_LPFILTER
41 #include "oam.h"
42
43 #define OPT_LIST "f:F:ixl"
44
45 int add_filter(),
46 reload_filter(),
47 delete_filter(),
48 list_filter();
49
50 static void alert_spooler(),
51 same_complaints();
52
53 static char *opt();
54
55 /*
56 * Unfortunately, the LP requirements show the listing of a filter
57 * to be in a different order than the stored filter table. We can't
58 * change the stored version because it's the same as UNISON uses.
59 * So, we can't reuse the "FL_..." #defines found in "filters.h".
60 * But the following have similar use.
61 */
62 #define FL_MAX_P FL_MAX
63 # define FL_IGN_P 8
64 # define FL_PTYPS_P 2
65 # define FL_PRTRS_P 3
66 # define FL_ITYPS_P 0
67 # define FL_NAME_P 7
68 # define FL_OTYPS_P 1
69 # define FL_TYPE_P 4
70 # define FL_CMD_P 5
71 # define FL_TMPS_P 6
72
73 #define TABLE 0
74 #define TABLE_I 1
75
76 static struct headings {
77 char *v;
78 short len;
79 } headings[FL_MAX_P] = {
80
81 #define ENTRY(X) X, sizeof(X)-1
82 ENTRY("Input types:"),
83 ENTRY("Output types:"),
84 ENTRY("Printer types:"),
85 ENTRY("Printers:"),
86 ENTRY("Filter type:"),
87 ENTRY("Command:"),
88 ENTRY("Options:"),
89 ENTRY(""),
90 ENTRY("")
91 #undef ENTRY
92
93 };
94
95 /**
96 ** usage()
97 **/
98
usage()99 void usage ()
100 {
101 (void) printf (gettext(
102 "usage:\n"
103 "\n"
104 " (add or change filter)\n"
105 " lpfilter -f filter-name {-F path-name | -}\n"
106 "\n"
107 " (restore delivered filter)\n"
108 " lpfilter -f filter-name -i\n"
109 "\n"
110 " (list a filter)\n"
111 " lpfilter -f filter-name -l\n"
112 "\n"
113 " (list all filters)\n"
114 " lpfilter -f \"all\" -l\n"
115 "\n"
116 " (delete filter)\n"
117 " lpfilter -f filter-name -x\n"));
118
119 return;
120 }
121
122 /**
123 ** main()
124 **/
125
main(argc,argv)126 int main (argc, argv)
127 int argc;
128 char *argv[];
129 {
130 extern int optind,
131 opterr,
132 optopt,
133 getopt();
134
135 extern char *optarg;
136
137 int c,
138 (*action)(),
139 (*newaction)();
140
141 FILE *input;
142
143 char *filter,
144 *p;
145 char stroptsw[] = "-X";
146
147
148 (void) setlocale (LC_ALL, "");
149 #if !defined(TEXT_DOMAIN)
150 #define TEXT_DOMAIN "SYS_TEST"
151 #endif
152 (void) textdomain(TEXT_DOMAIN);
153
154 if (!is_user_admin()) {
155 LP_ERRMSG (ERROR, E_LP_NOTADM);
156 exit (1);
157 }
158
159 action = 0;
160 input = 0;
161 filter = 0;
162
163 opterr = 0;
164
165 while ((c = getopt(argc, argv, OPT_LIST)) != -1) switch (c) {
166
167 case 'f':
168 if (filter)
169 LP_ERRMSG1 (WARNING, E_LP_2MANY, 'f');
170 filter = optarg;
171 if (
172 STREQU(NAME_ANY, filter)
173 || STREQU(NAME_NONE, filter)
174 ) {
175 LP_ERRMSG (ERROR, E_LP_ANYNONE);
176 exit (1);
177 } else if (!syn_name(filter)) {
178 LP_ERRMSG1 (ERROR, E_LP_NOTNAME, filter);
179 exit (1);
180 } else if (!*filter)
181 filter = NAME_ALL;
182 break;
183
184 case 'F':
185 if (input)
186 LP_ERRMSG1 (WARNING, E_LP_2MANY, 'F');
187 if (!(input = fopen(optarg, "r"))) {
188 LP_ERRMSG1 (ERROR, E_FL_OPEN, optarg);
189 exit (1);
190 }
191 newaction = add_filter;
192 goto Check;
193
194 case 'i':
195 newaction = reload_filter;
196 goto Check;
197
198 case 'x':
199 newaction = delete_filter;
200 goto Check;
201
202 case 'l':
203 newaction = list_filter;
204 Check: if (action && newaction != action) {
205 LP_ERRMSG2 (
206 ERROR,
207 E_LP_AMBIG,
208 opt(action),
209 opt(newaction)
210 );
211 exit (1);
212 }
213 action = newaction;
214 break;
215
216 default:
217 if (optopt == '?') {
218 usage ();
219 exit (0);
220 }
221 stroptsw[1] = optopt;
222 if (strchr(OPT_LIST, optopt))
223 LP_ERRMSG1 (ERROR, E_LP_OPTARG, stroptsw);
224 else
225 LP_ERRMSG1 (ERROR, E_LP_OPTION, stroptsw);
226 exit (1);
227
228 }
229
230 if (optind < argc && STREQU(argv[optind], "-"))
231 if (action) {
232 LP_ERRMSG2 (ERROR, E_LP_AMBIG, opt(action), "-");
233 exit (1);
234 } else {
235 action = add_filter;
236 optind++;
237 }
238
239 if (!filter) {
240 LP_ERRMSG (ERROR, E_FL_NOFILT);
241 exit (1);
242 }
243
244 if (!action) {
245 LP_ERRMSG (ERROR, E_FL_NOACT);
246 exit (1);
247 }
248
249 if (optind < argc)
250 LP_ERRMSG1 (WARNING, E_FL_IGNORE, argv[optind]);
251
252 return ((*action)(filter, input));
253 }
254
255 /**
256 ** add_filter()
257 **/
258
add_filter(filter,input)259 int add_filter (filter, input)
260 char *filter;
261 FILE *input;
262 {
263 register FILTER *pf,
264 *store,
265 *ps;
266
267 register int fld;
268
269 register char *p;
270
271 char buf[3 * BUFSIZ],
272 *file;
273
274 int line,
275 bad_headings,
276 real_fields[FL_MAX],
277 at_least_one,
278 ret;
279
280 FILTER flbuf;
281
282
283 /*
284 * First we read in the input and parse it into a filter,
285 * storing it in the filter buffer "flbuf". Keep track of
286 * which fields have been given, to avoid overwriting unchanged
287 * fields later.
288 */
289
290 if (!input)
291 input = stdin;
292
293 for (fld = 0; fld < FL_MAX; fld++)
294 real_fields[fld] = 0;
295 flbuf.templates = 0;
296
297 line = bad_headings = 0;
298 while (fgets(buf, sizeof(buf), input) != NULL) {
299
300 buf[strlen(buf) - 1] = 0;
301
302 line++;
303
304 p = buf + strspn(buf, " \t");
305 if (!*p || *p == '#')
306 continue;
307
308 for (fld = 0; fld < FL_MAX; fld++)
309 if (
310 headings[fld].v
311 && headings[fld].len
312 && CS_STRNEQU(
313 p,
314 headings[fld].v,
315 headings[fld].len
316 )
317 ) {
318 real_fields[fld] = 1;
319 p += headings[fld].len + 1;
320 break;
321 }
322
323 if (fld >= FL_MAX) {
324
325 if (bad_headings++ >= 5) {
326 LP_ERRMSG (ERROR, E_FL_GARBAGE);
327 return (1);
328 }
329 LP_ERRMSG1 (WARNING, E_FL_HEADING, line);
330
331 } else switch (fld) {
332
333 case FL_IGN_P:
334 case FL_NAME_P:
335 break;
336 case FL_CMD_P:
337 flbuf.command = strdup(strip(p));
338 break;
339 case FL_TYPE_P:
340 flbuf.type = s_to_filtertype(strip(p));
341 break;
342 case FL_PTYPS_P:
343 flbuf.printer_types = getlist(p, LP_WS, LP_SEP);
344 break;
345 case FL_ITYPS_P:
346 flbuf.input_types = getlist(p, LP_WS, LP_SEP);
347 break;
348 case FL_OTYPS_P:
349 flbuf.output_types = getlist(p, LP_WS, LP_SEP);
350 break;
351 case FL_PRTRS_P:
352 flbuf.printers = getlist(p, LP_WS, LP_SEP);
353 break;
354 case FL_TMPS_P:
355 if (flbuf.templates) {
356 char **temp;
357
358 temp = getlist(p, "", LP_SEP);
359 mergelist (&(flbuf.templates), temp);
360 freelist (temp);
361 } else
362 flbuf.templates = getlist(p, "", LP_SEP);
363 break;
364
365 }
366
367 }
368 if (ferror(input)) {
369 LP_ERRMSG (ERROR, E_FL_READ);
370 return (1);
371 }
372
373 /*
374 * We have the input stored, now get the current copy of the
375 * filter(s). If no filter exists, we create it.
376 */
377
378 if (STREQU(NAME_ALL, filter)) {
379
380 /*
381 * Adding ``all'' means changing all filters to reflect
382 * the information in the input. We'll preload the
383 * filters so that we know how many there are.
384 */
385 if (
386 !(file = getfilterfile(FILTERTABLE))
387 || loadfilters(file) == -1
388 ) {
389 switch (errno) {
390 case ENOENT:
391 LP_ERRMSG (ERROR, E_FL_NOTALL);
392 break;
393 default:
394 same_complaints (FILTERTABLE, TABLE);
395 break;
396 }
397 return (1);
398 }
399
400 store = (FILTER *)malloc((nfilters + 1) * sizeof(FILTER));
401 if (!store) {
402 LP_ERRMSG (ERROR, E_LP_MALLOC);
403 return (1);
404 }
405
406 for (ps = store; (pf = getfilter(filter)); )
407 *ps++ = *pf;
408 ps->name = 0;
409
410 switch (errno) {
411 case ENOENT:
412 if (ps - store != nfilters) {
413 LP_ERRMSG1 (
414 ERROR,
415 E_FL_STRANGE,
416 getfilterfile(FILTERTABLE)
417 );
418 return (1);
419 }
420 break;
421 default:
422 same_complaints (FILTERTABLE, TABLE);
423 return (1);
424 }
425
426 } else {
427
428 store = (FILTER *)malloc(2 * sizeof(FILTER));
429 if (!store) {
430 LP_ERRMSG (ERROR, E_LP_MALLOC);
431 return (1);
432 }
433
434 if ((pf = getfilter(filter))) {
435 store[0] = *pf;
436 } else
437 switch (errno) {
438 case ENOENT:
439 /*
440 * We must be adding a new filter, so
441 * set up default values. Check that
442 * we'll have something reasonable to add.
443 */
444 pf = store;
445 pf->name = strdup(filter);
446 pf->command = 0;
447 pf->type = fl_slow;
448 pf->printer_types = 0;
449 pf->printers = 0;
450 pf->input_types = 0;
451 pf->output_types = 0;
452 pf->templates = 0;
453 if (!flbuf.command) {
454 LP_ERRMSG (ERROR, E_FL_NOCMD);
455 return (1);
456 }
457 break;
458 default:
459 same_complaints (FILTERTABLE, TABLE);
460 return (1);
461 }
462
463 store[1].name = 0;
464
465 }
466
467 at_least_one = ret = 0;
468 for (ps = store; ps->name; ps++) {
469
470 for (fld = 0; fld < FL_MAX; fld++)
471 if (real_fields[fld]) switch(fld) {
472 case FL_IGN_P:
473 case FL_NAME_P:
474 break;
475 case FL_CMD_P:
476 ps->command = flbuf.command;
477 break;
478 case FL_TYPE_P:
479 ps->type = flbuf.type;
480 break;
481 case FL_PTYPS_P:
482 ps->printer_types = flbuf.printer_types;
483 break;
484 case FL_ITYPS_P:
485 ps->input_types = flbuf.input_types;
486 break;
487 case FL_OTYPS_P:
488 ps->output_types = flbuf.output_types;
489 break;
490 case FL_PRTRS_P:
491 ps->printers = flbuf.printers;
492 break;
493 case FL_TMPS_P:
494 ps->templates = flbuf.templates;
495 break;
496 }
497
498 if (putfilter(ps->name, ps) == -1) {
499 if (errno == EBADF) switch (lp_errno) {
500 case LP_ETEMPLATE:
501 LP_ERRMSG (ERROR, E_FL_BADTEMPLATE);
502 break;
503 case LP_EKEYWORD:
504 LP_ERRMSG (ERROR, E_FL_BADKEY);
505 break;
506 case LP_EPATTERN:
507 LP_ERRMSG (ERROR, E_FL_BADPATT);
508 break;
509 case LP_EREGEX:
510 {
511 char * why;
512
513 extern int regerrno;
514
515
516 switch (regerrno) {
517 case 11:
518 why = "range endpoint too large";
519 break;
520 case 16:
521 why = "bad number";
522 break;
523 case 25:
524 why = "\"\\digit\" out of range";
525 break;
526 case 36:
527 why = "illegal or missing delimiter";
528 break;
529 case 41:
530 why = "no remembered search string";
531 break;
532 case 42:
533 why = "\\(...\\) imbalance";
534 break;
535 case 43:
536 why = "too many \\(";
537 break;
538 case 44:
539 why = "more than 2 numbers given in \\{...\\}";
540 break;
541 case 45:
542 why = "} expected after \\";
543 break;
544 case 46:
545 why = "first number exceeds second in \\{...\\}";
546 break;
547 case 49:
548 why = "[...] imbalance";
549 break;
550 case 50:
551 why = "regular expression overflow";
552 break;
553 }
554 LP_ERRMSG1 (ERROR, E_FL_BADREGEX, why);
555 break;
556 }
557 case LP_ERESULT:
558 LP_ERRMSG (ERROR, E_FL_BADRESULT);
559 break;
560 case LP_ENOMEM:
561 errno = ENOMEM;
562 same_complaints (FILTERTABLE, TABLE);
563 break;
564 } else
565 same_complaints (FILTERTABLE, TABLE);
566 ret = 1;
567 break;
568 } else
569 at_least_one = 1;
570
571 }
572
573 if (at_least_one)
574 (void)alert_spooler ();
575
576 return (ret);
577 }
578
579 /**
580 ** reload_filter()
581 **/
582
reload_filter(filter)583 int reload_filter (filter)
584 char *filter;
585 {
586 register FILTER *pf,
587 *store,
588 *ps;
589
590 char *factory_file;
591
592 int ret,
593 at_least_one;
594
595 /*
596 * ``Manually'' load the archived filters, so that a call
597 * to "getfilter()" will read from them instead of the regular
598 * table.
599 */
600 if (
601 !(factory_file = getfilterfile(FILTERTABLE_I))
602 || loadfilters(factory_file) == -1
603 ) {
604 switch (errno) {
605 case ENOENT:
606 LP_ERRMSG (ERROR, E_FL_NOFACTY);
607 break;
608 default:
609 same_complaints (FILTERTABLE_I, TABLE_I);
610 break;
611 }
612 return (1);
613 }
614
615 if (STREQU(NAME_ALL, filter)) {
616
617 store = (FILTER *)malloc((nfilters + 1) * sizeof(FILTER));
618 if (!store) {
619 LP_ERRMSG (ERROR, E_LP_MALLOC);
620 return (1);
621 }
622
623 for (ps = store; (pf = getfilter(filter)); )
624 *ps++ = *pf;
625 ps->name = 0;
626
627 switch (errno) {
628 case ENOENT:
629 if (ps - store != nfilters) {
630 LP_ERRMSG1 (
631 ERROR,
632 E_FL_STRANGE,
633 getfilterfile(FILTERTABLE_I)
634 );
635 return (1);
636 }
637 break;
638 default:
639 same_complaints (FILTERTABLE_I, TABLE_I);
640 return (1);
641 }
642
643 } else {
644
645 store = (FILTER *)malloc(2 * sizeof(FILTER));
646 if (!store) {
647 LP_ERRMSG (ERROR, E_LP_MALLOC);
648 return (1);
649 }
650
651 if (!(pf = getfilter(filter))) switch (errno) {
652 case ENOENT:
653 LP_ERRMSG (ERROR, E_FL_FACTYNM);
654 return (1);
655 default:
656 same_complaints (FILTERTABLE_I, TABLE_I);
657 return (1);
658 }
659
660 store[0] = *pf;
661 store[1].name = 0;
662
663 }
664
665 /*
666 * Having stored the archived filter(s) in our own area, clear
667 * the currently loaded table so that the subsequent calls to
668 * "putfilter()" will read in the regular table.
669 */
670 trash_filters ();
671
672 at_least_one = ret = 0;
673 for (ps = store; ps->name; ps++)
674 if (putfilter(ps->name, ps) == -1) {
675 same_complaints (FILTERTABLE, TABLE);
676 ret = 1;
677 break;
678 } else
679 at_least_one = 1;
680
681 if (at_least_one)
682 (void)alert_spooler ();
683
684 return (ret);
685 }
686
687 /**
688 ** delete_filter()
689 **/
690
delete_filter(filter)691 int delete_filter (filter)
692 char *filter;
693 {
694 if (delfilter(filter) == -1) switch (errno) {
695 case ENOENT:
696 LP_ERRMSG1 (ERROR, E_FL_UNKFILT, filter);
697 return (1);
698 default:
699 same_complaints (FILTERTABLE, TABLE);
700 return (1);
701 }
702
703 (void)alert_spooler ();
704
705 return (0);
706 }
707
708 /**
709 ** list_filter()
710 **/
711
712 static void _list_filter();
713
list_filter(filter)714 int list_filter (filter)
715 char *filter;
716 {
717 register FILTER *pf;
718
719 char *nl;
720
721 if (STREQU(NAME_ALL, filter)) {
722
723 nl = "";
724 while ((pf = getfilter(filter))) {
725 printf (gettext("%s(Filter \"%s\")\n"), nl, pf->name);
726 _list_filter (pf);
727 nl = "\n";
728 }
729
730 switch (errno) {
731 case ENOENT:
732 return (0);
733 default:
734 same_complaints (FILTERTABLE, TABLE);
735 return (1);
736 }
737
738 } else {
739
740 if ((pf = getfilter(filter))) {
741 _list_filter (pf);
742 return (0);
743 }
744
745 switch (errno) {
746 case ENOENT:
747 LP_ERRMSG1 (ERROR, E_FL_UNKFILT, filter);
748 return (1);
749 default:
750 same_complaints (FILTERTABLE, TABLE);
751 return (1);
752 }
753
754 }
755 }
756
_list_filter(pf)757 static void _list_filter (pf)
758 register FILTER *pf;
759 {
760 register char **pp,
761 *sep;
762
763 register int fld;
764
765 char * head;
766
767
768 for (fld = 0; fld < FL_MAX_P; fld++) switch (fld) {
769 case FL_IGN_P:
770 case FL_NAME_P:
771 break;
772 case FL_CMD_P:
773 printf (
774 "%s %s\n",
775 headings[fld].v,
776 (pf->command? pf->command : "")
777 );
778 break;
779 case FL_TYPE_P:
780 printf (
781 "%s %s\n",
782 headings[fld].v,
783 (pf->type == fl_fast? FL_FAST : FL_SLOW)
784 );
785 break;
786 case FL_PTYPS_P:
787 pp = pf->printer_types;
788 goto Lists;
789 case FL_ITYPS_P:
790 pp = pf->input_types;
791 goto Lists;
792 case FL_OTYPS_P:
793 pp = pf->output_types;
794 goto Lists;
795 case FL_PRTRS_P:
796 pp = pf->printers;
797 Lists: printlist_qsep = 1;
798 printlist_setup ("", "", LP_SEP, "");
799 printf ("%s ", headings[fld].v);
800 printlist (stdout, pp);
801 printf ("\n");
802 break;
803 case FL_TMPS_P:
804 head = makestr(headings[fld].v, " ", (char *)0);
805 printlist_qsep = 1;
806 printlist_setup (head, "", "\n", "\n");
807 printlist (stdout, pf->templates);
808 break;
809 }
810
811 return;
812 }
813
814 /**
815 ** opt() - GENERATE OPTION FROM FUNCTION NAME
816 **/
817
818 static char *opt (fnc)
819 int (*fnc)();
820 {
821 if (fnc == add_filter)
822 return ("-F");
823 else if (fnc == reload_filter)
824 return ("-i");
825 else if (fnc == list_filter)
826 return ("-l");
827 else if (fnc == delete_filter)
828 return ("-x");
829 else
830 return ("-?");
831 }
832
833 /**
834 ** alert_spooler() - TELL SPOOLER TO LOAD FILTER TABLE
835 **/
836
alert_spooler()837 static void alert_spooler ()
838 {
839 char msgbuf[MSGMAX];
840
841 int mtype;
842
843 short status;
844
845 /*
846 * If the attempt to open a message queue to the
847 * Spooler fails, assume it isn't running and just
848 * return--don't say anything, `cause the user may
849 * know. Any other failure deserves an error message.
850 */
851
852 if (mopen() == -1)
853 return;
854
855 (void)putmessage (msgbuf, S_LOAD_FILTER_TABLE);
856
857 if (msend(msgbuf) == -1)
858 goto Error;
859 if (mrecv(msgbuf, MSGMAX) == -1)
860 goto Error;
861
862 mtype = getmessage(msgbuf, R_LOAD_FILTER_TABLE, &status);
863 if (mtype != R_LOAD_FILTER_TABLE) {
864 LP_ERRMSG1 (ERROR, E_LP_BADREPLY, mtype);
865 (void)mclose ();
866 exit (1);
867 }
868
869 if (status == MOK)
870 goto NoError;
871
872 Error: LP_ERRMSG (ERROR, E_FL_NOSPLOAD);
873
874 NoError:(void)mclose ();
875 return;
876
877 }
878
879 /**
880 ** same_complaints() - PRINT COMMON ERROR MESSAGES
881 **/
882
same_complaints(table,type)883 static void same_complaints (table, type)
884 char *table;
885 int type;
886 {
887 switch (errno) {
888 case EACCES:
889 if (type == TABLE)
890 LP_ERRMSG1 (
891 ERROR,
892 E_FL_ACCESS,
893 getfilterfile(table)
894 );
895 else
896 LP_ERRMSG1 (
897 ERROR,
898 E_FL_ACCESSI,
899 getfilterfile(table)
900 );
901 break;
902 case EAGAIN:
903 case EDEADLK:
904 LP_ERRMSG1 (ERROR, E_LP_AGAIN, getfilterfile(table));
905 break;
906 default:
907 LP_ERRMSG2 (
908 ERROR,
909 E_FL_UNKNOWN,
910 getfilterfile(table),
911 strerror(errno)
912 );
913 break;
914 }
915 return;
916 }
917