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#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32#ifndef UUCHECK
33#include "uucp.h"
34#endif
35
36
37/*  field array indexes for PERMISSIONS parameters */
38#define U_LOGNAME	0
39#define U_MACHINE	1
40#define U_CALLBACK	2
41#define U_REQUEST	3
42#define U_SENDFILES	4
43#define U_READPATH	5
44#define U_WRITEPATH	6
45#define U_NOREADPATH	7
46#define U_NOWRITEPATH	8
47#define U_MYNAME	9
48#define U_COMMANDS	10
49#define U_VALIDATE	11
50#define U_PUBDIR	12
51#define U_DIRECT	13
52#define U_ALIAS		14
53#define U_PATH		15
54/*  NUMFLDS should be one more than the highest U_ value */
55#define NUMFLDS		16
56
57/* fields found in PERMISSIONS for requested system/login */
58static char *_Flds[NUMFLDS];
59
60/* keyword/value structure */
61struct keywords {
62	char* kword;
63	int kvalue;
64};
65static struct keywords _Kwords[] = {
66	{"LOGNAME", U_LOGNAME},
67	{"MACHINE", U_MACHINE},
68	{"CALLBACK", U_CALLBACK},
69	{"REQUEST", U_REQUEST},
70	{"SENDFILES", U_SENDFILES},
71	{"READ", U_READPATH},
72	{"WRITE", U_WRITEPATH},
73	{"NOREAD", U_NOREADPATH},
74	{"NOWRITE", U_NOWRITEPATH},
75	{"MYNAME", U_MYNAME},
76	{"COMMANDS", U_COMMANDS},
77	{"VALIDATE", U_VALIDATE},
78	{"PUBDIR", U_PUBDIR},
79	{"DIRECT", U_DIRECT},
80	{"ALIAS", U_ALIAS},
81	{"PATH", U_PATH},
82};
83
84#define MAXCMDS		30
85#define MAXPATHS	20
86
87/* for all options on paths - read, write, noread, nowrite */
88/* NB: all pointers assumed to point to static data */
89static char *_RPaths[MAXPATHS+1];
90static char *_WPaths[MAXPATHS+1];
91static char *_NoRPaths[MAXPATHS+1];
92static char *_NoWPaths[MAXPATHS+1];
93static char *_Commands[MAXCMDS+1];
94static char _Cmd_defaults[BUFSIZ];
95
96/* option variables */
97static int _Request;	/* TRUE can request, FALSE can not request files */
98static int _Switch;	/* FALSE requires a call back to send any files */
99static int _CallBack;	/* TRUE for call back for any transaction */
100static int _NoSpool;	/* TRUE if delivering directly to destination file */
101static char _MyName[MAXBASENAME+1];	/* Myname from PERMISSIONS file */
102/* NB: _Pubdir and _Path assumed to point to dynamic data */
103static char *_Pubdir = NULL;		/* PUBDIR from PERMISSIONS file */
104static char *_Path = NULL;		/* PATH from PERMISSIONS file */
105
106struct name_value
107{
108	char *name;
109	char *value;
110};
111
112/* file pointer for PERMISSIONS */
113static FILE *Fp = NULL;
114
115/* functions */
116extern char *next_token(), *nextarg();
117extern int parse_tokens(), canPath(), mkdirs();
118static void fillFlds();
119static void fillList();
120static int cmdMatch(), listMatch(), nameMatch(),
121	userFind(), validateFind();
122
123int
124noSpool()
125{
126	return(_NoSpool);
127}
128
129/*
130 * fill in fields for login name
131 * name - the login id
132 * rmtname - remote system name
133 *
134 * return:
135 *	0 -> found login name
136 *	FAIL -> did not find login
137 */
138
139int
140logFind(name, rmtname)
141char *name, *rmtname;
142{
143	int ret;
144	DEBUG(5, "logFind called (name: %s, ", name);
145	DEBUG(5, "rmtname: %s)\n", rmtname);
146
147	ret = validateFind (rmtname);
148	if (ret == SUCCESS) { /* found VALIDATE entry */
149	    ret = userFind (name, rmtname, U_VALIDATE);
150	    if (ret) {
151		DEBUG(5, "machine/login match failed%s", "");
152		return(FAIL);
153	    }
154	}
155	else
156	    ret = userFind (name, "", U_LOGNAME);
157
158	DEBUG(7, "_Request (%s), ",
159	    requestOK() ? "TRUE" : "FALSE");
160	DEBUG(7, "_Switch (%s), ",
161	    switchRole() ? "TRUE" : "FALSE");
162	DEBUG(7, "_CallBack (%s), ",
163	    callBack() ? "TRUE" : "FALSE");
164	DEBUG(7, "_MyName (%s), ", _MyName);
165	DEBUG(7, "_NoSpool (%s), ",
166	    noSpool() ? "TRUE" : "FALSE");
167	return(ret);
168}
169
170/*
171 * fill in fields for machine name
172 * return:
173 *	0 -> found machine name
174 *	FAIL -> did not find machine
175 */
176
177int
178mchFind(name)
179char *name;
180{
181	int i, ret;
182	DEBUG(5, "mchFind called (%s)\n", name);
183	if ( (ret = userFind (name, "", U_MACHINE)) == FAIL)
184	    /* see if there is a default line */
185	    (void) userFind ("OTHER", "", U_MACHINE);
186
187	/*  mchFind is from MASTER mode - switch role is always ok */
188	_Switch = TRUE;
189
190	DEBUG(7, "_Request (%s), ",
191	    requestOK() ? "TRUE" : "FALSE");
192	DEBUG(7, "_Switch (%s), ",
193	    switchRole() ? "TRUE" : "FALSE");
194	DEBUG(7, "_CallBack (%s), ",
195	    callBack() ? "TRUE" : "FALSE");
196	DEBUG(7, "_MyName (%s), ", _MyName);
197	DEBUG(7, "_NoSpool (%s), ",
198	    noSpool() ? "TRUE" : "FALSE");
199	for (i=0; _Commands[i] != NULL; i++)
200	    DEBUG(7, "_Commands %s\n",  _Commands[i]);
201	return(ret);
202}
203
204/*
205 * this function will find a login name in the LOGNAME
206 * field.
207 * input:
208 *	name	-> who the remote says he/she is
209 * return:
210 *	SUCCESS	-> found
211 *	FAIL	-> not found
212 */
213static int
214nameMatch(name, fld)
215char *name, *fld;
216{
217	char *arg;
218
219	if (fld == NULL)
220	    return(FAIL);
221
222	while (*fld) {
223	    fld = nextarg(fld, &arg);
224	    if (EQUALS(arg, name))
225		return(SUCCESS);
226	}
227	return (FAIL);
228}
229
230
231/*
232 * interpret the _Flds options and set the option variables
233 */
234static void
235fillFlds()
236{
237
238	if (_Flds[U_REQUEST] != NULL) {
239		if (EQUALS(_Flds[U_REQUEST], "yes"))
240			_Request = TRUE;
241		else
242			_Request = FALSE;
243	}
244
245	if (_Flds[U_SENDFILES] != NULL) {
246		if (EQUALS(_Flds[U_SENDFILES], "yes"))
247			_Switch = TRUE;
248		else
249			_Switch = FALSE;
250	}
251
252	if (_Flds[U_CALLBACK] != NULL) {
253		if (EQUALS(_Flds[U_CALLBACK], "yes"))
254			_CallBack = TRUE;
255		else
256			_CallBack = FALSE;
257	}
258
259	if (_Flds[U_DIRECT] != NULL) {
260		if (EQUALS(_Flds[U_DIRECT], "yes"))
261			_NoSpool = TRUE;
262		else
263			_NoSpool = FALSE;
264	}
265
266	if (_Flds[U_MYNAME] != NULL) {
267		strncpy(_MyName, _Flds[U_MYNAME], MAXBASENAME);
268		_MyName[MAXBASENAME] = NULLCHAR;
269	}
270
271	if (_Flds[U_PUBDIR] != NULL) {
272		if (_Pubdir != NULL)
273		    free(_Pubdir);	/* get rid of previous one */
274		_Pubdir = strdup(_Flds[U_PUBDIR]);
275#ifndef UUCHECK
276		ASSERT(_Pubdir != NULL, Ct_ALLOCATE, _Flds[U_PUBDIR], 0);
277#else /* UUCHECK */
278		if (_Pubdir == NULL) {
279		    perror(gettext("malloc() error"));
280		    exit(1);
281		}
282#endif /* UUCHECK */
283		Pubdir = _RPaths[0] = _WPaths[0] = _Pubdir; /* reset default */
284	}
285
286	if (_Flds[U_PATH] != NULL) {
287		if (_Path != NULL)
288		    free(_Path);	/* get rid of previous one */
289		_Path = strdup(_Flds[U_PATH]);
290#ifndef UUCHECK
291		ASSERT(_Path != NULL, Ct_ALLOCATE, _Flds[U_PATH], 0);
292#else /* UUCHECK */
293		if (_Path == NULL) {
294		    perror(gettext("malloc() error"));
295		    exit(1);
296		}
297#endif /* UUCHECK */
298	}
299
300	return;
301}
302
303/*
304 * fill in the list vector for the system/login
305 * input:
306 *	type - list type (read, write, noread, nowrite, command)
307 * output:
308 *	list - filled in with items.
309 * return:
310 *	number of items in list
311 */
312static void
313fillList(type, list)
314int type;
315char *list[];
316{
317	char *p;
318	int num;
319	int maxlist = 0;
320
321	p = _Flds[type];
322
323	/* find list limit */
324	if (type == U_READPATH || type == U_WRITEPATH
325	 || type == U_NOREADPATH || type == U_NOWRITEPATH)
326		maxlist = MAXPATHS;
327	else if (type == U_COMMANDS)
328		maxlist = MAXCMDS;
329
330	if (p == NULL || !*p) {
331		 /* no names specified, default already setup */
332		return;
333	}
334
335	num = 0;
336	while (*p && num < maxlist) {
337		list[num] = p;
338		if (*p == ':') {	/* null path */
339			*p++ = NULLCHAR;
340			continue;
341		}
342		while (*p && *p != ':')
343			p++;
344		if (*p == ':')
345			*p++ = NULLCHAR;
346		DEBUG(7, "list (%s) ", list[num]);
347		num++;
348	}
349	DEBUG(7, "num = %d\n", num);
350	list[num] = NULL;
351	return;
352}
353
354/*
355 * Find the line of PERMISSIONS for login.
356 * The search is determined by the type field
357 * (type=U_LOGNAME, U_MACHINE or U_VALIDATE)
358 * For U_LOGNAME:
359 *	search for "name" in a LOGNAME= option
360 * For U_MACHINE:
361 *	search for "name" in a MACHINE= option
362 * For U_VALIDATE:
363 *	search for "rmtname" in a VALIDATE= option and
364 *	for the same entry see if "name" is in the LOGNAME= option
365 * input:
366 *	name -> search name
367 *	logname -> for validate entry
368 *	type -> U_MACHINE or U_LOGNAME
369 * output:
370 *	The global values of all options will be set
371 *	(e.g. _RPaths, _WPaths,  _Request, ...)
372 * return:
373 *	0 -> ok
374 *	FAIL -> no match found
375 */
376static int
377userFind(name, rmtname, type)
378char *name, *rmtname;
379int type;
380{
381	char *p, *arg, *buf = NULL;
382	static char default_buf[BUFSIZ];
383
384	if (name != NULL && strcmp(name, "DEFAULT") != 0) {
385		/* call ourself recursively to set defaults */
386		(void) userFind("DEFAULT", "", U_MACHINE);
387	} else {
388		/*
389		 * Handle case where looking for DEFAULT entry.
390		 * First initialize all defaults to their "base"
391		 * values.  Then the DEFAULT entry, if found,
392		 * will override these settings.
393		 */
394		_Request = FALSE;
395		_CallBack = FALSE;
396		_Switch = FALSE;
397		_NoSpool = FALSE;
398		_MyName[0] = NULLCHAR;
399		_RPaths[0] = _WPaths[0] = PUBDIR;	/* default is public */
400		_RPaths[1] = _WPaths[1] = NULLCHAR;
401		_NoRPaths[0] = NULLCHAR;
402		_NoWPaths[0] = NULLCHAR;
403		if (_Pubdir != NULL)
404			free(_Pubdir);
405		Pubdir = _Pubdir = strdup(PUBDIR);
406		if (_Path != NULL)
407			free(_Path);
408		_Path = strdup(PATH);
409		/* set up Commands defaults */
410		_Flds[U_COMMANDS] = strcpy(_Cmd_defaults, DEFAULTCMDS);
411		fillList(U_COMMANDS, _Commands);
412		/*
413		 * put defaults we read in in here so they're not overwritten
414		 * by non-DEFAULT entries.
415		 */
416		buf = default_buf;
417	}
418
419	if (name == NULL)	/* use defaults */
420		return(0);	/* I don't think this will ever happen */
421
422	if ( (Fp = fopen(PERMISSIONS, "r")) == NULL) {
423		DEBUG(5, "can't open %s\n", PERMISSIONS);
424		return(FAIL);
425	}
426
427	for (;;) {
428	    if (parse_tokens (_Flds, buf) != 0) {
429		(void) fclose(Fp);
430		DEBUG(5, "name (%s) not found; return FAIL\n", name);
431		return(FAIL);
432	    }
433
434	    p = _Flds[type];
435	    while (p && *p) {
436		p = nextarg(p, &arg);
437		switch (type) {
438		case U_VALIDATE:
439		    if (EQUALS(arg, rmtname)
440			&& nameMatch(name, _Flds[U_LOGNAME])==SUCCESS)
441				break;
442		    continue;
443
444		case U_LOGNAME:
445		    if (EQUALS(arg, name))
446				break;
447		    continue;
448
449		case U_MACHINE:
450		    if (EQUALSN(arg, name, MAXBASENAME))
451				break;
452		    continue;
453		}
454
455		(void) fclose(Fp);
456		fillFlds();
457
458		/* fill in path lists */
459		fillList(U_READPATH, _RPaths);
460		fillList(U_WRITEPATH, _WPaths);
461		if (!requestOK())
462		    _Flds[U_NOREADPATH] = "/";
463		fillList(U_NOREADPATH, _NoRPaths);
464		fillList(U_NOWRITEPATH, _NoWPaths);
465
466		/* fill in command list */
467		fillList(U_COMMANDS, _Commands);
468
469		return(0);
470	    }
471	}
472}
473
474/*
475 * see if name is in a VALIDATE option
476 * return:
477 *	FAIL -> not found
478 *	SUCCESS -> found
479 */
480static int
481validateFind(name)
482char *name;
483{
484
485	if ( (Fp = fopen(PERMISSIONS, "r")) == NULL) {
486		DEBUG(5, "can't open %s\n", PERMISSIONS);
487		return(FAIL);
488	}
489
490	for (;;) {
491	    if (parse_tokens (_Flds, NULL) != 0) {
492		DEBUG(5, "validateFind (%s) FAIL\n", name);
493		(void) fclose(Fp);
494		return(FAIL);
495	    }
496
497	    if (_Flds[U_VALIDATE] == NULL)
498		continue;
499	    if (nameMatch(name, _Flds[U_VALIDATE])==SUCCESS) {
500		(void) fclose(Fp);
501		return (SUCCESS);
502	    }
503	}
504
505}
506
507/*
508 * see if name is in an ALIAS option
509 * return:
510 *	NULL -> not found
511 *	otherwise -> machine name
512 */
513char *
514aliasFind(name)
515char *name;
516{
517
518	if ( (Fp = fopen(PERMISSIONS, "r")) == NULL) {
519		DEBUG(5, "can't open %s\n", PERMISSIONS);
520		return(NULL);
521	}
522
523	for (;;) {
524	    if (parse_tokens (_Flds, NULL) != 0) {
525		DEBUG(5, "aliasFind (%s) FAIL\n", name);
526		(void) fclose(Fp);
527		return(NULL);
528	    }
529
530	    if (_Flds[U_ALIAS] == NULL)
531		continue;
532	    if (nameMatch(name, _Flds[U_ALIAS])==SUCCESS) {
533		(void) fclose(Fp);
534#ifndef UUCHECK
535		ASSERT(strchr(_Flds[U_MACHINE], ':') == NULL,
536		    "PERMISSIONS file: ALIAS is one-to-many:",
537		    _Flds[U_MACHINE], 0);
538#else /* UUCHECK */
539		if (strchr(_Flds[U_MACHINE], ':') != NULL) {
540		    printf(gettext("ALIAS is one-to-many: %s -> %s\n"),
541			name, _Flds[U_MACHINE]);
542		    return(NULL);
543		}
544#endif /* UUCHECK */
545		return(_Flds[U_MACHINE]);
546	    }
547	}
548
549}
550
551/*
552 * parse a line in PERMISSIONS and return a vector
553 * of fields (flds)
554 *
555 * return:
556 *	0 - OK
557 *	EOF - at end of file
558 */
559int
560parse_tokens(flds, buf)
561char *flds[];
562char *buf;
563{
564	int i;
565	char *p;
566	struct name_value pair;
567	static char _line[BUFSIZ];
568	char *line = buf;
569
570	if (buf == NULL)
571		line = _line;	/* if no buffer specified, use default */
572	/* initialize defaults  in case parameter is not specified */
573	for (i=0;i<NUMFLDS;i++)
574		flds[i] = NULL;
575
576	if (getuline(Fp, line) == 0)
577		return(EOF);
578
579	for (p=line;p && *p;) {
580		p = next_token (p, &pair);
581
582		for (i=0; i<NUMFLDS; i++) {
583			if (EQUALS(pair.name, _Kwords[i].kword)) {
584				flds[i] = pair.value;
585				break;
586			}
587		}
588#ifndef UUCHECK
589		ASSERT(i<NUMFLDS, "PERMISSIONS file: BAD OPTION--",
590		    pair.name, NUMFLDS);
591#else /* UUCHECK */
592		if (i >= NUMFLDS) {
593			DEBUG(3, "bad option (%s) in PERMISSIONS\n",pair.name);
594			(void) printf("\n*****************************\n");
595			(void) printf(gettext("**BAD OPTION in PERMISSIONS file: %s\n"),
596				pair.name);
597			(void) printf("*****************************\n");
598			Uerrors++;
599			return(0);
600		}
601#endif /* UUCHECK */
602
603	}
604	return(0);
605}
606
607/*
608 * return a name value pair
609 *	string	-> input pointer
610 *	pair	-> name value pair
611 * return:
612 *	pointer to next character
613 */
614char *
615next_token (string, pair)
616char *string;
617struct name_value *pair;
618{
619	char	*prev = _uu_setlocale(LC_ALL, "C");
620
621	while ( (*string) && ((*string == '\t') || (*string == ' ')) )
622		string++;
623
624	pair->name = string;
625	while ((*string) && (*string != '='))
626		string++;
627	if (*string)
628		*string++ = NULLCHAR;
629
630	pair->value = string;
631	while ((*string) && (*string != '\t') && (*string != ' ')
632	    && (*string != '\n'))
633		string++;
634
635	if (*string)
636		*string++ = NULLCHAR;
637
638	(void) _uu_resetlocale(LC_ALL, prev);
639	return (string);
640}
641
642/*
643 * get a line from the PERMISSIONS
644 * take care of comments (#) in col 1
645 * and continuations (\) in last col
646 * return:
647 *	len of line
648 *	0 -> end of file
649 */
650int
651getuline(fp, line)
652FILE *fp;
653char *line;
654{
655	char *p, *c;
656	char buf[BUFSIZ];
657
658	p = line;
659	for (;fgets(buf, BUFSIZ, fp) != NULL;) {
660		/* remove trailing white space */
661		c = &buf[strlen(buf)-1];
662		while (c>=buf && (*c == '\n' || *c == '\t' || *c == ' ') )
663			*c-- = NULLCHAR;
664
665		if (buf[0] == '#' || buf[0] == '\n' || buf[0] == NULLCHAR)
666			continue;
667		(void) strcpy(p, buf);
668		p += strlen(buf);
669		if ( *(p-1) == '\\')
670			p--;
671		else
672			break;
673	}
674
675	return(p-line);
676}
677
678
679#define SMAX	15
680
681/*
682 * get the next colon separated argument from the list
683 * return:
684 *	p -> pointer to next arg in string
685 * input:
686 *	str -> pointer to input string
687 * output:
688 *	name -> pointer to arg string
689 */
690char *
691nextarg(str, name)
692char *str, **name;
693{
694	char *p, *b;
695	static char buf[SMAX+1];
696
697	for(b=buf,p=str; *p != ':' && *p && b < buf+SMAX;)
698		*b++ = *p++;
699	*b++ = NULLCHAR;
700	if (*p == ':')
701		p++;
702	*name = buf;
703	return(p);
704}
705
706/*
707 * check if requesting files is permitted
708 * return
709 *	TRUE -> request permitted
710 *	FALSE -> request denied
711 */
712int
713requestOK()
714{
715	return(_Request);
716}
717
718/*
719 * myName - return my name from PERMISSIONS file
720 *	or if not there, from  uucpname()
721 * return: none
722 */
723
724void
725myName(name)
726char *name;
727{
728	if (*_MyName)
729		strcpy(name, _MyName);
730	else
731		uucpname(name);
732	return;
733}
734
735/*
736 * check for callback required for any transaction
737 * return:
738 *	TRUE -> callback required
739 *	FALSE-> callback NOT required
740 */
741int
742callBack()
743{
744	return(_CallBack);
745}
746
747/*
748 * check for callback to send any files from here
749 * This means that the called (SLAVE) system will not switch roles.
750 * return:
751 *	TRUE -> callback requried to send files
752 *	FALSE-> callback NOT required to send files
753 */
754int
755switchRole()
756{
757	return(_Switch);
758}
759
760/*
761 * Check to see if command is valid for a specific machine.
762 * The PERMISSIONS file has an option COMMANDS=name1:name2:... for
763 * any machine that does not have the default list which is
764 * rmail
765 * Note that the PERMISSIONS file is read once for each system
766 * at the time the Rmtname is set in xprocess().
767 * Return codes:
768 *	ok: TRUE
769 *	fail: FALSE
770 */
771int
772cmdOK(cmd, fullcmd)
773char	*cmd, *fullcmd;
774{
775	DEBUG(7, "cmdOK(%s, )\n", cmd);
776	return(cmdMatch(cmd, fullcmd));
777}
778
779
780/*
781 * check a name against a list
782 * input:
783 *	name	-> name
784 *	list	-> list of names
785 * return:
786 *	TRUE	-> found path
787 *	FALSE	-> not found
788 */
789static int
790listMatch(name, list)
791char *name, *list[];
792{
793    int i;
794    char *temp, *tend;
795    struct stat statbuf;
796    dev_t _dev[MAXPATHS+1];
797    ino_t _ino[MAXPATHS+1];
798
799    /* ino set to 0 so stat is only done first time */
800    for (i=0; list[i] != NULL; i++)
801	_ino[i] = 0;
802
803    /* try to match inodes */
804    if ( (temp = strdup(name)) != NULL ) {
805	for ( tend = temp + strlen(temp) ; *temp; ) {
806	    if ( stat(temp, &statbuf) == 0 ) {
807		for (i=0; list[i] != NULL; i++) {
808		    if ( _ino[i] == 0 ) {
809			struct stat tempbuf;
810			if ( stat(list[i], &tempbuf) == 0 ) {
811			    _dev[i] = tempbuf.st_dev;
812			    _ino[i] = tempbuf.st_ino;
813			}
814		    }
815		    if ( _dev[i] == statbuf.st_dev
816		      && _ino[i] == statbuf.st_ino ) {
817			free(temp);
818			return(TRUE);
819		    }
820		}
821	    }
822	    *tend = '\0';
823	    if ( (tend = strrchr(temp, '/')) == NULL ) {
824		free(temp);
825		break;
826	    } else
827		*(tend+1) = '\0';
828	}
829    }
830
831    return(FALSE);
832}
833
834
835/*
836 * Check "name" against a BASENAME or full name of _Commands list.
837 * If "name" specifies full path, check full, else check BASENAME.
838 *  e.g. "name" rmail matches list item /usr/bin/rmail
839 * input:
840 *	name	-> name
841 * output:
842 *	fullname -> copy full command name into fullname if
843 *		    a full path was specified in _Commands;
844 *		    if not, put name into fullname.
845 * return:
846 *	TRUE	-> found path
847 *	FALSE	-> not found
848 */
849static int
850cmdMatch(name, fullname)
851char *name;
852char *fullname;
853{
854	int i;
855	char *bname;
856	int allok = FALSE;
857
858	for (i=0; _Commands[i] != NULL; i++) {
859		if (EQUALS(_Commands[i], "ALL")) {
860			/* if ALL specified in the list
861			 * set allok and continue in case
862			 * a full path name is specified for the command
863			 */
864			allok = TRUE;
865			continue;
866		}
867		if (name[0] != '/')
868			bname = BASENAME(_Commands[i], '/');
869		else
870			bname = _Commands[i];
871		DEBUG(7, "bname=%s\n", bname);
872		if (EQUALS(bname, name)) {
873			(void) strcpy(fullname, _Commands[i]);
874			return(TRUE);
875		}
876	}
877	if (allok == TRUE) {
878		/* ALL was specified and the command was not found in list */
879		(void) strcpy(fullname, name);
880		return(TRUE);
881	}
882	(void) strcpy(fullname, "NuLL");	/* this is a dummy command */
883	return(FALSE);
884}
885
886
887/*
888 * check the paths for this login/machine
889 * input:
890 *	path	pathname
891 *	flag	CK_READ or CK_WRITE
892 * output:
893 *	path	may be modified to canonical form
894 *		(../, ./, // will be interpreted/removed)
895 * returns:
896 *	0		-> success
897 *	FAIL		-> failure - not a valid path for access
898 */
899int
900chkpth(path, flag)
901char *path;
902{
903	char *s;
904
905	/*
906	 * this is probably redundant,
907	 * because expfile did it, but that's ok
908	 * Note - the /../ check is not required because of canPath
909	 */
910	if (canPath(path) == FAIL)
911		return(FAIL);
912
913	if (flag == CK_READ)
914		if (listMatch(path, _RPaths)
915		&& !listMatch(path, _NoRPaths))
916			return(0);
917	if (flag == CK_WRITE)
918		if (listMatch(path, _WPaths)
919		&& !listMatch(path, _NoWPaths))
920			return(0);
921
922
923	/* ok if uucp generated D. or X. name for the spool directory */
924	if (PREFIX(RemSpool, path) ) {
925    		s = &path[strlen(RemSpool)];
926		if ( (*s++ == '/')
927		  && (*s == DATAPRE || *s == XQTPRE)
928		  && (*(++s) == '.')
929		  && (strchr(s, '/') == NULL) )
930			return(0);
931	}
932
933	/*  path name not valid */
934	return(FAIL);
935}
936
937/*
938 * check write permission of file.
939 * if mopt != NULL and permissions are ok,
940 * a side effect of this routine is to make
941 * directories up to the last part of the
942 * "to" ( if they do not exit).
943 * Input:
944 *	to - a path name of the destination file or directory
945 *	from - full path name of source file
946 *	opt - create directory option (NULL - don't create)
947 * Output:
948 *	to - will be the full path name of the destination file
949 * returns:
950 *	0	->success
951 *	FAIL	-> failure
952 */
953int
954chkperm(from, to, opt)
955char *from, *to, *opt;
956{
957	char *lxp, *p;
958	struct stat s;
959	char dir[MAXFULLNAME];
960
961	if (*(p = LASTCHAR(to)) == '/') {
962	    if (strlcpy(p+1, BASENAME(from, '/'), MAXFULLNAME - strlen(to)) >=
963		MAXFULLNAME - strlen(to)) {
964		    return(FAIL);
965	    }
966	} else if (DIRECTORY(to)) {
967	    *++p = '/';
968	    if (strlcpy(p+1, BASENAME(from, '/'), MAXFULLNAME - strlen(to)) >=
969		MAXFULLNAME - strlen(to)) {
970		    return(FAIL);
971	    }
972	}
973
974	/* to is now the full path name of the destination file */
975
976	if (WRITEANY(to))
977	    return(0);
978	if (stat(to, &s) == 0)
979	    return(FAIL);	/* file exists, but not writeable */
980
981	/* file does not exist--check directory and make when necessary */
982
983	(void) strcpy(dir, to);
984	if ( (lxp=strrchr(dir, '/')) == NULL)
985	    return(FAIL);	/* no directory part of name */
986	if (lxp == dir)	/* at root */
987	    lxp++;
988	*lxp = NULLCHAR;
989
990	/* should check WRITEANY on parent before mkdirs() */
991	if (!DIRECTORY(dir)) {
992	    if (opt == NULL)
993		return(FAIL);	/* no directory and no opt to make them */
994	    else if (mkdirs(dir, PUBMASK) == FAIL)
995		return(FAIL);
996	}
997
998	/* the directory now exists--check for writability */
999	if (EQUALS(RemSpool, dir) || WRITEANY(dir))
1000	    return(0);
1001
1002	return(FAIL);
1003}
1004