/* This file is part of the zmail system, zsend.c.  It contains all
 * the code necessary for the sending part of zmail, including the
 * Zephyr Notification Service interface.
 *
 * Written by Barry Jaspan (bjaspan@athena.mit.edu), MIT Project Athena
 * and MIT Student Information Processing Board
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/signal.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <zephyr/zephyr.h>

#define DEFAULT_OUT	"zmail.out"
#define DEFAULT_INST	"ZSEND"
#define STDIN_FILE	"/tmp/zsend.stdin.tmp"

int sock, client_sock, port, silent;
char *whoami;

void create_socket(), send_zmail_msg(), wait_for_response(),
     send_to_socket(), usage(), copy_stdin();
int  exit_cleanly();

main(argc, argv)
   int argc;
   char **argv;
{
     FILE	*file;
     int	retval, arg, pid, seconds_to_wait, send_zephyr, max, count;
     char	*recip, *outfile, *inst, *inpfile;

     signal(SIGINT, exit_cleanly);
     signal(SIGTERM, exit_cleanly);
     signal(SIGHUP, exit_cleanly);
     
     whoami = argv[0];
     if((retval=ZInitialize())!=ZERR_NONE) {
          com_err(whoami,retval,"while initializing Zephyr.");
          exit_cleanly(); }
     if((retval=ZOpenPort((int *) 0))!=ZERR_NONE) {
          com_err(whoami,retval,"while opening Zephyr port.");
          exit_cleanly(); }

     recip = inpfile = "";
     silent = max = count = 0;
     outfile = DEFAULT_OUT;
     inst = DEFAULT_INST;
     seconds_to_wait = 30;
     send_zephyr = 1;
     arg = 1;
     while (argv[arg]) {
	  if (*argv[arg]=='-') {
	       switch (*(argv[arg]+1)) {
	       case 'f':
		    inpfile = argv[arg+1];
		    arg += 2;
		    break;
	       case 'd':
		    outfile = argv[arg+1];
		    arg += 2;
		    break;
	       case 'm':
		    max = atoi(argv[arg+1]);
		    arg += 2;
		    break;
	       case 'z':
		    send_zephyr = 0;
		    arg++;
		    break;
	       case 't':
		    seconds_to_wait = atoi(argv[arg+1]);
		    arg += 2;
		    break;
	       case 's':
		    silent = 1;
		    arg++;
		    break;
	       case 'i':
		    inst = argv[arg+1];
		    arg += 2;
		    break;
	       }
	  }
	  else recip = argv[arg++];
     }

     if ((argc<2) && (! silent)) usage();
     
     if (! *inpfile) {
	  copy_stdin();
	  inpfile = STDIN_FILE;
     }

     if (outfile[0]=='.') {
	  if (! silent) fprintf(stderr,"%s: Cannot suggest filename of %s\n",
				whoami,outfile);
	  exit_cleanly(); }
    
     if ((file = fopen(inpfile, "r"))==NULL) {
	  if (! silent) fprintf(stderr,"%s: Cannot open file %s\n",whoami,
				argv[2]);
	  exit_cleanly(); }
     
     create_socket();
     if (send_zephyr) send_zmail_msg(inst, recip, outfile);
     do {
	  if (! silent) printf("%s: Waiting for connection...\n",whoami);
	  wait_for_response(seconds_to_wait);
	  if (! silent) printf("%s: Sending file %s\n",whoami,inpfile);
	  pid = fork();
	  if (pid == -1) {
	       if (! silent) fprintf(stderr,"%s: Error while forking",whoami);
	       if (! silent) perror("");
	       exit_cleanly(); }
	  else if (pid==0) send_to_socket(file);
	  else close(client_sock);
     } while ((!max) || (++count < max)); 
}

int exit_cleanly()
{
     close(client_sock);
     close(sock);
     exit(1);
}

void copy_stdin()
{
     char	file[BUFSIZ];
     int	f, length;

     if ((f=open(STDIN_FILE, O_CREAT|O_WRONLY, 0777))<0) {
	  if (! silent) perror("Opening STDIN_FILE");
	  exit_cleanly();
     }
     
     while ((length=fread(file,1,BUFSIZ,stdin))>0)
     if (write(f, file, length)<0) {
	  if (! silent) perror("writing data");
	  exit_cleanly();
     }
     close(f);
}
    

void usage()
{
     printf("Usage: %s [-i instance] [-f infile] [-d outfile] \
[username]\n",whoami);
     exit_cleanly();
}

void send_to_socket(outfile)
   FILE *outfile;
{
     char		file[BUFSIZ];
     int		length;

     fseek(outfile, 0, 0);
     while ((length=fread(file,1,BUFSIZ,outfile))>0)
	  write(client_sock, file, length);
     close(client_sock);
     fclose(outfile);
     _exit(0);
}

void create_socket()
{
     struct sockaddr_in	server;
     int		length;

     if ((sock = socket(AF_INET, SOCK_STREAM, 0))<0) {
	  fprintf(stderr,"%s: Error opening socket.\n",whoami);
	  exit_cleanly(); }

     server.sin_family = AF_INET;
     server.sin_addr.s_addr = INADDR_ANY;
     server.sin_port = 0;
     if (bind(sock, &server, sizeof(server))) {
	  if (! silent) fprintf(stderr,"%s: Error binding socket: ",whoami);
	  if (! silent) perror("");
	  exit_cleanly(); }
     
     length = sizeof(server);
     if (getsockname(sock, &server, &length)) {
	  if (! silent) fprintf(stderr,"%s: Error getting port number: ",
				whoami);
	  if (! silent) perror("");
	  exit_cleanly();
     }
     port = ntohs(server.sin_port);
}

void wait_for_response(tim)
   int	tim;
{
     fd_set		request;
     struct timeval	timeout;
     int		stat;
     
     listen(sock, 5);

     bzero((char *) &timeout, sizeof(struct timeval));
     FD_ZERO(&request);
     FD_SET(sock, &request);
     if (tim) {
	  timeout.tv_sec = tim;
	  if ((stat=select(sock+1, &request, 0, 0, &timeout))==0) {
	            close(sock);
		    unlink(STDIN_FILE);
		    exit(0); }
     }
     else (stat=select(sock+1, &request, 0, 0, NULL));

     if (stat < 0) {
	  if (! silent) fprintf(stderr,"%s: Error selecting socket: ",whoami);
	  if (! silent) perror("");
	  exit_cleanly();
     }

     if ((client_sock=accept(sock, NULL, 0))<0) {
	  if (! silent)
	       fprintf(stderr,"%s: Error accepting remote connection: ",
		       whoami);
	  if (! silent) perror("accepting connection");
	  exit_cleanly(); }
}

void send_zmail_msg(i, r, filename)
   char		*i, *r, *filename;
{
     int	retval, length;
     ZNotice_t	notice, retnotice;
     char	hostname[50], msg[500], defmsg[500];;

     gethostname(hostname, sizeof(hostname));
     sprintf(msg,"%s.MIT.EDU%c%d %c%s%c",hostname,'\0',port,'\0',filename,
	     '\0');
     length = strlen(hostname)+strlen(".MIT.EDU")+strlen(filename)+8;

     sprintf(defmsg,"@center(@bold(ZMAIL))\n$sender is trying to send you a file to you on instance @bold($instance).\n\nTo accept the file, type:\n/mit/bjaspan/bin/zrecv -h %s -p %d -f <filename>\n\n$sender suggests the filename \"%s\"",hostname,port,filename);

     bzero((char *) &notice, sizeof(ZNotice_t));
     notice.z_kind = ACKED;
     notice.z_class = "ZMAIL";
     notice.z_class_inst = i;
     notice.z_recipient = r;
     notice.z_sender = ZGetSender();
     notice.z_message = msg;
     notice.z_message_len = length;
     notice.z_default_format = defmsg;
     
     if ((retval=ZSendNotice(&notice,ZAUTH))!=ZERR_NONE) {
	  if (! silent) com_err(whoami,retval,"while sending message.");
	  exit_cleanly(); }
     
     if ((retval=ZIfNotice(&retnotice, (struct sockaddr_in *) 0,
			   ZCompareUIDPred,(char *)&notice.z_uid))!=ZERR_NONE)
     {
	  ZFreeNotice(&retnotice);
	  if (! silent) com_err(whoami,retval,
				"while waiting for acknowledgement.");
          exit_cleanly(); }
}
