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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved  	*/
28
29#include "uucp.h"
30
31/*
32 * uucp
33 * user id
34 * make a copy in spool directory
35 */
36int Copy = 0;
37static int _Transfer = 0;
38char Nuser[32];
39char *Ropt = " ";
40char Optns[10];
41char Uopts[BUFSIZ];
42char Xopts[BUFSIZ];
43char Sgrade[NAMESIZE];
44int Mail = 0;
45int Notify = 0;
46
47void cleanup(), ruux(), usage();
48int eaccess(), guinfo(), vergrd(), gwd(), ckexpf(), uidstat(), uidxcp(),
49	copy(), gtcfile();
50void commitall(), wfabort(), mailst(), gename(), svcfile();
51
52char	Sfile[MAXFULLNAME];
53
54int
55main(argc, argv, envp)
56int argc;
57char *argv[];
58char	**envp;
59{
60	char *jid();
61	int	ret;
62	int	errors = 0;
63	char	*fopt, *sys2p;
64	char	sys1[MAXFULLNAME], sys2[MAXFULLNAME];
65	char	fwd1[MAXFULLNAME], fwd2[MAXFULLNAME];
66	char	file1[MAXFULLNAME], file2[MAXFULLNAME];
67	short	jflag = 0;	/* -j flag  Jobid printout */
68	extern int	split();
69
70
71	/* Set locale environment variables local definitions */
72	(void) setlocale(LC_ALL, "");
73#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
74#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it wasn't */
75#endif
76	(void) textdomain(TEXT_DOMAIN);
77
78	/* this fails in some versions, but it doesn't hurt */
79	Uid = getuid();
80	Euid = geteuid();
81	if (Uid == 0)
82		(void) setuid(UUCPUID);
83
84	/* choose LOGFILE */
85	(void) strcpy(Logfile, LOGUUCP);
86
87	Env = envp;
88	fopt = NULL;
89	(void) strcpy(Progname, "uucp");
90	Pchar = 'U';
91	*Uopts = NULLCHAR;
92	*Xopts = NULLCHAR;
93	*Sgrade = NULLCHAR;
94
95	if (eaccess(GRADES, 0) != -1) {
96		Grade = 'A';
97		Sgrades = TRUE;
98		sprintf(Sgrade, "%s", "default");
99	}
100
101	/*
102	 * find name of local system
103	 */
104	uucpname(Myname);
105	Optns[0] = '-';
106	Optns[1] = 'd';
107	Optns[2] = 'c';
108	Optns[3] = Nuser[0] = Sfile[0] = NULLCHAR;
109
110	/*
111	 * find id of user who spawned command to
112	 * determine
113	 */
114	(void) guinfo(Uid, User);
115
116	/*
117	 * create/append command log
118	 */
119	commandlog(argc,argv);
120
121	while ((ret = getopt(argc, argv, "Ccdfg:jmn:rs:x:")) != EOF) {
122		switch (ret) {
123
124		/*
125		 * make a copy of the file in the spool
126		 * directory.
127		 */
128		case 'C':
129			Copy = 1;
130			Optns[2] = 'C';
131			break;
132
133		/*
134		 * not used (default)
135		 */
136		case 'c':
137			break;
138
139		/*
140		 * not used (default)
141		 */
142		case 'd':
143			break;
144		case 'f':
145			Optns[1] = 'f';
146			break;
147
148		/*
149		 * set service grade
150		 */
151		case 'g':
152			snprintf(Xopts, sizeof (Xopts), "-g%s", optarg);
153			if (!Sgrades) {
154				if (strlen(optarg) < (size_t)2 && isalnum(*optarg))
155					Grade = *optarg;
156				else {
157					(void) fprintf(stderr, gettext("No"
158					    " administrator defined service"
159					    " grades available on this"
160					    " machine.\n"));
161					(void) fprintf(stderr, gettext("UUCP"
162					    " service grades range from"
163					    " [A-Z][a-z] only.\n"));
164					cleanup(-1);
165				}
166			}
167			else {
168				(void) strncpy(Sgrade, optarg, NAMESIZE-1);
169				Sgrade[NAMESIZE-1] = NULLCHAR;
170				if (vergrd(Sgrade) != SUCCESS)
171					cleanup(FAIL);
172			}
173			break;
174
175		case 'j':	/* job id */
176			jflag = 1;
177			break;
178
179		/*
180		 * send notification to local user
181		 */
182		case 'm':
183			Mail = 1;
184			(void) strcat(Optns, "m");
185			break;
186
187		/*
188		 * send notification to user on remote
189		 * if no user specified do not send notification
190		 */
191		case 'n':
192			/*
193			 * We should add "n" option to Optns only once,
194			 * even if multiple -n option are passed to uucp
195			 */
196			if (!Notify) {
197				(void) strlcat(Optns, "n", sizeof (Optns));
198				Notify = 1;
199			}
200			(void) sprintf(Nuser, "%.8s", optarg);
201
202			/*
203			 * We do the copy multiple times when multiple
204			 * -n options are specified, but
205			 * only the last -n value is used.
206	 		 */
207			(void) snprintf(Uopts, sizeof (Uopts), "-n%s ", Nuser);
208
209			break;
210
211		/*
212		 * create JCL files but do not start uucico
213		 */
214		case 'r':
215			Ropt = "-r";
216			break;
217
218		/*
219		 * return status file
220		 */
221		case 's':
222			fopt = optarg;
223			/* "m" needed for compatability */
224			(void) strcat(Optns, "mo");
225			break;
226
227		/*
228		 * turn on debugging
229		 */
230		case 'x':
231			Debug = atoi(optarg);
232			if (Debug <= 0)
233				Debug = 1;
234#ifdef SMALL
235			fprintf(stderr, gettext("WARNING: uucp built with SMALL"
236			    " flag defined -- no debug info available\n"));
237#endif /* SMALL */
238			break;
239
240		default:
241			usage();
242			break;
243		}
244	}
245	DEBUG(4, "\n\n** %s **\n", "START");
246	gwd(Wrkdir);
247	if (fopt) {
248		if (*fopt != '/')
249			(void) snprintf(Sfile, MAXFULLNAME, "%s/%s",
250					Wrkdir, fopt);
251		else
252			(void) snprintf(Sfile, MAXFULLNAME, "%s", fopt);
253
254	}
255	else
256		if (strlcpy(Sfile, "dummy", sizeof (Sfile)) >= sizeof (Sfile))
257			return (2);
258
259	/*
260	 * work in WORKSPACE directory
261	 */
262	ret = chdir(WORKSPACE);
263	if (ret != 0) {
264		(void) fprintf(stderr, gettext("No work directory - %s -"
265		    " get help\n"), WORKSPACE);
266		cleanup(-12);
267	}
268
269	if (Nuser[0] == NULLCHAR)
270		(void) strcpy(Nuser, User);
271	(void) strcpy(Loginuser, User);
272	DEBUG(4, "UID %ld, ", (long) Uid);
273	DEBUG(4, "User %s\n", User);
274	if (argc - optind < 2) {
275		usage();
276	}
277
278	/*
279	 * set up "to" system and file names
280	 */
281
282	(void) split(argv[argc - 1], sys2, fwd2, file2);
283	if (*sys2 != NULLCHAR) {
284		(void) strncpy(Rmtname, sys2, MAXBASENAME);
285		Rmtname[MAXBASENAME] = NULLCHAR;
286
287		/* get real Myname - it depends on who I'm calling--Rmtname */
288		(void) mchFind(Rmtname);
289		myName(Myname);
290
291		if (versys(sys2) != 0) {
292			(void) fprintf(stderr,
293			    gettext("bad system: %s\n"), sys2);
294			cleanup(-EX_NOHOST);
295		}
296	}
297
298	DEBUG(9, "sys2: %s, ", sys2);
299	DEBUG(9, "fwd2: %s, ", fwd2);
300	DEBUG(9, "file2: %s\n", file2);
301
302	/*
303	 * if there are more than 2 argsc, file2 is a directory
304	 */
305	if (argc - optind > 2)
306		(void) strcat(file2, "/");
307
308	/*
309	 * do each from argument
310	 */
311
312	for ( ; optind < argc - 1; optind++) {
313	    (void) split(argv[optind], sys1, fwd1, file1);
314	    if (*sys1 != NULLCHAR) {
315		if (versys(sys1) != 0) {
316			(void) fprintf(stderr,
317			    gettext("bad system: %s\n"), sys1);
318			cleanup(-EX_NOHOST);
319		}
320	    }
321
322	    /*  source files can have at most one ! */
323	    if (*fwd1 != NULLCHAR) {
324		/* syntax error */
325	        (void) fprintf(stderr,
326		    gettext("illegal  syntax %s\n"), argv[optind]);
327	        exit(2);
328	    }
329
330	    /*
331	     * check for required remote expansion of file names -- generate
332	     *	and execute a uux command
333	     * e.g.
334	     *		uucp   owl!~/dan/..  ~/dan/
335	     *
336	     * NOTE: The source file part must be full path name.
337	     *  If ~ it will be expanded locally - it assumes the remote
338	     *  names are the same.
339	     */
340
341	    if (*sys1 != NULLCHAR)
342		if ((strchr(file1, '*') != NULL
343		      || strchr(file1, '?') != NULL
344		      || strchr(file1, '[') != NULL)) {
345		        /* do a uux command */
346		        if (ckexpf(file1) == FAIL)
347			    exit(6);
348			(void) strncpy(Rmtname, sys1, MAXBASENAME);
349			Rmtname[MAXBASENAME] = NULLCHAR;
350			/* get real Myname - it depends on who I'm calling--Rmtname */
351			(void) mchFind(Rmtname);
352			myName(Myname);
353			if (*sys2 == NULLCHAR)
354			    sys2p = Myname;
355		        ruux(sys1, sys1, file1, sys2p, fwd2, file2);
356		        continue;
357		}
358
359	    /*
360	     * check for forwarding -- generate and execute a uux command
361	     * e.g.
362	     *		uucp uucp.c raven!owl!~/dan/
363	     */
364
365	    if (*fwd2 != NULLCHAR) {
366	        ruux(sys2, sys1, file1, "", fwd2, file2);
367	        continue;
368	    }
369
370	    /*
371	     * check for both source and destination on other systems --
372	     *  generate and execute a uux command
373	     */
374
375	    if (*sys1 != NULLCHAR )
376		if ( (!EQUALS(Myname, sys1))
377	    	  && *sys2 != NULLCHAR
378	    	  && (!EQUALS(sys2, Myname)) ) {
379		    ruux(sys2, sys1, file1, "", fwd2, file2);
380	            continue;
381	        }
382
383
384	    sys2p = sys2;
385	    if (*sys1 == NULLCHAR) {
386		if (*sys2 == NULLCHAR)
387		    sys2p = Myname;
388		(void) strcpy(sys1, Myname);
389	    } else {
390		(void) strncpy(Rmtname, sys1, MAXBASENAME);
391		Rmtname[MAXBASENAME] = NULLCHAR;
392		/* get real Myname - it depends on who I'm calling--Rmtname */
393		(void) mchFind(Rmtname);
394		myName(Myname);
395		if (*sys2 == NULLCHAR)
396		    sys2p = Myname;
397	    }
398
399	    DEBUG(4, "sys1 - %s, ", sys1);
400	    DEBUG(4, "file1 - %s, ", file1);
401	    DEBUG(4, "Rmtname - %s\n", Rmtname);
402	    if (copy(sys1, file1, sys2p, file2))
403	    	errors++;
404	}
405
406	/* move the work files to their proper places */
407	commitall();
408
409	/*
410	 * Wait for all background uux processes to finish so
411	 * that our caller will know that we're done with all
412	 * input files and it's safe to remove them.
413	 */
414	while (wait(NULL) != -1)
415		;
416
417	/*
418	 * do not spawn daemon if -r option specified
419	 */
420	if (*Ropt != '-') {
421#ifndef	V7
422		long	limit;
423		char	msg[100];
424		limit = ulimit(1, (long) 0);
425		if (limit < MINULIMIT)  {
426			(void) sprintf(msg,
427			    "ULIMIT (%ld) < MINULIMIT (%ld)", limit, MINULIMIT);
428			logent(msg, "Low-ULIMIT");
429		}
430		else
431#endif
432			xuucico(Rmtname);
433	}
434	if (jflag) {
435		(void) strncpy(Jobid, jid(), NAMESIZE);
436		printf("%s\n", Jobid);
437	}
438	cleanup(errors);
439	/*NOTREACHED*/
440	return (0);
441}
442
443/*
444 * cleanup lock files before exiting
445 */
446void
447cleanup(code)
448int	code;
449{
450	static int first = 1;
451
452	if (first) {
453		first = 0;
454		rmlock(CNULL);
455		if (code != 0)
456			wfabort();  /* this may be extreme -- abort all work */
457	}
458	if (code < 0) {
459	       (void) fprintf(stderr,
460		   gettext("uucp failed completely (%d)\n"), (-code));
461		exit(-code);
462	}
463	else if (code > 0) {
464		(void) fprintf(stderr, gettext(
465		    "uucp failed partially: %d file(s) sent; %d error(s)\n"),
466		 _Transfer, code);
467		exit(code);
468	}
469	exit(code);
470}
471
472static FILE *syscfile();
473/*
474 * generate copy files for s1!f1 -> s2!f2
475 *	Note: only one remote machine, other situations
476 *	have been taken care of in main.
477 * return:
478 *	0	-> success
479 * Non-zero     -> failure
480 */
481int
482copy(s1, f1, s2, f2)
483char *s1, *f1, *s2, *f2;
484{
485	FILE *cfp;
486	struct stat stbuf, stbuf1;
487	int type, statret;
488	char dfile[NAMESIZE];
489	char cfile[NAMESIZE];
490	char command[10+(2*MAXFULLNAME)];
491	char file1[MAXFULLNAME], file2[MAXFULLNAME];
492	char msg[BUFSIZ];
493
494	type = 0;
495	(void) strcpy(file1, f1);
496	(void) strcpy(file2, f2);
497	if (!EQUALS(s1, Myname))
498		type = 1;
499	if (!EQUALS(s2, Myname))
500		type = 2;
501
502	DEBUG(4, "copy: file1=<%s> ", file1);
503	DEBUG(4, "file2=<%s>\n", file2);
504	switch (type) {
505	case 0:
506
507		/*
508		 * all work here
509		 */
510		DEBUG(4, "all work here %d\n", type);
511
512		/*
513		 * check access control permissions
514		 */
515		if (ckexpf(file1))
516			 return(-6);
517		if (ckexpf(file2))
518			 return(-7);
519
520		setuid(Uid);
521		if (chkperm(file1, file2, strchr(Optns, 'd')) &&
522		    (access(file2, W_OK) == -1)) {
523			(void) fprintf(stderr, gettext("permission denied\n"));
524			cleanup(1);
525		}
526
527		/*
528		 * copy file locally
529		 *
530		 * Changed from uidxcp() to fic file made and owner
531		 * being modified for existing files, and local file
532		 * name expansion.
533		 */
534		DEBUG(2, "local copy: %s -> ", file1);
535		DEBUG(2, "%s\n", file2);
536
537		sprintf(command, "cp %s %s", file1, file2);
538		if ((cfp = popen(command, "r")) == NULL) {
539			perror("popen");
540			DEBUG(5, "popen failed - errno %d\n", errno);
541			setuid(Euid);
542			return (FAIL);
543		}
544		if (pclose(cfp) != 0) {
545			DEBUG(5, "Copy failed - errno %d\n", errno);
546			return (FAIL);
547		}
548		setuid(Euid);
549
550		/*
551		 * if user specified -m, notify "local" user
552		 */
553		 if ( Mail ) {
554		 	sprintf(msg,
555		 	"REQUEST: %s!%s --> %s!%s (%s)\n(SYSTEM %s) copy succeeded\n",
556		 	s1, file1, s2, file2, User, s2 );
557		 	mailst(User, "copy succeeded", msg, "", "");
558		}
559		/*
560		 * if user specified -n, notify "remote" user
561		 */
562		if ( Notify ) {
563			sprintf(msg, "%s from %s!%s arrived\n",
564				file2, s1, User );
565			mailst(Nuser, msg, msg, "", "");
566		}
567		return(0);
568	case 1:
569
570		/*
571		 * receive file
572		 */
573		DEBUG(4, "receive file - %d\n", type);
574
575		/*
576		 * expand source and destination file names
577		 * and check access permissions
578		 */
579		if (file1[0] != '~')
580			if (ckexpf(file1))
581				 return(6);
582		if (ckexpf(file2))
583			 return(7);
584
585
586		gename(DATAPRE, s2, Grade, dfile);
587
588		/*
589		 * insert JCL card in file
590		 */
591		cfp = syscfile(cfile, s1);
592		(void) fprintf(cfp,
593	       	"R %s %s %s %s %s %o %s %s\n", file1, file2,
594			User, Optns,
595			*Sfile ? Sfile : "dummy",
596			0777, Nuser, dfile);
597		(void) fclose(cfp);
598		(void) sprintf(msg, "%s!%s --> %s!%s", Rmtname, file1,
599		    Myname, file2);
600		logent(msg, "QUEUED");
601		break;
602	case 2:
603
604		/*
605		 * send file
606		 */
607		if (ckexpf(file1))
608			 return(6);
609		/* XQTDIR hook enables 3rd party uux requests (cough) */
610		DEBUG(4, "Workdir = <%s>\n", Wrkdir);
611		if (file2[0] != '~' && !EQUALS(Wrkdir, XQTDIR))
612			if (ckexpf(file2))
613				 return(7);
614		DEBUG(4, "send file - %d\n", type);
615
616		if (uidstat(file1, &stbuf) != 0) {
617			(void) fprintf(stderr,
618			    gettext("can't get status for file %s\n"), file1);
619			return(8);
620		}
621		if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
622			(void) fprintf(stderr,
623			    gettext("directory name illegal - %s\n"), file1);
624			return(9);
625		}
626		/* see if I can read this file as read uid, gid */
627		if (access(file1, R_OK) != 0) {
628			(void) fprintf(stderr,
629			    gettext("uucp can't read (%s) mode (%o)\n"),
630			    file1, stbuf.st_mode&0777);
631			return(3);
632		}
633
634		/*
635		 * make a copy of file in spool directory
636		 */
637
638		gename(DATAPRE, s2, Grade, dfile);
639
640		if (Copy || !READANY(file1) ) {
641
642			if (uidxcp(file1, dfile))
643			    return(5);
644
645			(void) chmod(dfile, DFILEMODE);
646		}
647
648		cfp = syscfile(cfile, s2);
649		(void) fprintf(cfp, "S %s %s %s %s %s %lo %s %s\n",
650		    file1, file2, User, Optns, dfile,
651		    (long) stbuf.st_mode & LEGALMODE, Nuser, Sfile);
652		(void) fclose(cfp);
653		(void) sprintf(msg, "%s!%s --> %s!%s", Myname, file1,
654		    Rmtname, file2);
655		logent(msg, "QUEUED");
656		break;
657	}
658	_Transfer++;
659	return(0);
660}
661
662
663/*
664 *	syscfile(file, sys)
665 *	char	*file, *sys;
666 *
667 *	get the cfile for system sys (creat if need be)
668 *	return stream pointer
669 *
670 *	returns
671 *		stream pointer to open cfile
672 *
673 */
674
675static FILE	*
676syscfile(file, sys)
677char	*file, *sys;
678{
679	FILE	*cfp;
680
681	if (gtcfile(file, sys) == FAIL) {
682		gename(CMDPRE, sys, Grade, file);
683		ASSERT(access(file, 0) != 0, Fl_EXISTS, file, errno);
684		cfp = fdopen(creat(file, CFILEMODE), "w");
685		svcfile(file, sys, Sgrade);
686	} else
687		cfp = fopen(file, "a");
688	ASSERT(cfp != NULL, Ct_OPEN, file, errno);
689	return(cfp);
690}
691
692
693/*
694 * generate and execute a uux command
695 */
696
697void
698ruux(rmt, sys1, file1, sys2, fwd2, file2)
699char *rmt, *sys1, *file1, *sys2, *fwd2, *file2;
700{
701    char cmd[BUFSIZ];
702    char xcmd[BUFSIZ];
703    char * xarg[6];
704    int narg = 0;
705    int i;
706
707    /* get real Myname - it depends on who I'm calling--rmt */
708    (void) mchFind(rmt);
709    myName(Myname);
710
711    xarg[narg++] = UUX;
712    xarg[narg++] = "-C";
713    if (*Xopts != NULLCHAR)
714	xarg[narg++] = Xopts;
715    if (*Ropt  != ' ')
716	xarg[narg++] = Ropt;
717
718    (void) sprintf(cmd, "%s!uucp -C", rmt);
719
720    if (*Uopts != NULLCHAR)
721	(void) sprintf(cmd+strlen(cmd), " (%s) ", Uopts);
722
723    if (*sys1 == NULLCHAR || EQUALS(sys1, Myname)) {
724        if (ckexpf(file1))
725  	    exit(6);
726	(void) sprintf(cmd+strlen(cmd), " %s!%s ", sys1, file1);
727    }
728    else
729	if (!EQUALS(rmt, sys1))
730	    (void) sprintf(cmd+strlen(cmd), " (%s!%s) ", sys1, file1);
731	else
732	    (void) sprintf(cmd+strlen(cmd), " (%s) ", file1);
733
734    if (*fwd2 != NULLCHAR) {
735	if (*sys2 != NULLCHAR)
736	    (void) sprintf(cmd+strlen(cmd),
737		" (%s!%s!%s) ", sys2, fwd2, file2);
738	else
739	    (void) sprintf(cmd+strlen(cmd), " (%s!%s) ", fwd2, file2);
740    }
741    else {
742	if (*sys2 == NULLCHAR || EQUALS(sys2, Myname))
743	    if (ckexpf(file2))
744		exit(7);
745	(void) sprintf(cmd+strlen(cmd), " (%s!%s) ", sys2, file2);
746    }
747
748    xarg[narg++] = cmd;
749    xarg[narg] = (char *) 0;
750
751    xcmd[0] = NULLCHAR;
752    for (i=0; i < narg; i++) {
753	strcat(xcmd, xarg[i]);
754	strcat(xcmd, " ");
755    }
756    DEBUG(2, "cmd: %s\n", xcmd);
757    logent(xcmd, "QUEUED");
758
759    if (fork() == 0) {
760	ASSERT(setuid(getuid()) == 0, "setuid", "failed", 99);
761	execv(UUX, xarg);
762	exit(0);
763    }
764    return;
765}
766
767void
768usage()
769{
770
771	(void) fprintf(stderr, gettext(
772	"Usage:  %s [-c|-C] [-d|-f] [-g GRADE] [-jm] [-n USER]\\\n"
773	"[-r] [-s FILE] [-x DEBUG_LEVEL] source-files destination-file\n"),
774	Progname);
775	cleanup(-2);
776}
777