#ifndef lint
static char RCSid[] = "$Header: upsd.c,v 1.4 86/11/19 15:21:52 scooter Exp $";
#endif

/*
 * upsd - package delivery server
 *
 * upsd is the program that is called by inetd when a ups
 * request is issued.  It will read and write to standard input
 * and standard output.  Here is a description of the ups
 * protocol:
 *
 * 	ups			upsd					type
 *
 *  user_name	------>		login name of package receiver		String
 *  from_name	------>		login name of package sender		String
 *  full_name	------>		full name of package sender		String
 *  message	------>		mail message to send			String
 *		<-----		0 for OK				Byte
 * For each file:
 *  file_name	------>		name of file to be delivered		String
 *  file_size	------>		size of file in bytes			Long
 *		<-----		0 for OK				Byte
 *  file	------>		file size bytes
 *		<-----		0 for OK				Byte
 *  file_mode	------>		file mode				Int
 *		<-----		0 for OK				Byte
 *
 * When its all done:
 *  complete	------>		We're done ('-Done-')			String
 *		<-----		0 for OK				Byte
 *
 * $Author: scooter $
 * $Revision: 1.4 $
 * $Date: 86/11/19 15:21:52 $
 *
 * $Log:	upsd.c,v $
 * Revision 1.4  86/11/19  15:21:52  scooter
 * Changed error severity level from ERR to INFO
 * 
 * Revision 1.3  86/09/19  18:53:37  scooter
 * Added -i option for mail specification.
 * Also added syslog stuff.
 * 
 * Revision 1.2  85/08/21  22:28:11  scooter
 * Release revision: added more complete RCS headers.
 * 
 *
 */

#include	<stdio.h>
#include	<pwd.h>
#include	<grp.h>
#include	<sys/file.h>
#include	<sys/types.h>
#include	<netinet/in.h>
#include	<netdb.h>
#include	<syslog.h>
#include	<string.h>

long	atol();
int	atoi();

char	buffer[BUFSIZ*5];	/* character input buffer */
char	file_name[BUFSIZ];	/* file name */
char	file_path[BUFSIZ];	/* full path to destination */
char	user_name[BUFSIZ];	/* Receiver name */
char	from_name[BUFSIZ];	/* Sender name */
char	full_name[BUFSIZ];	/* Full name of sender */
char	file_list[BUFSIZ];	/* List of all files */
long	file_size;		/* number of bytes in file */
int	file_mode;		/* mode of the file */
char	mail_message[BUFSIZ*4];	/* mail message */
int	mcount;			/* number of characters in mail buffer */

int	fid;			/* File descriptor for destination */
#ifdef DEBUG
int	debugflag = 1;		/* Debug flag */
#else
int	debugflag = 0;		/* Debug flag */
#endif

main(argc, argv)
int argc;
char **argv;
{
	char *ptr,*tptr,*mptr;
	int f, i, file;
	struct sockaddr_in sin;
	struct hostent *peer;
	struct passwd *pwent;

	openlog("upsd",LOG_ODELAY,LOG_DAEMON);

	syslog(LOG_DEBUG, "started - test");

	if(argc>1 && !strcmp(argv[1], "-d") )
		debugflag++;

	if (debugflag)
		syslog(LOG_DEBUG,"started");

	i = sizeof (sin);
	if (getpeername(0, &sin, &i) < 0)
		syslog(LOG_ERR,"getpeername failed: %m");

	if (debugflag)
		syslog(LOG_DEBUG,"Calling gethostbyaddr");

	peer = gethostbyaddr((char *)&sin.sin_addr,
	    sizeof(sin.sin_addr),sin.sin_family);

	buffer[0] = '\0';
	file_list[0] = '\0';

	if (debugflag)
		syslog(LOG_DEBUG,"Reading first buffer");

	read(0, buffer, BUFSIZ*5);  /* fetch receiver and sender names */

	if (debugflag)
		syslog(LOG_DEBUG,"receiver/sender/message: %s",buffer);

	ptr = buffer;
	while (*ptr != '\n')
		ptr++;
	*ptr++ = NULL;
	strcpy(user_name, buffer);	/* save receiver name */
	tptr = ptr;
	while (*tptr != '\n')
		tptr++;
	*tptr++ = NULL;
	strcpy(from_name, ptr);		/* save sender name */
	mptr = tptr;
	while ((*mptr != '\n') && (*mptr != '\0'))
		mptr++;
	*mptr++ = NULL;
	strcpy (full_name, tptr);	/* save sender's full name */
	strcpy (mail_message, mptr);	/* save the mail message */

	/*
	 * Check for a valid user
	 */
	if ((pwent = getpwnam(user_name)))
		ack();
	else
	{
		error("Receiver name","No such person");
		exit(1);
	}

	mcount = strlen (mail_message);
	if (debugflag)
		syslog(LOG_DEBUG,"mail message (%d chars):\n %s", mcount, mail_message);

	for (file = 0 ;;)
	{
		if (read(0, buffer, BUFSIZ) <= 0) {
			error("file name","premature EOF or file read error");
			continue;
		}
		strcpy(file_name,buffer);

		if (debugflag)
			syslog(LOG_DEBUG,"File name:%s",file_name);


		/*
		 * Are we done??
		 */
		if (!strncmp(file_name,"-Done-",6)) break;

		if ( (fid = opendest(user_name,file_name,
		    pwent->pw_uid,pwent->pw_gid)) <= 0 )
		{
			continue;
		}
		ack();

		if (read(0, buffer, BUFSIZ) <= 0) {
			error("file size","premature EOF or file read error");
			continue;
		}

		file_size = atol(buffer); /* get number of bytes */
		ack();

		copyfile(&file);

		if (read(0, buffer, BUFSIZ) <= 0) {
			error("file mode","premature EOF or file read error");
			continue;
		}
		file_mode = atoi(buffer); 	/* get the file mode */
		file_mode &= 0700;		/* Strip the low order modes */
		if (chmod(file_path,file_mode)) {
			error("file mode","chmod failed");
			continue;
		}
		
		ack();
		chown(file_path,pwent->pw_uid,pwent->pw_gid);

	}
	ack();

	if (file)
		sendmail(from_name,full_name,peer->h_name,
		    user_name,file_list,file++,mail_message,mcount);
}

