/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:net.c 12.0$ */
/* $ACIS:net.c 12.0$ */
/* $Source: /ibm/acis/usr/src/old/berknet/RCS/net.c,v $ */

#ifndef lint
static char *rcsid = "$Header:net.c 12.0$";
#endif

static char sccsid[] = "@(#)net.c	4.2	(Berkeley)	9/12/82";

/* sccs id variable */
static char *net_sid = "@(#)net.c	1.8";

# include "defs.h"
/* must be setuid root */
/*
	net - -b -c cmd -f -i file -l name -mmach -n -o file -p passwd
		-r file -s file -u uid -w -x -y -z command
		
	-	take from standard input
	-b	never send anything back
	-c cmd	think of this as a "cmd" *
	-f	force prompting of user name and password
	-i file	remote stdin *
	-l name remote login name
	-m Mach	remote machine
	-n	do not write back anything, always mail them back
	-o file	remote stdout & stderr *
	-p pass remote password
	-q 	quiet option, send back only if rcode !=0 or if there is stdout
	-r file	local response file
	-s file	local stdin file *
	
	(super users only, always skip login/passwd check:)
	-u uid	net queue files should be owned by uid (16 bits)
	-w	this is a write/mail response cmd *
	-x	this is being forwarded through us to another machine *
	-y	skip login/password check *
	-z	this is a response file being returned *

	* = not documented in net(NEW)
	
*/
/*
	code	option	reason
	q		normal request
	w	-w	message to be written back
	 	-x	being forwarded through us
	y	-y	simply skips login check (used by netlpr)
	s	-z	normal response
*/
/* global variables */
struct userinfo status;

/* local variables */
static char dfname[]=		DFNAME;

main(argc, argv)
  char **argv; {
	register int i;
	int outerror(),uid;
	char localin[FNS], skey[30];
	char buf[BUFSIZ], suid[10];
	char sin =0, zopt = 0, wopt = 0, yopt = 0, xopt = 0;
	char *s,**sargv;
	long cnt = 0L, maxfile = MAXFILELARGE;
	FILE *file, *temp, *rfile;
	struct utmp *putmp;
	struct stat statbuf;
	struct header hd;

	debugflg = DBV;
	hd.hd_scmdact[0] = hd.hd_srespfile[0] = hd.hd_soutfile[0] = 0;
	hd.hd_sinfile[0] = hd.hd_scmdvirt[0] = hd.hd_sttyname[0] = 0;
	localin[0] = 0;
	suid[0] = 0;
	sargv = argv;

	if(isatty(0)) strcat(hd.hd_sttyname,ttyname(0));
	else if(isatty(2)) strcat(hd.hd_sttyname,ttyname(2));
	remote = 0;
	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
		signal(SIGHUP, outerror);
	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
		signal(SIGQUIT, outerror);
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, outerror);
	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
		signal(SIGTERM, outerror);

	while(argc > 1 && argv[1][0] == '-'){
		argc--; argv++;
		switch(argv[0][1]){
		case 0:   
			sin++; 
			break;
		case 'b': 
			status.nonotify++; 
			break;
		case 'c': 
			harg(hd.hd_scmdvirt); 
			break;
		case 'f': 
			status.force++; 
			break;
		case 'i': 
			harg(hd.hd_sinfile); 
			break;
		case 'l': 
			harg(status.login); 
			break;
		case 'm': 
			harg(buf); 
			remote = lookup(buf);
			if(remote == 0){
				fprintf(stderr,"Unknown machine %s\n",buf);
				exit(EX_NOHOST);
			}
			break;
		case 'n': 
			status.nowrite++; 
			break;
		case 'o': 
			harg(hd.hd_soutfile); 
			break;
		case 'p':
			harg(status.mpasswd);
			if(status.mpasswd[0] == 0)
			  	strcpy(status.mpasswd,"\n\n");
			break;
		case 'q': 
			status.quiet++; 
			break;
		case 'r': 
			harg(buf); 
			addir(hd.hd_srespfile,buf); 
			break;
		case 's': 
			harg(localin); 
			break;
		case 'u': 
			harg(suid); 
			break;
		case 'w': 
			wopt++; 
			break;
		case 'x': 
			xopt++; 
			break;
		case 'y': 
			yopt++; 
			break;
		case 'z': 
			zopt++; 
			break;
		default:
			fprintf(stderr,"Unknown option %s\n",argv[0]);
			break;
		}
		}
	while(argc > 1){
		argc--; argv++;
		strcat(hd.hd_scmdact,argv[0]);
		strcat(hd.hd_scmdact," ");
		}
	sargv[1] = 0;		/* so ps won't show passwd ??? */
	hd.hd_uidfrom = uid = getuid();
	hd.hd_gidfrom = getgid();
	hd.hd_code = 'q';
	if(zopt || wopt || yopt || xopt || suid[0] != 0){
		/* check z or w or y or x option permission */
# ifndef TESTING
		/* check effective user id ?? */
		if (uid != SUPERUSER && uid != NUID) {
			fprintf(stderr, "Error: Not super-user\n");
			fprintf(stderr,"zopt %d wopt %d yopt %d xopt %d suid[0] %s\n", zopt, wopt, yopt, xopt, suid);
			fprintf(stderr,"uid %d\n", uid);
			debugflg = 1;
			printhd(&hd);
			outerror(EX_UNAVAILABLE);
			}
# endif
		hd.hd_code = zopt? 's': 'w';
		hd.hd_code = yopt? 'y': hd.hd_code;
		if (status.mpasswd[0] == 0)	/* no passwd required */
			strcpy(status.mpasswd, "\n");
		debug("zopt %d wopt %d yopt %d xopt %d suid[0] %s\n", zopt, wopt, yopt, xopt, suid);
		debug("uid %d\n", uid);
		if(xopt)
			setuid(SUPERUSER);
	}
