#ifndef lint
static char RCSid[] = "$Header: QD:ups/ups2/RCS/ups.c,v 1.8 1986/12/11 15:58:18 scooter Exp alph $";
#endif

/*
 * ups - user interface to the package delivery system
 *
 * usage: ups user@host file1 file2 ...
 *
 * $Author: scooter $
 * $Revision: 1.8 $
 * $Date: 1986/12/11 15:58:18 $
 *
 * $Log: ups.c,v $
 * Revision 1.8  86/12/11  15:58:18  scooter
 * Added alias expansion code which allows ups to follow /usr/lib/aliases.
 * 
 * Revision 1.7  86/09/19  18:53:15  scooter
 * Added -i option for mail specification
 * 
 * Revision 1.6  86/09/18  15:19:45  scooter
 * More fixes to the '.' problam
 * 
 * Revision 1.5  86/09/17  09:43:38  scooter
 * Added code to do automatic renaming of "." files for delivery to
 * avoid the "cannot delete" problem
 * 
 * Revision 1.4  85/08/21  22:27:45  scooter
 * Release revision: added more complete RCS headers.
 * 
 */

#ifdef amigados
#include "sys.h"
#endif

#include <stdio.h>
#include <pwd.h>
#include <ndbm.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <netdb.h>
#include <ctype.h>

#ifdef amigados
#include "ups_protos.h"
#endif

#define	LS "/bin/ls"

struct	passwd *getpwuid();
FILE 	*fopen();
int	rem;
int	qflg = 0;
int	iflg = 0;
int	on   = 1;
char	buffer[BUFSIZ*5];
char	mbuffer[BUFSIZ*4];

struct	user_list {
	char	*u_user;
	char	*u_host;
	struct	user_list *u_next;
};

#ifdef KR
main(argc, argv)
char **argv;
int argc;
#else
int main(int argc, char **argv)
#endif
{
    char	myhost[BUFSIZ];
    int	file;
    int	ret;
#ifdef amigados
    char	*user,*host,*tmp;
#else
    char	*user,*host,*tmp,*rindex(),*index();
#endif
    struct	passwd *mypwent;
#ifdef amigados
    struct	user_list *u_top, *alias_expand();
    struct      user_list dummy_user;
#else
    struct	user_list *u_list,*u_top, *alias_expand();
#endif
#ifdef amigados
    if(1)
    {
	char *env;
	    
	env = getenv("HOSTNAME");
	if(!env)
	{
	    fprintf(stderr, "HOSTNAME not set\n");
	    exit(1);
	}
	strncpy(myhost, env, BUFSIZ);
	env = getenv("USERNAME");
	if(!env)
	{
	    fprintf(stderr, "USERNAME not set\n");
	    exit(1);
	}
	mypwent = malloc(sizeof(struct passwd));
	memset(mypwent, 0, sizeof(struct passwd));
	mypwent->pw_passwd = strdup("");
	mypwent->pw_shell = strdup("");
	mypwent->pw_name = strdup(env);
	mypwent->pw_gecos = strdup("Amiga OS Power User");
    }
#else
    gethostname(myhost,BUFSIZ);
    if( (mypwent = getpwuid(getuid())) == NULL )
    {
	fprintf(stderr,"ups: who are you?\n");
	exit(1);
    }
#endif
    argv++;
    argc--;
    while (argc && *argv[0] == '-')
    {
	char 	c;

	switch (c = argv[0][1])
	{

    case 'q':
	    qflg++;
	    break;

    case 'i':
	    iflg++;
	    break;

    default:
	    fprintf(stderr,"ups: unknown option %c\n",c);

	}
	argv++;
	argc--;
    }

    if (argc <= 1 || qflg)
	upsread(mypwent);

    if (argc < 2) 
    {
	fprintf(stderr,"usage: ups user@host file1 file2 ...\n");
	exit(0);
    }

    /*
     * Get the name of the destination user and host
     */

    user = argv[0];
    host = rindex(user,'@');
    fprintf(stderr, "user = %s\n", user);
#ifndef	ALIASES
    if (host == NULL)
	host = myhost;
    else
	*host++ = '\0';

    u_top = &dummy_user;
    dummy_user.u_next = 0;
    dummy_user.u_user = user;
    dummy_user.u_host = host;
#else
    if (host != NULL)
	*host++ = '\0';

    u_top = alias_expand(user,host,myhost);
#endif				/* ALIASES */

#ifdef DEBUG
    printf("Here we are again\n");
#endif

    if (iflg)
	upsgetmsg(mbuffer);
    else
	mbuffer[0] = '\0';

    while(u_top != NULL)
    {

#ifdef	DEBUG
	printf("Connecting to %s for user %s\n",u_top->u_host,u_top->u_user);
#endif
	rem = upsconnect(u_top->u_host); /* Connect to the server */

	/*
	 * Send the to name, from name, and our name
	 */
		
	tmp = index(mypwent->pw_gecos,',');
	if (tmp)
	    *tmp = '\0';

	sprintf(buffer, "%s\n%s\n%s\n%s", u_top->u_user,
		mypwent->pw_name, mypwent->pw_gecos,mbuffer);

#ifdef	DEBUG
	printf("Sending: %s\n",buffer);
	fflush(stdout);
#endif				/* DEBUG */

	write(rem, buffer, strlen(buffer)+1);
	ret = read(rem, buffer, BUFSIZ);
	if (buffer[0])
	    problem(1);

	for (file = 1 ; file < argc ; file++)
	{

#ifdef	DEBUG
	printf("Sending file: %s\n",argv[file]);
#endif	/* DEBUG */

	sendfile(argv[file]);
    }

    sprintf(buffer, "-Done-");
    write(rem, buffer, strlen(buffer)+1);
    read(rem, buffer, BUFSIZ);	/* get result */
    if (buffer[0])		/* problem? */
	problem(1);		/* yes, go handle it */

    close(rem);
	u_top = u_top->u_next;
    }

}




