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
45int			add_filter(),
46			reload_filter(),
47			delete_filter(),
48			list_filter();
49
50static void		alert_spooler(),
51			same_complaints();
52
53static 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
76static 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
99void			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
126int			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;
204Check:		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
259int			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
583int			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
691int			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
712static void		_list_filter();
713
714int			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
757static 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;
797Lists:		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
818static 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
837static 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
872Error:	LP_ERRMSG (ERROR, E_FL_NOSPLOAD);
873
874NoError:(void)mclose ();
875	return;
876
877}
878
879/**
880 ** same_complaints() - PRINT COMMON ERROR MESSAGES
881 **/
882
883static 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