#ifdef CRN
	strcpy( status.jobno, MAGICCRN );	/* default (invalid) crn */
#else
	strcpy( status.jobno, "XYZZ");		/* default (invalid) crn */
#endif

	if(hd.hd_code == 'q' && !xopt){
		/* read passwd file, get status.localname & crn */
		passwdent();
	}

	/* sets remote,status.login,status.force,status.mpasswd,
		status.nonotify, status.nowrite */
	/* may read passwd file if getenv(HOME) reads it */
	commandfile();
	if(status.force)status.login[0] = status.mpasswd[0] = 0;

	/* look up login name and passwd in the environment */
	envloginpasswd(remote,status.login,status.mpasswd);


	if(remote == 0)remote = getremote(local);
# ifndef TESTING
	if(remote == local){
		fprintf(stderr,"Request sent to local machine - doesn't make sense\n");
		/* outerror(); */
		}
# endif
	strcat(status.defcmd," ");
	if(strlen(hd.hd_scmdact) == 0)strcpy(hd.hd_scmdact,status.defcmd);
	hd.hd_scmdact[strlen(hd.hd_scmdact)-1] = 0;
	do {
		mktemp(dfname); /* make until unique!! */
	} while(stat(dfname,&statbuf) >= 0);
	/* determine through machine */
	i = gothru(local,remote);
	if(i == 0){
		s = longname(remote);
		if(s != 0)fprintf(stderr,"No path to %s machine.\n",s);
		else fprintf(stderr,"Unknown machine\n");
		outerror(EX_NOHOST);
		}
	dfname[strlen(dfname)-11] = i;		/* set directory */
	dfname[strlen(dfname)-7] = i;		/* set file (unused) */
	/* check to see if data files are directories */
	if(isdirectory(hd.hd_srespfile) || isdirectory(hd.hd_sinfile) || isdirectory(hd.hd_soutfile)){
		fprintf(stderr,"%s is a directory, must be a file\n",
			isdirectory(hd.hd_srespfile)    ? hd.hd_srespfile :
			isdirectory(hd.hd_sinfile)  ? hd.hd_sinfile :
			hd.hd_soutfile);
		outerror(EX_USAGE);
	}
	if(suid[0] != 0)uid = atoi(suid);
	if(hd.hd_srespfile[0]){
		if(strcmp(hd.hd_srespfile,"/dev/tty") == 0){
		fprintf(stderr,"Can't have /dev/tty as response file.\n");
			outerror(EX_USAGE);
			}
		if(stat(hd.hd_srespfile,&statbuf) == -1){
			strcpy(buf,hd.hd_srespfile);
			s = &buf[0];
			s = s + strlen(buf) - 1;
			while(*s != '/' && s > &(buf[0]))s--;
			*s = 0;
			debug("chkdir %s",buf);
			if(strlen(buf) == 0)strcpy(buf,".");
			if(access(buf,2) == -1){
				perror(buf);
				outerror(EX_USAGE);
				}
			if((rfile=fopen(hd.hd_srespfile,"w")) == NULL){
				perror(hd.hd_srespfile);
				outerror(EX_USAGE);
				}
			chmod(hd.hd_srespfile,0600);
			fclose(rfile);
			mchown(hd.hd_srespfile,uid,hd.hd_gidfrom);
			}
		else if(access(hd.hd_srespfile,2) == -1){
			perror(hd.hd_srespfile);
			outerror(EX_USAGE);
			}
		else if(getsize(&statbuf) != 0L){
			fprintf(stderr,"%s must have 0-length or not exist\n",
				hd.hd_srespfile);
			outerror(EX_USAGE);
		}
	}
	/* go ahead and prompt for login name and passwd, if neccessary,
	   as long as the X option has not been specified */
	if(hd.hd_code == 'q' && !xopt)promptlogin(remote);

	/* at this point, we create the dfa... file */
	file = fopen(dfname,"w");
	if(file == NULL){
		perror(dfname);
		outerror(EX_OSERR);
		}
	chmod(dfname,0600);
	mchown(dfname,uid,getgid());
	if(xopt)goto stickit;
	if(status.mpasswd[0] == '\n')
		status.mpasswd[0] = 0;
	if(status.mpasswd[0] == 0 && hd.hd_code == 'q' &&
		strcmp(status.login,"network") != 0){
		fprintf(stderr,"Zero-length password not allowed\n");
		outerror(EX_USAGE);
		}
	if(hd.hd_code == 'q' && (streql(status.login,"root") == 0 ||
		streql(status.login,"ruut") == 0)){
		fprintf(stderr,"Can't login as root through the network\n");
		outerror(EX_USAGE);
		}
	makeuukey(skey,status.login,remote);
	nbsencrypt(status.mpasswd,skey,hd.hd_sencpasswd);
	enmask(status.mpasswd);
	hd.hd_lttytime = 0;
	if(hd.hd_sttyname[0] && status.nowrite == 0){
		putmp = getutmp(hd.hd_sttyname);
		if(putmp != NULL) hd.hd_lttytime = putmp->ut_time;
	}