upsconnect(host)
char *host;
{
	struct hostent *hp;
	struct servent *sp;
	struct	sockaddr_in sin;
	int s;

	hp = gethostbyname(host);
	if (hp == NULL) {
		static struct hostent def;
		static struct in_addr defaddr;
		static char namebuf[128];
#ifndef amigados
		int inet_addr();
#endif

		defaddr.s_addr = inet_addr(host);
		if (defaddr.s_addr == -1) {
			printf("unknown host: %s\n", host);
			exit(1);
		}
		strcpy(namebuf, host);
		def.h_name = namebuf;
		def.h_addr = (char *)&defaddr;
		def.h_length = sizeof (struct in_addr);
		def.h_addrtype = AF_INET;
		def.h_aliases = 0;
		hp = &def;
	}
	sp = getservbyname("ups", "tcp");
	if (sp == 0) {
		fprintf(stderr,"tcp/ups: unknown service\n");
		exit(1);
	}
	sin.sin_family = hp->h_addrtype;
	bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
	sin.sin_port = sp->s_port;
	s = socket(hp->h_addrtype, SOCK_STREAM, 0);
	if (s < 0) {
		fflush(stderr);
		perror("ups (socket)");
		exit(1);
	}
	setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,&on,sizeof(on));
#ifdef	DEBUG
	setsockopt(s,SOL_SOCKET,SO_DEBUG,&on,sizeof(on));
#endif	/* DEBUG */
	if (connect(s, (char *)&sin, sizeof (sin)) < 0) {
		fflush(stderr);
		perror("ups (connect)");
		close(s);
		exit(1);
	}
	return(s);
}