/*
copyfile
copyfile will copy file_size many bytes from stdin to a temporarily created
file. The filename will be passed back via buffer.
*/

copyfile(file)
int *file;
{
	long cnt;				/* file size counter */
	int i;

	cnt = file_size;		/* count down input bytes */
	while (cnt > 0) {
		i = read(0, buffer, BUFSIZ);
		write(fid, buffer, i);
		cnt -= i;
	}
	close(fid);

	ack();
	sprintf(buffer,"%-15s",file_name);
	if (*file%4)
		strcat(file_list," ");
	else
		strcat(file_list,"\n\t");

	(*file)++;
	strcat(file_list,buffer);
}

/*
error(option, string)
char *option, *string;
This routine is called when some error condition has been encountered.
option contains an identifier message, while string contains the actual
error message. Before message is printed, a non-null character is output
first, then the string error message.
*/

error(option, string)
char *option, *string;
{
	char buf[BUFSIZ];


	buf[0] = 1;
	write(1, buf, 1);	/* nak */
	sprintf(buf, "upsd: %s: %s\n", option, string);
	write(1, buf, strlen(buf)+1);

	syslog(LOG_INFO,"error - %s",buf);
}



/*
 * ack()
 * This routine is called to return an OK to the remote host.
 */

ack()
{
	buffer[0] = 0;
	write(1,buffer,1);
}



/*
 * opendest(name,file)
 * char *name,*file;
 *
 * Open the destination file "file" in UPSDIR/user, creating the
 * file if necessary.
 */

int
opendest(name,file,uid,gid)
char *name,*file;
int uid,gid;
{
	int ret;

	sprintf(file_path,"%s/%s",UPSDIR,name); /* Form path to directory */

	if (access(file_path,F_OK) == (-1))
	{
		mkdir(file_path,0700);
		chown(file_path,uid,gid);
	}

	sprintf(file_path,"%s/%s/%s",UPSDIR,name,file);

	if (!access(file_path,F_OK))
	{
		error("file creation","A file by that name has already been sent to that user.");
		return(0);
	} else {
		ret = open(file_path,O_WRONLY|O_CREAT,0600);
		if (ret <= 0)
		{
			sprintf(buffer,"unable to open destination file: %s",
			    file_path);
			error("file open",buffer);
			return(0);
		}
		return(ret);
	}
}



/*
 * sendmail(from,full,from_host,to,list,mess,count)
 * char *from,*full,*to,*from_host,*list,*mess;
 * int	count;
 *
 * This routine sends mail to the destination user to inform
 * them that ups files are awaiting them.
 */

sendmail(from,full,from_host,to,list,file,mess,count)
char *from,*full,*to,*from_host,*list,*mess;
int file,count;
{
	FILE *send,*popen();
	static char myhost[BUFSIZ];

	gethostname(myhost,BUFSIZ);
	sprintf(buffer,"%s -f%s@%s -F\"%s\" -t",SENDMAIL,from,from_host,full);

	if(debugflag)
		syslog(LOG_DEBUG,"sendmail\n%s",buffer);

	send = popen(buffer,"w");

	fprintf(send,"To: %s@%s\n",to,myhost);

	if(debugflag)
		syslog(LOG_DEBUG,"To: %s@%s",to,myhost);

	fprintf(send,"Subject: UPS delivery\n");
	if (file > 1)
		strcpy(buffer,"files");
	else
		strcpy(buffer,"file");

	fprintf(send,"I have sent you the following %s using ups:\n",buffer);
	fprintf(send,"%s\n\n",list);
	if (file > 1)
		strcpy(buffer,"these files");
	else
		strcpy(buffer,"this file");

	fprintf(send,"To retrieve %s, use the ups command.\n\n\n",buffer);
	if (count)
		fprintf(send,"%s\n",mess);
	else
		fprintf(send,"\n\n\n\n---ups\n");
	pclose(send);
}