/*
	debug("p:%s:\n",status.mpasswd);
*/
	/* write the header info onto 'file' */
	hd.hd_mchto = remote;
	hd.hd_mesgid.msg_mch = hd.hd_mchfrom = local;
	hd.hd_vmajor = VMAJOR;
	hd.hd_vminor = VMINOR;
	strcpy(hd.hd_snto,status.login);
	strcpy(hd.hd_snfrom,status.localname);
	strcpy(hd.hd_spasswd,status.mpasswd);
	strcpy(hd.hd_ijobno, status.jobno );
	hd.hd_mesgid.msg_ltime = hd.hd_ltimesent = gettime();
	hd.hd_fquiet = status.quiet;
	hd.hd_fnonotify = status.nonotify;
	hd.hd_mesgid.msg_pid = getpid();
	hd.hd_fcompressed = 0;
	/* handle account pairs, accounts which do not require
	   a passwd if you are logged in on the same one here */
	hd.hd_facctpair = fisacctpair(&hd);

	writehdfd(&hd,file);
	printhd(&hd);
stickit:
	if(sin)
		while((i = fread(buf,1,BUFSIZ,stdin)) > 0){
			if(fwrite(buf,1,i,file) != i){
				perror("net queue file");
				outerror(EX_OSFILE);
				}
			if((cnt += i) > maxfile)goto toobig;
			if(feof(stdin))break;
			}
	else if(localin[0]){
		if(access(localin,4) == -1){
			perror(localin);
			outerror(EX_OSFILE);
			}
		temp = fopen(localin,"r");
		if(temp == NULL){
			perror(localin);
			outerror(EX_OSFILE);
			}
		while((i = fread(buf,1,BUFSIZ,temp)) > 0){
			if((cnt += i) > maxfile)goto toobig;
			if(fwrite(buf,1,i,file) != i){
				perror("net queue file");
				outerror(EX_OSFILE);
				}
			}
		fclose(temp);
		}
	fclose(file);
	chmod(dfname,0400);
	dfname[strlen(dfname)-9] = 'c';
	file = fopen(dfname,"w");
	chmod(dfname,0400);
	fclose(file);
	mchown(dfname,uid,getgid());
	exit(EX_OK);
toobig:
	fprintf(stderr,"No more than %ld bytes can be sent\n",maxfile);
	outerror(EX_USAGE);		/* no return */
	}
/* 
   called if there is an error, makes sure that the files created
   are deleted and the terminal is reset to echo
*/
outerror(ret){
	register int i;
	struct sgttyb stt;
	signal(SIGHUP,SIG_IGN); signal(SIGINT,SIG_IGN);
	signal(SIGQUIT,SIG_IGN); signal(SIGTERM,SIG_IGN);
	unlink(dfname);
	i = strlen(dfname) - 9;
	dfname[i] = (dfname[i] == 'c' ? 'd' : 'c');
	unlink(dfname);
	if(gtty(0,&stt) >= 0){
		stt.sg_flags |= ECHO;
		stty(0,&stt);
		}
	exit(ret);
	}