sendfile(file)
char *file;
{
    struct	stat fstatus;
    int	loc,n;
    char	*tmp,*fname;

    if ( (loc = open(file,O_RDONLY)) <= 0 ) {
	perror("ups (open)");
	return(1);
    }
    if (fstat(loc, &fstatus)) {
	perror("ups (fstat)");
	return(1);
    }

    if ((fstatus.st_mode&S_IFMT) != S_IFREG) {
	switch (fstatus.st_mode&S_IFMT)
	{
    case S_IFDIR:
	    tmp = "directory";
	    break;
#ifdef S_IFCHR
    case S_IFCHR:
	    tmp = "character device";
	    break;
    case S_IFBLK:
	    tmp = "block device";
	    break;
#endif
#ifdef S_IFLNK
    case S_IFLNK:
	    tmp = "symbolic link";
	    break;
#endif
#ifdef S_IFSOCK
    case S_IFSOCK:
	    tmp = "socket";
	    break;
#endif
	}

	fprintf(stderr,
		"ups: %s is a %s, only regular files may be sent\n",
		file,tmp);
	fflush(stderr);
	return(1);
    }

    /*
     * Send the file name
     */

    /*
     * First strip off the directory path
     */
    	sprintf(buffer,"%s",file);
#ifndef amigados
    tmp = rindex(buffer,'/');
    if (tmp)
	*tmp++ = '\0';
    else
	tmp = buffer;
#else
    tmp = rindex(buffer,'/');
    if (tmp)
	*tmp++ = '\0';
    else
    {
	tmp = rindex(buffer,':');
	if (tmp)
	    *tmp++ = '\0';
	else
	    tmp = buffer;
    }
#endif
    fname = tmp;

    if (*tmp == '.')
    {
	while ( (*tmp == '.') && (*tmp != '\0') )tmp++;

	fprintf(stdout,
		"WARNING: file %s has been renamed to %s for delivery\n",
		fname,tmp);
    }

    write(rem, tmp, strlen(tmp)+1);
    read(rem, buffer, BUFSIZ);
    if (buffer[0])		/* problem */
    {
	problem(0);
	return(1);
    }

    /*
     * Send the file size in bytes
     */

    sprintf(buffer, "%d",fstatus.st_size);
    write(rem, buffer, strlen(buffer)+1);
    read(rem, buffer, BUFSIZ);
    if (buffer[0])		/* problem */
    {
	problem(0);
	return(1);
    }

    /*
     * Send the file
     */
    	while (n = read(loc, buffer, BUFSIZ))
	    write(rem, buffer, n);
    close(loc);
    read(rem, buffer, BUFSIZ);	/* get result */
    if (buffer[0])		/* problem */
    {
	problem(0);
	return(1);
    }
#ifndef amigados
    sprintf(buffer, "%u", fstatus.st_mode);
#else /* use unix modes for transmission*/
    {
	int unix_mode=0, ami_mode;
	
	ami_mode = fstatus.st_mode;
	
	if(ami_mode & 8)
	    unix_mode |= 0400;
	if(ami_mode & 5)
	    unix_mode |= 0200;
	if(ami_mode & 2)
	    unix_mode |= 0100;

	sprintf(buffer, "%u", unix_mode);
    }
#endif
    write(rem, buffer, strlen(buffer)+1);
    read(rem, buffer, BUFSIZ);	/* get result */
    if (buffer[0])		/* problem */
    {
	problem(0);
	return(1);
    }
    return(0);
}



upsread(mypwent)
struct	passwd *mypwent;
{
#ifndef amigados
	union	wait status;
	int	pid;
	char    c;
#endif
	char	line[BUFSIZ],*tmp;
#ifdef amigados
	strmfp(buffer,UPSDIR,mypwent->pw_name);
#else
	sprintf(buffer,"%s/%s",UPSDIR,mypwent->pw_name);
#endif
	if (qflg)
	{
		if (!access(buffer,F_OK)) {
			fprintf(stderr,
			    "You have ups files awaiting you (type ups to accept delivery)\n");
		}
		exit(0);
	}
	if (access(buffer,F_OK)) {
		fprintf(stderr,"Nothing waiting in ups\n");
		exit(0);
	}
	fprintf(stdout,"\nYou have the following files awaiting delivery:\n\n");
#ifdef amigados
        {
	    char lbuf[BUFSIZ];
	    
	    sprintf(lbuf, "list %s", buffer);
	    system(lbuf);
        }
#else
	if (pid = vfork())
	{
		wait(&status);
	} else {
		execl(LS,"ls","-C",buffer,(char *)0);
		perror("ups (exec)");
		return(0);
	}
#endif
	fprintf(stdout,"\n");

	fprintf(stdout,
	    "Do you wish to accept delivery in your\n");
#ifdef amigados
	fprintf(stdout,
	    "current directory (%s)? ",
	    getcwd(line, BUFSIZ));
#else
	fprintf(stdout,
	    "current directory (%s)? ",
	    getwd(line));
#endif

getinp:
	if (fgets(line,BUFSIZ,stdin) == NULL) {
		fprintf(stdout,"\n");
		exit(0);
	}