enmask(s)
  register char *s; {
	while(*s){
		*s &= 0177;		/* strip quote bites */
		*s++ ^= 040;		/* invert upper-lower */
		}
	}
addir(s,t)
  register char *s, *t; {
	if(t[0] == '/')strcpy(s,t);
	else {
		gwd(s);
		strcat(s,t);
		}
	}

/* returns true if phd is an account pair, false otherwise */
fisacctpair(phd)
register struct header *phd; 
{
	return(0);
}



static struct stat x;
static struct direct y;
static int off = -1;


/* these three routines gwd, cat, ckroot and 
   data structures x, y, off, do a pwd to string name */
#ifdef V6
static FILE *file;

gwd(name)
  register char *name; {
	*name = 0;
	for(;;){
		stat(".",&x);
		if((file = fopen("..","r")) == NULL)break;
		do {
			if(fread(&y,1,sizeof y,file) != sizeof y)break;
			} while(y.d_ino != x.st_ino);
		fclose(file);
		if(y.d_ino == ROOTINO){
			ckroot(name);
			break;
			}
		if(cat(name))break;
		chdir("..");
		}
	chdir(name);
	}
ckroot(name)
  char *name; {
	register int i;
	if(stat(y.d_name,&x) < 0)return;
	i = x.st_dev;
	if(chdir("/") < 0)return;
	if((file = fopen("/","r")) == NULL)return;
	do {
		if(fread(&y,1,sizeof y,file) != sizeof y)return;
		if(y.d_ino == 0)continue;
		if(stat(y.d_name,&x) < 0)return;
		} while(x.st_dev!=i || (x.st_mode&S_IFMT)!=S_IFDIR);
	if(strcmp(y.d_name,".") != 0 && strcmp(y.d_name,"..") != 0)
		if(cat(name))return;
	i = strlen(name);
	name[i+1] = 0;
	while(--i >= 0)name[i + 1] = name[i];
	name[0] = '/';
	return;
	}
#else
static DIR *file;
static struct stat xx;

gwd(name)
  register char *name;  {
	int rdev, rino;
	register int i;
	register struct direct *dp;

	*name = 0;
	stat("/", &x);
	rdev = x.st_dev;
	rino = x.st_ino;
	for (;;) {
		stat(".", &x);
		if (x.st_ino == rino && x.st_dev == rdev)
			break;
		if ((file = opendir("..")) == NULL)
			break;
		fstat(file->dd_fd, &xx);
		chdir("..");
		if (x.st_dev == xx.st_dev) {
			if (x.st_ino == xx.st_ino)
				break;
			do
				if ((dp = readdir(file)) == NULL)
					break;
			while (dp->d_ino != x.st_ino);
		}
		else do {
			if ((dp = readdir(file)) == NULL)
				break;
			stat(dp->d_name, &xx);
		} while (xx.st_ino != x.st_ino || xx.st_dev != x.st_dev);
		blkcpy(dp, &y, DIRSIZ(dp));
		closedir(file);
		if (cat(name))
			break;
	}
	i = strlen(name);
	name[i+1] = 0;
	while (--i >= 0) name[i+1] = name[i];
	name[0] = '/';
}
#endif

cat(name)
  register char *name; {		/* return 1 to exit */
	register int i,j;
	i = -1;
	while(y.d_name[++i] != 0);
	if((off+i+2) > 511)return(1);
	for(j = off +1; j >= 0; --j)name[j+i+1] = name[j];
	off = i + off + 1;
	name[i] = '/';
	for(--i; i>= 0; --i)name[i] = y.d_name[i];
	return(0);
	}


/*
	this function takes a file name and tells whether it is a 
	directory or on. Returns 1 if so, 0 otherwise.
	null strings etc. return 0.
*/
isdirectory(fn)
	char *fn;
{
	int i,ret=0;
	if(fn == NULL || *fn == 0)return(0);
	i = strlen(fn);
	if(i == 1){
		if(strcmp(fn,".")       == 0)ret = 1;
		if(strcmp(fn,"/")       == 0)ret = 1;
	}
	else if(i == 2){
		if(strcmp(fn,"..")      == 0)ret = 1;
		if(strcmp(fn,"/.")      == 0)ret = 1;
	}
	else {
		if(strcmp(fn+i-2,"/.")  == 0)ret = 1;
		if(strcmp(fn+i-3,"/..") == 0)ret = 1;
	}
	return(ret);
}

blkcpy(from, to, size)
	register *from, *to;
	register int size;
{
	while (size-- > 0)
		*to++ = *from++;
}