	tmp = &line[0];
	while (isspace(*tmp)) tmp++;

	switch (*tmp) {
	case 'y':
	case 'Y':
#ifndef amigados
		sprintf(line,"mv -i %s/* . ; rmdir %s",buffer,buffer);
		system(line);
#else
		sprintf(line,"copy %s/#? \"\"",buffer);
		if(!system(line))
		{
		    sprintf(line,"c:delete %s ALL FORCE",buffer);
		    system(line);
		}
#endif
		exit(0);

	case 'n':
	case 'N':
		exit(0);

	default:
		fprintf(stdout,"Please answer 'yes' or 'no': ");
		goto getinp;
	}
}



/*
 * problem() is called when the install demon process return a non-zero reply.
 * This usually means something recognizable went wrong and we should expect
 * a reason to follow. Read in the reason, output it on the terminal and die.
 */

problem(die)
int die;
{
	char buf[BUFSIZ];	/* place to read into */

	if (read(rem, buf, BUFSIZ) > 0)		/* if we have something */
		fprintf(stderr, "ups: %s", buf);
	if (die) {
		close(rem);		/* close network channel */
		exit(1);
	}
}

int
upsgetmsg(mailbuffer)
char *mailbuffer;
{
	int done = 0;

	fprintf(stdout,
	    "Enter your message followed by '.<RETURN>' or a <CTRL>D:\n");

	while (!done)
	{
		fprintf(stdout,"> ");
		if (gets(mailbuffer) == NULL)
		{
			fprintf(stdout,"\n");
			done++;
		} else if ( (*mailbuffer == '.') && (*(mailbuffer+1) == '\0'))
		{
			done++;
			*mailbuffer = '\0';
		} else {
			while(*mailbuffer != '\0')
				mailbuffer++;
			*mailbuffer++ = '\n';
			*mailbuffer = '\0';
		}
	}
	fprintf(stdout,"[EOT]\n");
}



#ifdef ALIASES
struct user_list *
alias_expand(user,host,myhost)
char *user,*host,*myhost;
{
#ifndef amigados
	char	*malloc();
#endif
	DBM	*dp;
	datum	key,content;
	struct	user_list *list,*top,*alloc_list();
	char	*cp,*tp,*hp;

	list = top = (struct user_list *)NULL;

	if (host != NULL)
		return(alloc_list(user,host));

	dp = dbm_open("/usr/lib/aliases", O_RDONLY, 0644);

	if (dp == NULL) {
		if(host == NULL)
			return(alloc_list(user, myhost));
		else
			return(alloc_list(user, host));
	}

	key.dptr = user;
	key.dsize = strlen(user) + 1;

	content = dbm_fetch(dp,key);
	if (content.dptr == NULL)
		return(alloc_list(user,myhost));
	cp = content.dptr;

	while (cp != NULL)
	{
		tp = cp;
		cp = index(cp,',');
		if (cp != NULL)
			*cp++ = '\0';
		if (index(tp,'!')) {
			fprintf(stderr,
			    "ups: WARNING: cannot alias %s to %s (no ups over uucp)\n",
			    user,hp);
			continue;
		} else if(index(tp,'|')) {
			fprintf(stderr,
			    "ups: WARNING: cannot alias %s to %s (no shells allowed)\n",
			    user,hp);
			continue;
		}
		hp = tp;
		hp = index(tp,'@');
		if (hp == NULL)
			hp = myhost;
		else
			*hp++ = '\0';
		if (top == NULL)
			top = list = alloc_list(tp,hp);
		else
		{
			list->u_next = alloc_list(tp,hp);
			list = list->u_next;
		}
	}
	return(top);
}
#endif

struct	user_list *alloc_list(user,host)
char *user,*host;
{
#ifndef amigados
	char *malloc();
#endif
	struct	user_list *list;

	list = (struct user_list *)malloc(sizeof(struct user_list));
	list->u_user = malloc(strlen(user) + 1);
	list->u_host = malloc(strlen(host) + 1);
	strcpy(list->u_user,user);
	strcpy(list->u_host,host);
	list->u_next = (struct user_list *)NULL;
	return(list);
}
