[mod.sources] v06i034: Elm mail system

sources-request@mirror.UUCP (06/30/86)

Submitted by: Dave Taylor <pyramid!hplabs!hpldat!taylor>
Mod.sources: Volume 6, Issue 34
Archive-name: elm/Part09

# Continuation of Shell Archive, created by hpldat!taylor

# This is part 9

# To unpack the enclosed files, please use this file as input to the
# Bourne (sh) shell.  This can be most easily done by the command;
#     sh < thisfilename


if [ ! -d src ]
then
  echo creating directory src
  mkdir src
fi

# ---------- file src/output_utils.c ----------

filename="src/output_utils.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/output_utils.c...
fi

cat << 'END-OF-FILE' > $filename
/**			output_utils.c			**/

/** This file contains routines used for output in the ELM program.

    These routines (C) Copyright 1986 Dave Taylor
**/

#include "headers.h"


static char err_buffer[SLEN];		/* store last error message */

static char central_message_buffer[SLEN];


show_last_error()
{
	/** rewrite last error message! **/

	error(err_buffer);
}

clear_error()
{
	MoveCursor(LINES,0);
	CleartoEOLN();
	err_buffer[0] = '\0';
}

set_error(s)
char *s;
{
	strcpy(err_buffer, s);
}

error(s)
char *s;
{
	/** outputs error 's' to screen at line 22, centered! **/

	MoveCursor(LINES,0);
	CleartoEOLN();
	PutLine0(LINES,(COLUMNS-strlen(s))/2,s);
	fflush(stdout);
	strcpy(err_buffer, s);	/* save it too! */
}

error1(s, a)
char *s, *a;
{
	/** same as error, but with a 'printf' argument **/
	char buffer[SLEN];

	sprintf(buffer,s,a);
	error(buffer);
}

error2(s, a1, a2)
char *s, *a1, *a2;
{
	/** same as error, but with two 'printf' arguments **/
	char buffer[SLEN];

	sprintf(buffer,s, a1, a2);
	error(buffer);
}

error3(s, a1, a2, a3)
char *s, *a1, *a2, *a3;
{
	/** same as error, but with three 'printf' arguments **/
	char buffer[SLEN];

	sprintf(buffer,s, a1, a2, a3);
	error(buffer);
}

prompt(s)
char *s;
{
	/** prompt user for input on LINES-3 line, left justified **/

	PutLine0(LINES-3,0,s);
	CleartoEOLN();
}

prompt1(s,a)
char *s, *a;
{
	/** same as prompt, but with a 'printf' argument **/
	char buffer[SLEN];

	sprintf(buffer,s,a);
	prompt(buffer);
}


set_central_message(string, arg)
char *string, *arg;
{
	/** set up the given message to be displayed in the center of
	    the current window **/ 

	sprintf(central_message_buffer, string, arg);
}

display_central_message()
{
	/** display the message if set... **/

	if (central_message_buffer[0] != '\0') {
	  ClearLine(LINES-15);
	  Centerline(LINES-15, central_message_buffer);
	  fflush(stdout);
	}
}

clear_central_message()
{
	/** clear the central message buffer **/

	central_message_buffer[0] = '\0';
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 1987 ]
  then
    echo $filename changed - should be 1987 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/mailtime.c ----------

filename="src/mailtime.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/mailtime.c...
fi

cat << 'END-OF-FILE' > $filename
/**			mailtime.c			**/

/** This set of routines is used to figure out when the user last read
    their mail and to also figure out if a given message is new or not.

    (C) Copyright 1986 Dave Taylor
**/

#include "headers.h"

#include <sys/types.h>
#include <sys/stat.h>
#ifdef BSD
#  include <sys/time.h>
#else
#  include <time.h>
#endif

resolve_received(entry)
struct header_rec *entry;
{
	/** Entry has the data for computing the time and date the 
	    message was received.  Fix it and return **/

	switch (tolower(entry->month[0])) {
	  case 'j' : if (tolower(entry->month[1]) == 'a')
		       entry->received.month = JANUARY;
		     else if (tolower(entry->month[2]) == 'n')
	               entry->received.month = JUNE;
		     else
	               entry->received.month = JULY;
	             break;
	  case 'f' : entry->received.month = FEBRUARY;
	 	     break;
	  case 'm' : if (tolower(entry->month[2]) == 'r')
	               entry->received.month = MARCH;
		     else
		       entry->received.month = MAY;
	             break;
	  case 'a' : if (tolower(entry->month[1]) == 'p')
	               entry->received.month = APRIL;
	             else
	               entry->received.month = AUGUST;
		     break;
	  case 's' : entry->received.month = SEPTEMBER;
		     break;
	  case 'o' : entry->received.month = OCTOBER;
		     break;
	  case 'n' : entry->received.month = NOVEMBER;
	  	     break;
	  case 'd' : entry->received.month = DECEMBER;
		     break;
	}

	sscanf(entry->day, "%d", &(entry->received.day));

	sscanf(entry->year, "%d", &(entry->received.year));
	if (entry->received.year > 100) entry->received.year -= 1900;

	sscanf(entry->time, "%d:%d", &(entry->received.hour),
	       &(entry->received.minute));
}

get_mailtime()
{
	/** Instantiate the values of the last_read_mail stat
	    variable based on the file access time/date of the
	    file mailtime_file.  IF the file doesn't exist,
	    then assume all mail is new. **/

	struct stat buffer;
	struct tm   *timebuf;
	char   filename[SLEN];

	sprintf(filename, "%s/%s", home, mailtime_file);

	if (stat(filename, &buffer) == -1) {
	  last_read_mail.month = 0;
	  last_read_mail.day = 0;
	  last_read_mail.year = 0;
	  last_read_mail.hour = 0;
	  last_read_mail.minute = 0;
	}
	else {	/* stat okay... */
	  timebuf = (struct tm *) localtime(&(buffer.st_mtime));
	  
	  last_read_mail.month = timebuf->tm_mon;
	  last_read_mail.day = timebuf->tm_mday;
	  last_read_mail.year = timebuf->tm_year;
	  last_read_mail.hour = timebuf->tm_hour;
	  last_read_mail.minute = timebuf->tm_min;
	}
}

update_mailtime()
{
	/** This routine updates the last modified time of the 
	    .last_read_mail file in the users home directory.
	    If the file doesn't exist, it creates it!! **/

	char filename[SLEN];

#ifdef BSD
	struct timeval	tval[2];
	struct timezone tzone;
#endif
	
	sprintf(filename, "%s/%s", home, mailtime_file);

#ifdef BSD
	gettimeofday(&tval[0], &tzone);
	gettimeofday(&tval[1], &tzone);

	if (utime(filename, &tval) == -1)
#else
	if (utime(filename, NULL) == -1)
#endif
	  (void) creat(filename, 0777);
}

new_msg(entry)
struct header_rec entry;
{
	/** Return true if the current message is NEW.  This can be
	    easily tested by seeing 1) if we're reading the incoming
	    mailbox and then, if so, 2) if the received_on_machine
	    date is more recent than the last_read_mail date.
	**/

	if (mbox_specified != 0) return(FALSE);		/* not incoming */
	
	/** Two tests - if received is OLDER than last read mail, then
	    immediately return FALSE.  If received is NEWER than last
	    read mail then immediately return TRUE **/

	if (entry.received.year < last_read_mail.year) 
	  return(FALSE);				
	
	if (entry.received.year > last_read_mail.year) 
	  return(TRUE);				
	
	if (entry.received.month < last_read_mail.month)
	  return(FALSE);			

	if (entry.received.month > last_read_mail.month)
	  return(TRUE);			

	if (entry.received.day < last_read_mail.day)
	  return(FALSE);

	if (entry.received.day > last_read_mail.day)
	  return(TRUE);

	if (entry.received.hour < last_read_mail.hour)		
	  return(FALSE);		
	
	if (entry.received.hour > last_read_mail.hour)		
	  return(TRUE);		
	
	if (entry.received.minute < last_read_mail.minute)
	  return(FALSE);	

	return(TRUE);
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 4271 ]
  then
    echo $filename changed - should be 4271 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/encode.c ----------

filename="src/encode.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/encode.c...
fi

cat << 'END-OF-FILE' > $filename
/**		encode.c		**/

/** This is a heavily mangled version of the 'cypher' program written by
    person or persons unknown.  

    (C) Copyright 1986, Dave Taylor
**/

#include <stdio.h>
#include "curses.h"
#include "headers.h"

#define RS	94
#define RN	4
#define RMASK	0x7fff	/* use only 15 bits */

static char r[RS][RN];		/* rotors */
static char ir[RS][RN];		/* inverse rotors */
static char h[RS];		/* half rotor */
static char s[RS];		/* shuffle vector */
static int  p[RN];		/* rotor indices */

static char the_key[SLEN];	/* unencrypted key */
static char *encrypted_key;	/* encrypted key   */

getkey(send)
int send;
{
	/** this routine prompts for and returns an encode/decode
	    key for use in the rest of the program.  If send == 1
	    then need to mess with rawmode. **/

	char buffer[NLEN];
	int gotkey = 0;

	ClearLine(21);

	if (send) Raw(OFF);

	while ( !gotkey ) {
	  MoveCursor(LINES-1,0);
	  ClearLine(LINES-1);
	  if (send)
	    strcpy( buffer, getpass( "Enter encryption key: "));
	  else
	    strcpy( buffer, getpass( "Enter decryption key: "));
	  MoveCursor(LINES-1,0);
	  if ( strcmp( buffer, getpass( "Please enter it again: "))) {
	    error("Your keys were not the same!");
	    sleep(1);
	    clear_error();
	    continue;
	  }
	  strcpy(the_key, buffer);	/* save unencrypted key */
	  makekey( buffer );
	  gotkey = 1;
	}

	if (send) Raw(ON);

	setup();		/** initialize the rotors etc. **/

	ClearLine(LINES-1);		
	clear_error();
}

get_key_no_prompt()
{
	/** This performs the same action as get_key, but assumes that
	    the current value of 'the_key' is acceptable.  This is used
	    when a message is encrypted twice... **/

	char buffer[SLEN];

	strcpy(buffer, the_key);

	makekey( buffer );

	setup();
}

encode(line)
char *line;
{
	/** encrypt or decrypt the specified line.  Uses the previously
	    entered key... **/

	register int i, index, j, ph = 0;

	for (index=0; index < strlen(line); index++) {
	  i = (int) line[index];

	  if ( (i >= ' ') && (i < '~') ) {
	    i -= ' ';

	    for ( j = 0; j < RN; j++ )		/* rotor forwards */
	      i = r[(i+p[j])%RS][j];

	    i = ((h[(i+ph)%RS])-ph+RS)%RS;	/* half rotor */

	    for ( j--  ; j >= 0; j-- )		/* rotor backwards */
	      i = (ir[i][j]+RS-p[j])%RS;

	    j = 0;				/* rotate rotors */
	    p[0]++;
	    while ( p[j] == RS ) {
	      p[j] = 0;
	      j++;
	      if ( j == RN ) break;
	      p[j]++;
            }
  
	    if ( ++ph == RS )
	      ph = 0;

	    i += ' ';
	  }
	  
	  line[index] = (char) i;	/* replace with altered one */
	}
}


makekey( rkey)
char *rkey;
{
	/** encrypt the key using the system routine 'crypt' **/

	char key[8], salt[2], *crypt();

	strncpy( key, rkey, 8);
	salt[0] = key[0];
	salt[1] = key[1];
	encrypted_key = crypt( key, salt);
}

/*
 * shuffle rotors.
 * shuffle each of the rotors indiscriminately.  shuffle the half-rotor
 * using a special obvious and not very tricky algorithm which is not as
 * sophisticated as the one in crypt(1) and Oh God, I'm so depressed.
 * After all this is done build the inverses of the rotors.
 */

setup()
{
	register long i, j, k, temp;
	long seed;

	for ( j = 0; j < RN; j++ ) {
		p[j] = 0;
		for ( i = 0; i < RS; i++ )
			r[i][j] = i;
	}

	seed = 123;
	for ( i = 0; i < 13; i++)		/* now personalize the seed */
	  seed = (seed*encrypted_key[i] + i) & RMASK;

	for ( i = 0; i < RS; i++ )		/* initialize shuffle vector */
	  h[i] = s[i] = i;

	for ( i = 0; i < RS; i++) {		/* shuffle the vector */
	  seed = (5 * seed + encrypted_key[i%13]) & RMASK;;
	  k = ((seed % 65521) & RMASK) % RS;
	  temp = s[k];
	  s[k] = s[i];
	  s[i] = temp;
	}

	for ( i = 0; i < RS; i += 2 ) {	/* scramble the half-rotor */
	  temp = h[s[i]];			/* swap rotor elements ONCE */
	  h[s[i]] = h[s[i+1]];
	  h[s[i+1]] = temp;
	}

	for ( j = 0; j < RN; j++) {			/* select a rotor */

	  for ( i = 0; i < RS; i++) {		/* shuffle the vector */
	    seed = (5 * seed + encrypted_key[i%13]) & RMASK;;
	    k = ((seed % 65521) & RMASK) % RS;
	    temp = r[i][j];
	    r[i][j] = r[k][j];
	    r[k][j] = temp;
	  }

	  for ( i = 0; i < RS; i++) 		/* create inverse rotors */
	    ir[r[i][j]][j] = i;
       }
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 4156 ]
  then
    echo $filename changed - should be 4156 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/leavembox.c ----------

filename="src/leavembox.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/leavembox.c...
fi

cat << 'END-OF-FILE' > $filename
/**			leavembox.c			**/

/** leave current mailbox, updating etc. as needed...
  
    (C) Copyright 1985, Dave Taylor
**/

#include "headers.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

#define  ECHOIT 	1	/* echo on for prompting! */

/** added due to a bug in the 2.1 OS **/

struct utimbuf {
	time_t	actime;		/** access time **/
	time_t  modtime;	/** modification */
       };

extern int errno;

char *error_name(), *error_description();

int
leave_mbox(quitting)
int quitting;
{
	/** Exit, saving files into mbox and deleting specified, or simply 
	    delete specified mail... If "quitting" is true, then output status 
            regardless of what happens.  Returns 1 iff mailfile was
	    changed (ie messages deleted from file), 0 if not, and -1 if new
	    mail has arrived in the meantime...
	**/

	FILE *temp;
	char outfile[SLEN], buffer[SLEN];
	struct stat buf;		/* stat command  */
	struct utimbuf times;		/* utime command */
	register int to_delete = 0, to_save = 0, i, mode = 00644,
		     pending = 0, number_saved = 0, last_sortby;
	char dflt;
	long bytes();

	dprint0(1,"\n\n-- leaving_mailbox --\n\n");

	if (message_count == 0) 
	  return(FALSE);	/* nothing changed */

	for (i = 0; i < message_count; i++)
	  if (ison(header_table[i].status, DELETED)) to_delete++;
	  else                                       to_save++;

	dprint2(2,"Count: %d to delete and %d to save\n", to_delete, to_save);

	if (mbox_specified == 0) 
	  update_mailtime();

	if (hp_softkeys) {
	  define_softkeys(YESNO);		/* YES or NO on softkeys */
	  softkeys_on();
	}

	if (always_del) 	/* set up the default answer... */
	  dflt = 'y';
	else 
	  dflt = 'n';

	if (to_delete)
	  if (to_save) {
	    sprintf(buffer, "Delete message%s? (y/n) ", plural(to_delete));
	    if (want_to(buffer, dflt, ECHOIT) != 'y') {
	      if (mbox_specified == 0) unlock();	/* remove lock! */
	      dprint1(3,"\tDelete message%s? - answer was NO\n", 
			plural(to_delete));
	      error("Nothing deleted");
	      return(FALSE);	/* nothing was deleted! */
	    }
	  }
	  else if (! to_save) {		/* nothing to save!! */
	    if (want_to("Delete all mail? (y/n) ", dflt, ECHOIT)!='y') {
	      if (mbox_specified == 0) unlock();	/* remove lock! */
	      dprint0(3,"Delete all mail? - answer was NO\n");
	      error("Nothing deleted");
	      return(FALSE);   /* nothing was deleted */
	    }
	  }

	if (always_leave) 
	  dflt = 'y';
	else
	  dflt = 'n';
	  
	/** we have to check to see what the sorting order was...so that
	    the order of saved messages is the same as the order of the
	    messages originally (a subtle point...) **/

	if (sortby != RECEIVED_DATE) {	/* what we want anyway! */
	  last_sortby = sortby;
	  sortby = RECEIVED_DATE;
	  sort_mailbox(message_count, FALSE);
	  sortby = last_sortby;
	}

	if (to_save && mbox_specified == 0) 
	  if (want_to("Keep mail in incoming mailbox? (y/n) ",dflt, ECHOIT)
	      == 'y') 
	      if (to_delete)	    /* okay - keep undeleted as pending!    */
	        pending++;
	      else {	   	    /* gag! nothing to delete, don't save!  */	
	        unlock();	    /* remove mailfile lock!	   	    */
	        dprint0(3,"Keep mail in incoming mailbox? -- answer was YES\n");
	        error("Mailbox unchanged");
	        return(FALSE);	/* nothing changed! */
	      }

	/** okay...now lets do it! **/

	if (to_save > 0) {
	  if (to_delete > 0)
	    sprintf(buffer ,"[%s %d message%s, and deleting %d]", 
	          pending? "keeping" : "storing", 
		  to_save, plural(to_save), to_delete);
	  else if (quitting)
	    sprintf(buffer,"[%s %s]",
	          pending? "keeping" : "storing",
		  to_save > 1? "all messages" : "message");
	  else
	    buffer[0] = '\0';	/* no string! */
	}
	else {
	  if (to_delete > 0)
	    sprintf(buffer, "[deleting all messages]");
	  else if (quitting)
	    sprintf(buffer, "[no messages to %s, and none to delete]",
	            pending? "keep" : "save");
	  else
	    buffer[0] = '\0';
	}

	dprint1(2,"Action: %s\n", buffer);

	error(buffer);

	if (! mbox_specified) {
	  if (pending) {                /* keep some messages pending! */
	    sprintf(outfile,"%s%d", temp_mbox, getpid());
	    unlink(outfile);
	  }
	  else if (mailbox_defined)	/* save to specified mailbox */
	    strcpy(outfile, mailbox);
	  else				/* save to $home/mbox */
	    sprintf(outfile,"%s/mbox", home);
	}
	else {
	  if (! to_delete) return(FALSE);	/* no work to do! */
          sprintf(outfile, "%s%d", temp_file, getpid());
	  unlink(outfile); /* ensure it's empty! */
	}

	if (to_save) {
	  if ((errno = can_open(outfile, "a"))) {
	    error1(
	         "Permission to append to %s denied!  Leaving mailbox intact\n",
		 outfile);
	    dprint2(1,
		 "Error: Permission to append to outfile %s denied!! (%s)\n",
		 outfile, "leavembox");
	    dprint2(1,"** %s - %s **\n", error_name(errno),
		 error_description(errno));
	    unlock();
	    return(0);
	  }
	  if ((temp = fopen(outfile,"a")) == NULL) {
	    if (mbox_specified == 0)
	      unlock();		/* remove mailfile lock! */
	    dprint1(1,"Error: could not append to file %s\n", 
		    outfile);
	    dprint2(1,"** %s - %s **\n", error_name(errno),
		 error_description(errno));
	    sprintf(buffer, "           Could not append to file %s!          ",
		    outfile);
	    emergency_exit(Centerline(LINES-1, buffer));
	  }
  
	  for (i = 0; i < message_count; i++)
	    if (! (header_table[i].status & DELETED)) {
	      current = i+1;
	      if (! number_saved++) {
	        dprint2(2,"Saving message%s #%d, ", plural(to_save), current);
	      }
	      else {
		dprint1(2,"#%d, ", current);
	      }
	      copy_message("", temp, FALSE, FALSE);
	    }
	  fclose(temp);
	  dprint0(2,"\n\n");
	}

	/* remove source file...either default mailbox or original copy of 
           specified one! */

	if (mbox_specified == 0) {
	  /** let's grab the original mode and date/time of the mailfile 
	      before removing it **/

          if (stat(infile, &buf) == 0)
	    mode = buf.st_mode & 00777;
	  else {
	    dprint2(1,"Error: errno %s attempting to stat file %s\n", 
		     error_name(errno), infile);
            error2("Error %s (%s) on stat(%s)", error_name(errno), 
		error_description(errno), infile);
	  }
	}

	fclose(mailfile);	/* close the baby... */
	
	if (mailfile_size != bytes(infile)) {
	  sort_mailbox(message_count, FALSE);	/* display sorting order! */
	  unlock();
	  error("New mail has just arrived - resyncing...");
	  return(-1);
	}
	unlink(infile); 	/* and BLAMO!        */

	if (to_save && (mbox_specified || pending)) {
	  if (link(outfile, infile) != 0) 
	    if (errno == EXDEV) { /** different file devices!  Use copy! **/
	      if (copy(outfile, infile) != 0) {
	        dprint2(1,"leavembox: copy(%s, %s) failed;",
			outfile, infile);
	        dprint2(1,"** %s - %s **\n", error_name(errno),
		     error_description(errno));
	        error("couldn't modify mail file!");
	        sleep(1);
	        sprintf(infile,"%s/%s", home, unedited_mail);
		if (copy(outfile, infile) != 0) {
	          dprint1(1,"leavembox: couldn't copy to %s either!!  Help;", 
			  infile);
	          dprint2(1,"** %s - %s **\n", error_name(errno),
		          error_description(errno));
	          error("something godawful is happening to me!!!");
		  emergency_exit();
	        }
		else {
	          dprint1(1,"\nWoah! Confused - Saved mail in %s (leavembox)\n", 
			  infile);
	          error1("saved mail in %s", infile);
	        }
	      }	
	    }
	    else {
	      dprint2(1,"link(%s, %s) failed (leavembox)\n", outfile, infile);
	      dprint2(1,"** %s - %s **\n", error_name(errno),
			error_description(errno));
	      error2("link failed! %s - %s", error_name(errno),
		error_description(errno));
	      emergency_exit();
	    }
	  unlink(outfile);
	}

	if (mbox_specified == 0) {
	  if (mode != 00644) { /* if not the default mail access mode... */
	    if (! pending) { /* if none still being saved */
	      temp = fopen(infile, "w");
	      fclose(temp);
	    }
	    chmod(infile,mode);

	    /* let's set the access times of the new mail file to be
	       the same as the OLD one (still sitting in 'buf') ! */

	    times.actime = buf.st_atime;
	    times.modtime= buf.st_mtime;

	    if (utime(infile, &times) != 0) {
	      dprint0(1,"Error: encountered error doing utime (leavmbox)\n");
	      dprint2(1,"** %s - %s **\n", error_name(errno), 
		     error_description(errno));
	      error2("Error %s trying to change file %s access time", 
		     error_name(errno), infile);
	    }
	  }
	  unlock();	/* remove the lock on the file ASAP! */

	  /** finally, let's change the ownership of the default
	      outgoing mailbox, if needed **/

	  if (to_save) 
	    chown(outfile, userid, groupid);
	}

#ifdef SAVE_GROUP_MAILBOX_ID
	chown(infile, userid, getegid());	/** see the Config Guide **/
#else
	chown(infile, userid, groupid);		/**  file owned by user  **/
#endif

	return(to_delete);	
}

char lock_name[SLEN];

lock(direction)
int direction;
{
	/** Create lock file to ensure that we don't get any mail 
	    while altering the mailbox contents!
	    If it already exists sit and spin until 
               either the lock file is removed...indicating new mail
	    or
	       we have iterated MAX_ATTEMPTS times, in which case we
	       either fail or remove it and make our own (determined
	       by if REMOVE_AT_LAST is defined in header file

	    If direction == INCOMING then DON'T remove the lock file
	    on the way out!  (It'd mess up whatever created it!).
	**/

	register int iteration = 0, access_val;

	sprintf(lock_name,"%s%s.lock", mailhome, username);

	access_val = access(lock_name, ACCESS_EXISTS);

	while (access_val != -1 && iteration++ < MAX_ATTEMPTS) {
	  dprint1(2,"File '%s' currently exists!  Waiting...(lock)\n", 
		  lock_name);
	  if (direction == INCOMING)
	    PutLine0(LINES, 0, "\nMail being received!\twaiting...");
	  else
	    error1("Attempt %d: Mail being received...waiting", 
                   iteration);
	  sleep(5);
	  access_val = access(lock_name, ACCESS_EXISTS);
	}
	
	if (access_val != -1) {

#ifdef REMOVE_AT_LAST

	  /** time to waste the lock file!  Must be there in error! **/

	  dprint0(2,
	     "Warning: I'm giving up waiting - removing lock file(lock)\n");
	  if (direction == INCOMING)
	    PutLine0(LINES, 0,"\nTimed out - removing current lock file...");
	  else
	    error("Throwing away the current lock file!");

	  if (unlink(lock_name) != 0) {
	    dprint3(1,"Error %s (%s)\n\ttrying to unlink file %s (%s)\n", 
		     error_name(errno), error_description(errno), lock_name);
	    PutLine1(LINES, 0, 
		   "\n\rI couldn't remove the current lock file %s\n\r", 
		   lock_name);
	    PutLine2(LINES, 0, "** %s - %s **\n\r", error_name(errno),
		   error_description(errno));
	    if (direction == INCOMING)
	      leave();
	    else
	      emergency_exit();
	  }
	  
	  /* everything is okay, so lets act as if nothing had happened... */

#else

	  /* okay...we die and leave, not updating the mailfile mbox or
	     any of those! */
	  if (direction == INCOMING) {
	    PutLine1(LINES, 0, "\nGiving up after %d iterations...", iteration);
	    PutLine0(LINES, 0, 
		"Please try to read your mail again in a few minutes.\n");
	    dprint1(2,"Warning:bailing out after %d iterations...(lock)\n", 
		    iteration);
	    leave_locked();
	  }
	  else {
	    dprint1(2,"Warning: after %d iterations, timed out! (lock)\n", 
		    iteration);
	    leave(error("Timed out on lock file reads.  Leaving program"));
	  }

#endif
	}

	/* if we get here we can create the lock file, so lets do it! */

	if (creat(lock_name, 0) == -1) {
	  dprint2(1,"Can't create lock file: creat(%s) raises error %s (lock)\n", 
		  lock_name, error_name(errno));
	  if (errno == EACCES)
	    leave(error1(
                 "Can't create lock file!  I need write permission in %s!\n\r",
		  mailhome));
	  else {
	    dprint1(1,"Error encountered attempting to create lock %s\n", 
		  lock_name);
	    dprint2(1,"** %s - %s **\n", error_name(errno),
		  error_description(errno));
	    PutLine1(LINES, 0,
         "\n\rError encountered while attempting to create lock file %s;\n\r", 
		  lock_name);
	    PutLine2(LINES, 0, "** %s - %s **\n\r", error_name(errno),
		  error_description(errno));
	    leave();
	  }
	}
}

unlock()
{
	/** Remove the lock file!    This must be part of the interrupt
	    processing routine to ensure that the lock file is NEVER
	    left sitting in the mailhome directory! **/

	(void) unlink(lock_name);
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 12538 ]
  then
    echo $filename changed - should be 12538 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/bounceback.c ----------

filename="src/bounceback.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/bounceback.c...
fi

cat << 'END-OF-FILE' > $filename
/**			bounceback.c			**/

/** This set of routines implement the bounceback feature of the mailer.
    This feature allows mail greater than 'n' hops away (n specified by
    the user) to have a 'cc' to the user through the remote machine.  

    Due to the vagaries of the Internet addressing (uucp -> internet -> uucp)
    this will NOT generate bounceback copies with mail to an internet host!

    (C) Copyright 1986 by Dave Taylor
**/

#include "headers.h"

char *bounce_off_remote();		/* forward declaration */

int
uucp_hops(to)
char *to;
{	
	/** Given the entire "To:" list, return the number of hops in the
	    first address (a hop = a '!') or ZERO iff the address is to a
  	    non uucp address.
	**/

	register int hopcount = 0, index;

	for (index = 0; ! whitespace(to[index]) && to[index] != '\0'; index++) {
	  if (to[index] == '!')
	    hopcount++;
	  else if (to[index] == '@' || to[index] == '%' || to[index] == ':')
	    return(0);	/* don't continue! */
	}

	return(hopcount);
}
	
char *bounce_off_remote(to)
char *to;
{
	/** Return an address suitable for framing (no, that's not it...)
	    Er, suitable for including in a 'cc' line so that it ends up
	    with the bounceback address.  The method is to take the first 
	    address in the To: entry and break it into machines, then 
	    build a message up from that.  For example, consider the
	    following address:
			a!b!c!d!e!joe
	    the bounceback address would be;
			a!b!c!d!e!d!c!b!a!ourmachine!ourname
	    simple, eh?
	**/

	static char address[LONG_STRING];	/* BEEG address buffer! */

	char   host[MAX_HOPS][SHORT_SLEN];	/* for breaking up addr */
	register int hostcount = 0, hindex = 0, 
	       index;

	for (index = 0; !whitespace(to[index]) && to[index] != '\0'; index++) {
	  if (to[index] == '!') {
	    host[hostcount][hindex] = '\0';
	    hostcount++;
	    hindex = 0;
	  }
	  else 
	    host[hostcount][hindex++] = to[index];
	}

	/* we have hostcount hosts... */

	strcpy(address, host[0]);	/* initialize it! */

	for (index=1; index < hostcount; index++) {
	  strcat(address, "!");
	  strcat(address, host[index]);
	}
	
	/* and now the same thing backwards... */

	for (index = hostcount -2; index > -1; index--) {
	  strcat(address, "!");
	  strcat(address, host[index]);
	}

	/* and finally, let's tack on our machine and login name */

	strcat(address, "!");
	strcat(address, hostname);
	strcat(address, "!");
	strcat(address, username);

	/* and we're done!! */

	return( (char *) address );
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 2498 ]
  then
    echo $filename changed - should be 2498 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/opt_utils.c ----------

filename="src/opt_utils.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/opt_utils.c...
fi

cat << 'END-OF-FILE' > $filename
/**			opt_utils.c			**/

/** This file contains routines that might be needed for the various
     machines that the mailer can run on.  Please check the Makefile
     for more help and/or information. 

     (C) Copyright 1986 Dave Taylor
**/

#include <stdio.h>

#ifdef BSD
#  include <pwd.h>
#endif

#ifdef UTS
#  include <sys/utsname.h>
#  include <sys/tubio.h>

#  define  TTYIN		0		/* standard input */

gethostname(hostname,size) /* get name of current host */
int size;
char *hostname;
{
	/** Return the name of the current host machine.  UTS only **/

	/** This routine compliments of Scott McGregor at the HP
	    Corporate Computing Center **/
     
	int uname();
	struct utsname name;

	(void) uname(&name);
	(void) strncpy(hostname,name.nodename,size-1);
	hostname[size] = '\0';

}

int
isa3270()
{
	/** Returns TRUE and sets LINES and COLUMNS to the correct values
	    for an Amdahl (IBM) tube screen, or returns FALSE if on a normal
	    terminal (of course, next to a 3270, ANYTHING is normal!!) **/

	struct tubiocb tubecb;

	dprint0(3,"Seeing if we're a 3270...");

	if (ioctl(TTYIN, TUBGETMOD, &tubecb) == -1) {
	  dprint0(3,"We're not!\n");
	  return(FALSE);	/* not a tube! */
	}
	
	LINES   = tubecb->line_cnt - 1;
	COLUMNS = tubecb->col_cnt;
	
	dprint2(3,"We are!  %d lines and %d columns!\n",
		LINES, COLUMNS);
	return(TRUE);
}

#endif /* def UTS */

#ifdef BSD

cuserid(uname)
char *uname;
{
	/** Added for compatibility with Bell systems, this is the last-ditch
	    attempt to get the users login name, after getlogin() fails.  It
	    instantiates "uname" to the name of the user...
	**/

	struct passwd *password_entry, *getpwuid();

	password_entry = getpwuid(getuid());

	strcpy(uname, password_entry->pw_name);
}

/** some supplementary string functions for Berkeley Unix systems **/

strspn(source, keys)
char *source, *keys;
{
	/** This function returns the length of the substring of
	    'source' (starting at zero) that consists ENTIRELY of
	    characters from 'keys'.  This is used to skip over a
	    defined set of characters with parsing, usually. 
	**/

	register int loc = 0, key_index = 0;

	while (source[loc] != '\0') {
	  key_index = 0;
	  while (keys[key_index] != source[loc])
	    if (keys[key_index++] == '\0')
	      return(loc);
	  loc++;
	}

	return(loc);
}

strcspn(source, keys)
char *source, *keys;
{
	/** This function returns the length of the substring of
	    'source' (starting at zero) that consists entirely of
	    characters NOT from 'keys'.  This is used to skip to a
	    defined set of characters with parsing, usually. 
	    NOTE that this is the opposite of strspn() above
	**/

	register int loc = 0, key_index = 0;

	while (source[loc] != '\0') {
	  key_index = 0;
	  while (keys[key_index] != '\0')
	    if (keys[key_index++] == source[loc])
	      return(loc);
	  loc++;
	}

	return(loc);
}

char *strtok(source, keys)
char *source, *keys;
{
	/** This function returns a pointer to the next word in source
	    with the string considered broken up at the characters 
	    contained in 'keys'.  Source should be a character pointer
	    when this routine is first called, then NULL subsequently.
	    When strtok has exhausted the source string, it will 
	    return NULL as the next word. 

	    WARNING: This routine will DESTROY the string pointed to
	    by 'source' when first invoked.  If you want to keep the
	    string, make a copy before using this routine!!
	 **/

	register int  last_ch;
	static   char *sourceptr;
		 char *return_value;

	if (source != NULL)
	  sourceptr = source;
	
	if (*sourceptr == '\0') 
	  return(NULL);		/* we hit end-of-string last time!? */

	sourceptr += strspn(sourceptr, keys);	/* skip leading crap */
	
	if (*sourceptr == '\0') 
	  return(NULL);		/* we've hit end-of-string */

	last_ch = strcspn(sourceptr, keys);	/* end of good stuff */

	return_value = sourceptr;		/* and get the ret   */

	sourceptr += last_ch;			/* ...value 	     */

	if (*sourceptr != '\0')		/* don't forget if we're at END! */
	  sourceptr++;			   /* and skipping for next time */

	return_value[last_ch] = '\0';		/* ..ending right    */
	
	return((char *) return_value);		/* and we're outta here! */
}

char *strpbrk(source, keys)
char *source, *keys;
{
	/** Returns a pointer to the first character of source that is any
	    of the specified keys, or NULL if none of the keys are present
	    in the source string. 
	**/

	register int loc = 0, key_index = 0;

	while (source[loc] != '\0') {
	  key_index = 0;
	  while (keys[key_index] != '\0')
	    if (keys[key_index++] == source[loc])
	      return((char *) (source + loc));
	  loc++;
	}
	
	return(NULL);
}

char *strchr(buffer, ch)
char *buffer, ch;
{
	/** Returns a pointer to the first occurance of the character
	    'ch' in the specified string or NULL if it doesn't occur **/

	char *address;

	address = buffer;

	while (*address != ch) {
	  if (*address == '\0')
	    return (NULL);
	  address++;
	}

	return ( (char *) address);
}

#endif
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 4997 ]
  then
    echo $filename changed - should be 4997 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/softkeys.c ----------

filename="src/softkeys.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/softkeys.c...
fi

cat << 'END-OF-FILE' > $filename
/**			softkeys.c			**/

/** This file and associated routines: (C) Copyright 1986, Dave Taylor **/

#include <stdio.h>
#include "headers.h"

define_softkeys(level)
int level;
{
	if (! hp_softkeys) return;

	if (level == MAIN) {
	  if (notesfile) {
	    define_key(f1, "  Show    Note",   "\r");
	    define_key(f2, "  Reply  to Note",  "r");
	    define_key(f3, " Change  Mailbox", "c");
	    define_key(f4, "  Save    Note",   "s");
	    define_key(f5, " Delete/Undelete", "^");
	    define_key(f6, " Print    Note",   "p");
	    define_key(f7, "  HELP",           "?");
	    define_key(f8, "  Quit     ELM",   "q");
	  }
	  else {
	    define_key(f1, "  Show     Msg",   "\r");
	    define_key(f2, "  Mail     Msg",   "m");
	    define_key(f3, "  Reply  to Msg",  "r");
	    define_key(f4, " Change  Mailbox", "c");
	    define_key(f5, "  Save     Msg",   "s");
	    define_key(f6, " Delete/Undelete", "^");
	    define_key(f7, " Print     Msg",   "p");
	    define_key(f8, "  Quit     ELM",   "q");
	  }
	}
	else if (level == ALIAS) {
	  define_key(f1, " Alias  Current",  "a");
	  define_key(f2, " Check   Alias",   "c");
	  define_key(f3, " Make    Alias",   "m");
	  clear_key(f4);
	  clear_key(f5);
	  clear_key(f6);
	  clear_key(f7);
	  define_key(f8, " Return  to ELM",  "r");
	}
	else if (level == YESNO) {
	  define_key(f1, "  Yes",  "y");
	  clear_key(f2);
	  clear_key(f3);
	  clear_key(f4);
	  clear_key(f5);
	  clear_key(f6);
	  clear_key(f7);
	  define_key(f8, "   No",  "n");
	}
	else if (level == READ) {
	  define_key(f1, "  Next    Page  ", " ");
	  clear_key(f2);
	  clear_key(f3);
	  clear_key(f4);
	  clear_key(f5);
	  clear_key(f6);
	  clear_key(f7);
	  define_key(f8, " Return  to ELM", "\n");
	}
	else if (level == CHANGE) {
	  define_key(f1, "  Mail  Directry", "=/");
	  define_key(f2, "  Home  Directry", "~/");
	  clear_key(f3);
	  define_key(f4, "Incoming Mailbox", "!\n");
	  clear_key(f5);
	  clear_key(f6);
	  clear_key(f7);
	  define_key(f8, " Cancel", "\n");
	}

	softkeys_on();
}

define_key(key, display, send)
int key;
char *display, *send;
{

	char buffer[30];

	sprintf(buffer,"%s%s", display, send);

	fprintf(stderr, "%c&f%dk%dd%dL%s", ESCAPE, key,
		strlen(display), strlen(send), buffer);
	fflush(stdout);
}

softkeys_on()	
{ 
	/* turn on softkeys (esc&jB) and turn on MENU and USER/SYSTEM */

	if (hp_softkeys) {
	  fprintf(stderr, "%c&jB%c&jR", ESCAPE, ESCAPE); 
	  fflush(stdout);
	}
	
}

softkeys_off()	
{ 
	/* turn off softkeys (esc&j@) */

	if (hp_softkeys) {
	  fprintf(stderr, "%c&j@", ESCAPE); 
	  fflush(stdout);
	}
}

clear_key(key)  
{ 	
	/** set a key to nothing... **/

	if (hp_softkeys) 
	   define_key(key, "                ", ""); 
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 2696 ]
  then
    echo $filename changed - should be 2696 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/mailmsg2.c ----------

filename="src/mailmsg2.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/mailmsg2.c...
fi

cat << 'END-OF-FILE' > $filename
/** 			mailmsg2.c			**/

/** Interface to allow mail to be sent to users.  Part of ELM  **/

/** (C) Copyright 1986, Dave Taylor 			       **/

#include "headers.h"
#include <errno.h>

extern int errno;

char *error_name(), *error_description();

char *format_long(), *strip_commas(), *tail_of_string();
FILE *write_header_info();

extern char subject[SLEN], action[SLEN], reply_to[SLEN], expires[SLEN], 
       priority[SLEN], to[VERY_LONG_STRING], cc[VERY_LONG_STRING],
       in_reply_to[SLEN];

int gotten_key = 0;

char *bounce_off_remote();

mail(expanded_to, expanded_cc, copy_msg, edit_message, batch)
char *expanded_to, *expanded_cc;
int  copy_msg, edit_message, batch;
{
	/** Given the addresses and various other miscellany (specifically, 
	    'copy-msg' indicates whether a copy of the current message should 
	    be included, 'edit_message' indicates whether the message should 
	    be edited and 'batch' indicates that the message should be read 
	    from stdin) this routine will invoke an editor for the user and 
	    then actually mail off the message.
	**/

	FILE *reply, *real_reply; /* second is post-input buffer */
	char filename[SLEN], filename2[SLEN],
             very_long_buffer[VERY_LONG_STRING];
	char ret_ch, ch;
	register int retransmit = FALSE;

	static int cancelled_msg = 0;

	dprint2(4,"\nMailing to '%s' (with%s editing)\n",
		  expanded_to, edit_message? "" : "out");
	
	/** first generate the temporary filename **/

	sprintf(filename,"%s%d",temp_file, getpid());

	/** if possible, let's try to recall the last message? **/

	if (! batch)
	  retransmit = recall_last_msg(filename, copy_msg, &cancelled_msg);

	/** if we're not retransmitting, create the file.. **/

	if (! retransmit)
	  if ((reply = fopen(filename,"w")) == NULL) {
	    dprint2(1,
               "Attempt to write to temp file %s failed with error %s (mail)\n",
		 filename, error_name(errno));
	    error2("Could not create file %s (%s)",filename,
		 error_name(errno));
	    return(1);
	  }

	if (batch) {
	  Raw(OFF);
	  ret_ch = '\n';
	  if (isatty(fileno(stdin))) {
	    printf("To: %s\nSubject: %s\n", expanded_to, subject);
	    printf("\nPlease enter your message, ending with a ^D:\n\n");
	    ret_ch = '\0';
	  }
	  while (gets(very_long_buffer, VERY_LONG_STRING) != NULL) 
	    fprintf(reply, "%s%c", very_long_buffer, ret_ch);

	  if (isatty(fileno(stdin))) 
	    printf("<end-of-message>\n");
	}

	if (copy_msg && ! retransmit) 	/* if retransmit we ALREADY have it! */
	  if (edit_message) 
	    copy_message(prefixchars, reply, noheader, FALSE);
	  else
	    copy_message("", reply, noheader, FALSE);

	if (!batch && ! retransmit && signature) {
	  sprintf(filename2, "%s/%s", home, signature_file);
	  (void) append(reply, filename2);
	}

	if (! retransmit)
	  (void) fclose(reply);	/* on replies, it won't be open! */

	/** Edit the message **/

	if (edit_message)
	  create_readmsg_file();	/* for "readmsg" routine */

	ch = edit_message? 'e' : 'e';	/* drop through if needed... */

	if (! batch) {
	  do {
	    switch (ch) {
	      case 'e' : if (edit_message) edit_the_message(filename);

	               /** now let's try reading it again... **/

		       (void) fclose(reply);	/* make sure it's closed */
	               if ((reply = fopen(filename,"r")) == NULL) {
	                 dprint2(1,
	    "Attempt to open file %s for reading failed with error %s (mail)\n",
                                filename, error_name(errno));
	                 error1("Could not open reply file (%s)", 
				error_name(errno));
	                 return(1);
	               }
		       break;

	       case 'h' : edit_headers();		
			break;
	    }

	    /** ask that silly question again... **/
  
	    if ((ch = verify_transmission()) == 'f') {	/* cancelled it! */
	      cancelled_msg = TRUE;
	      return(1);
	    }
	  } while (ch != 's');
	}
	else if ((reply = fopen(filename,"r")) == NULL) {
	    dprint2(1,
	    "Attempt to open file %s for reading failed with error %s (mail)\n",
             filename, error_name(errno));
	    error1("Could not open reply file (%s)", error_name(errno));
	    return(1);
	}

	cancelled_msg = FALSE;	/* it ain't cancelled, is it? */

	/** ask about bounceback if the user wants us to.... **/

	if (uucp_hops(to) > bounceback && bounceback > 0) 
	  if (verify_bounceback() == TRUE) {
	    if (strlen(cc) > 0) strcat(cc, ", ");
	    strcat(cc, bounce_off_remote(to));
	  }

	/** grab a copy if the user so desires... **/

	if (auto_cc && ! batch) 
	  save_copy(subject, expanded_to, expanded_cc, filename, to);

	/** write all header information into real_reply **/

	sprintf(filename2,"%s%d",temp_file, getpid()+1);
	
	/** try to write headers to new temp file **/

	dprint2(5, "Composition file='%s' and mail buffer='%s'\n", 
		    filename, filename2);

	if ((real_reply=write_header_info(filename2, expanded_to, expanded_cc))
	     == NULL) {	

	  /** IT FAILED!!  MEIN GOTT!  Use a dumb mailer instead! **/

	  dprint1(3,"** write_header failed: %s\n", error_name(errno));

	  if (cc[0] != '\0')  		/* copies! */
	    sprintf(expanded_to,"%s %s", expanded_to, expanded_cc);

	  sprintf(very_long_buffer, "( (%s -s \"%s\" %s ; %s %s) & ) < %s",
                  mailx, subject, strip_commas(expanded_to), 
		  remove, filename, filename);

	  error1("Message sent using dumb mailer - %s", mailx);
	  sleep(2);	/* ensure time to see this prompt! */

	}
	else {
	  copy_message_across(reply, real_reply);

	  fclose(real_reply);

	  if (cc[0] != '\0')  				         /* copies! */
	    sprintf(expanded_to,"%s %s", expanded_to, expanded_cc);

	  if (access(sendmail, EXECUTE_ACCESS) == 0)	/* Yeah!  Sendmail! */
	    sprintf(very_long_buffer,"( (%s %s %s ; %s %s) & ) < %s",
                  sendmail, smflags, strip_commas(expanded_to), 
		  remove, filename2, filename2);
	  else 				   /* oh well, use default mailer... */
            sprintf(very_long_buffer,"( (%s %s ; %s %s) & ) < %s", 
                  mailer, strip_commas(expanded_to), 
		  remove, filename2, filename2);
	}
	
	fclose(reply);

	if (mail_only) {
	  printf("sending mail...");
	  fflush(stdout);
	}
	else {	
	  PutLine0(LINES,0,"sending mail...");
	  CleartoEOLN();
	}

	system_call(very_long_buffer, SH);

	if (mail_only) 
	  printf("\rmail sent!      \n\r");
	else 
	  set_error("Mail sent!");

	return(TRUE);
}

int
recall_last_msg(filename, copy_msg, cancelled_msg)
char *filename;
int  copy_msg, *cancelled_msg;
{
	/** If filename exists and we've recently cancelled a message,
	    the ask if the user wants to use that message instead!  This
	    routine returns TRUE if the user wants to retransmit the last
	    message, FALSE otherwise...
	**/

	register int retransmit = FALSE;

	if (access(filename, EDIT_ACCESS) == 0 && *cancelled_msg) {
	  Raw(ON);
	  CleartoEOLN();
	  if (copy_msg)
	    PutLine1(LINES-1,0,"Recall last kept message instead? (y/n) y%c",
		     BACKSPACE);
	  else
	    PutLine1(LINES-1,0,"Recall last kept message? (y/n) y%c", 
		     BACKSPACE);
	  fflush(stdout);
	  if (tolower(ReadCh()) != 'n') {
	    Write_to_screen("Yes",0);	
            retransmit++;
	  }
	  else 
	    Write_to_screen("No",0);	

	  fflush(stdout);

	  *cancelled_msg = 0;
	}

	return(retransmit);
}

edit_the_message(filename)
char *filename;
{
	/** Invoke the users editor on the filename.  Return when done. **/

	char buffer[SLEN];
	register int stat;

	PutLine0(LINES, 0, "invoking editor...");  fflush(stdout);

	sprintf(buffer,"%s %s", editor, filename);

	Raw(OFF);

	chown(filename, userid, groupid);	/* file was owned by root! */

	if (cursor_control)
	  transmit_functions(OFF);		/* function keys are local */

	if ((stat = system_call(buffer, SH)) != 0) { 
	  dprint1(1,"System call failed with stat %d (edit_the_message)\n", 
		  stat);
	  error1("Can't invoke editor '%s' for composition", editor);
	  dprint2(1,"** %s - %s **\n", error_name(errno), 
		error_description(errno));
	  ClearLine(LINES-1);
	  sleep(2);
	}

	if (cursor_control)
	  transmit_functions(ON);		/* function keys are local */
	
	Raw(ON);
}

int
verify_transmission()
{
	/** Ensure the user wants to send this.  This routine returns
	    TRUE if everything is okay, FALSE if cancelled. **/

	char ch;

	if (mail_only) {
	  if (isatty(fileno(stdin))) {
	    printf("\n\rAre you sure you want to send this? (y/n) ");
	    CleartoEOLN();
	    printf("y%c", BACKSPACE);
	    fflush(stdin);				/* wait for answer! */
	    fflush(stdout);
	    if (tolower(ReadCh()) == 'n') { 			/* >SIGH< */
	      printf("No\n\r\n\r\n\rMail not sent!!\n\r");
	      return('f');	/* forget it! */
	    }
	    else
	      printf("Yes\n\r\n\r");
	    return('s');       /* send this baby! */
	  }
	  else
	    return('s');	/*    ditto       */
	}
	else if (check_first) {    /* used to check strlen(infile) > 0 too? */
reprompt:
	  MoveCursor(LINES,0);
	  CleartoEOLN();
	  ClearLine(LINES-1);

	  PutLine1(LINES-1, 0, 
	   "Please choose: E)dit msg, edit H)eaders, S)end, or F)orget : s%c", 
	   BACKSPACE);

	  fflush(stdin);	/* wait for answer! */
	  fflush(stdout);
	  ch = tolower(ReadCh());

	  switch (ch) {
	     case 'f': Write_to_screen("Forget",0);
	               set_error(
          "Message kept - Can be restored at next F)orward, M)ail or R)eply ");
		       break;

	     case '\n' :
	     case '\r' :
	     case 's'  : Write_to_screen("Send",0);
			 ch = 's';		/* make sure! */
			 break;

	     case 'e'  : Write_to_screen("Edit",0);
			 break;
	     case 'h'  : Write_to_screen("Headers",0);
			 break;

	     default   : Write_to_screen("%c??", 1, 07);	/* BEEP */
			 sleep(1);
		         goto reprompt;		/* yech */
	   }

	   return(ch);
	}
}

FILE *
write_header_info(filename, long_to, long_cc)
char *filename, *long_to, *long_cc;
{
	/** Try to open filedesc as the specified filename.  If we can,
	    then write all the headers into the file.  The routine returns
	    'filedesc' if it succeeded, NULL otherwise **/

	static FILE *filedesc;		/* our friendly file descriptor  */

	FILE *headerfdesc;	       /* file descriptor for .elmheaders */
	char  fnamebuffer[SLEN];       /* buffer for filename creation    */
	char  buffer[LONG_SLEN];       /* .elmheader file reading buffer  */

	char  *get_arpa_date();

	if ((filedesc = fopen(filename, "w")) == NULL) {
	  dprint1(1,
	    "Attempt to open file %s for writing failed! (write_header_info)\n",
	     filename);
	  dprint2(1,"** %s - %s **\n\n", error_name(errno),
		 error_description(errno));
	  error2("Error %s encountered trying to write to %s", 
		error_name(errno), filename);
	  sleep(2);
	  return(NULL);		/* couldn't open it!! */
	}

	fprintf(filedesc, "To: %s\n", format_long(long_to, strlen("To:")));

	fprintf(filedesc,"Date: %s\n", get_arpa_date());

#ifndef DONT_ADD_FROM
# ifdef  INTERNET_ADDRESS_FORMAT
#  ifdef  USE_DOMAIN
	fprintf(filedesc,"From: %s@%s%s (%s)\n", username, hostname, 
		DOMAIN, full_username);
#  else
	fprintf(filedesc,"From: %s@%s (%s)\n", username, hostname, 
		full_username);
#  endif
# else
	fprintf(filedesc,"From: %s!%s (%s)\n", hostname, username,
		full_username);
# endif
#endif

	fprintf(filedesc, "Subject: %s\n", subject);

	if (cc[0] != '\0')
	  fprintf(filedesc, "Cc: %s\n", format_long(long_cc, strlen("Cc: ")));

	if (strlen(action) > 0)
	    fprintf(filedesc, "Action: %s\n", action);
	
	if (strlen(priority) > 0)
	    fprintf(filedesc, "Priority: %s\n", priority);
	
	if (strlen(expires) > 0)
	    fprintf(filedesc, "Expiration-Date: %s\n", expires);
	
	if (strlen(reply_to) > 0)
	    fprintf(filedesc, "Reply-To: %s\n", reply_to);

	if (strlen(in_reply_to) > 0)
	    fprintf(filedesc, "In-Reply-To: %s\n", in_reply_to);
	
	/*** add the users .mailheaders file if available ***/

	sprintf(fnamebuffer, "%s/%s", home, mailheaders);

	if ((headerfdesc = fopen(fnamebuffer, "r")) != NULL) {
	  while (fgets(buffer, LONG_SLEN, headerfdesc) != NULL)
	    if (strlen(buffer) < 2) {
	      dprint0(2,
	         "Strlen of line from .elmheaders is < 2 (write_header_info)");
	      error1("Warning: blank line in %s ignored!", fnamebuffer);
	      sleep(2);
	    }
	    else 
	      fprintf(filedesc, "%s", buffer);

	    fclose(headerfdesc);
	}

	fprintf(filedesc, "X-Mailer: ELM [version %s]\n\n", VERSION);

	return((FILE *) filedesc);
}

copy_message_across(source, dest)
FILE *source, *dest;
{
	/** copy the message in the file pointed to by source to the
	    file pointed to by dest.  **/

	int  crypted = FALSE;			/* are we encrypting?  */
	int  encoded_lines = 0;			/* # lines encoded     */
	char buffer[LONG_SLEN];			/* file reading buffer */

	while (fgets(buffer, LONG_SLEN, source) != NULL) {
	  if (buffer[0] == '[') {
	    if (strncmp(buffer, START_ENCODE, strlen(START_ENCODE))==0)
	      crypted = TRUE;
	     else if (strncmp(buffer, END_ENCODE, strlen(END_ENCODE))==0)
	      crypted = FALSE;
	     else if (strncmp(buffer, DONT_SAVE, strlen(DONT_SAVE)) == 0)
	      continue;	/* next line? */
	    }
	    else if (crypted) {
	      if (! gotten_key++)
	        getkey(ON);
	      else if (! encoded_lines++)
	        get_key_no_prompt();		/* reinitialize.. */
	     
	      encode(buffer);
	    }
	    fputs(buffer, dest);
	  }
}

int
verify_bounceback()
{
	/** Ensure the user wants to have a bounceback copy too.  (This is
	    only called on messages that are greater than the specified 
	    threshold hops and NEVER for non-uucp addresses.... Returns
	    TRUE iff the user wants to bounce a copy back.... 
	 **/

	if (mail_only) {
	  printf("Would you like a copy \"bounced\" off the remote? (y/n) ");
	  CleartoEOLN();
	  printf("n%c", BACKSPACE);
	  fflush(stdin);				/* wait for answer! */
	  fflush(stdout);
	  if (tolower(ReadCh()) != 'y') {
	    printf("No\n\r");
	    return(FALSE);
	  }
	  else
	    printf("Yes - Bounceback included\n\r");
	}
	else {
	  MoveCursor(LINES,0);
	  CleartoEOLN();
	  PutLine1(LINES,0, 
		"\"Bounce\" a copy off the remote machine? (y/n) y%c",
		BACKSPACE);
	  fflush(stdin);	/* wait for answer! */
	  fflush(stdout);
	  if (tolower(ReadCh()) != 'y') { 
	    Write_to_screen("No", 0);
	    fflush(stdout);
	    return(FALSE);
	  }
	  Write_to_screen("Yes!", 0);
	  fflush(stdout);
	}

	return(TRUE);
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 14200 ]
  then
    echo $filename changed - should be 14200 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

# ---------- file src/savecopy.c ----------

filename="src/savecopy.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file src/savecopy.c...
fi

cat << 'END-OF-FILE' > $filename
/** 			savecopy.c			**/

/** Save a copy of the specified message in the users savemail mailbox.

         (C) Copyright 1986, Dave Taylor 			       
**/

#include "headers.h"
#ifdef BSD
#  include <sys/time.h>
#else
#  include <time.h>
#endif

#include <errno.h>

char *format_long(), *get_arpa_date();
char *error_name(), *error_description();
char *ctime();

extern char in_reply_to[SLEN];	/* In-Reply-To: string */
extern int gotten_key;		/* for encryption      */
extern int errno;

save_copy(subject, to, cc, filename, original_to)
char *subject, *to, *cc, *filename, *original_to;
{
	/** This routine appends a copy of the outgoing message to the
	    file specified by the SAVEFILE environment variable.  **/

	FILE *save,		/* file id for file to save to */
	     *message;		/* the actual message body     */
	long  thetime;		/* variable holder for time    */
	char  buffer[SLEN],	/* read buffer 		       */
	      savename[SLEN],	/* name of file saving into    */
	      newbuffer[SLEN];  /* first name in 'to' line     */
	register int i;		/* for chopping 'to' line up   */
	int   crypted=0;	/* are we encrypting?          */

	savename[0] = '\0';

	if (save_by_name) {
	  get_return_name(to, buffer, FALSE);
	  if (strlen(buffer) == 0) {
	    dprint1(3,"Warning: get_return_name couldn't break down %s\n", to);
	    savename[0] = '\0';
	  }
	  else {
	    sprintf(savename, "%s%s%s", folders, 
	            lastch(folders) == '/'? "" : "/", buffer);

	    if ((errno = can_access(savename, READ_ACCESS)))
	      savename[0] = '\0';
	  }
	}

	if (strlen(savename) == 0) {
	  if (strlen(savefile) == 0)
	    return(error("variable 'SAVEFILE' not defined!"));
	  strcpy(savename, savefile);
	}

	if ((errno = can_access(savename, WRITE_ACCESS))) {
	  dprint0(2,"Error: attempt to autosave to a file that can't...\n");
	  dprint1(2,"\tbe appended to: %s (save_copy)\n", savename);
	  dprint2(2,"** %s - %s **\n", error_name(errno),
		  error_description(errno));
	  error1("permission to append to %s denied!", savename);
	  sleep(2);
	  return(FALSE);
	}

	if ((save = fopen(savename, "a")) == NULL) {
	  dprint2(1,"Error: Couldn't append message to file %s (%s)\n",
		  savename, "save_copy");
	  dprint2(1,"** %s - %s **\n", error_name(errno),
		  error_description(errno));
	  error1("couldn't append to %s", savename);
	  sleep(2);
	  return(FALSE);
	}

	if ((message = fopen(filename, "r")) == NULL) {
	  fclose(save);
	  dprint1(1,"Error: Couldn't read file %s (save_copy)\n", filename);
	  dprint2(1,"** %s - %s **\n", error_name(errno),
		  error_description(errno));
	  error1("couldn't read file %s!", filename);
	  sleep(2);
	  return(FALSE);
	}

	for (i=0; original_to[i] != '\0' && ! whitespace(original_to[i]); i++)
	  newbuffer[i] = original_to[i];

	newbuffer[i] = '\0';

	tail_of(newbuffer, buffer, FALSE);

	thetime = (long) time(0);	/* this must be here for it to work! */

	fprintf(save,"\nFrom To:%s %s", buffer, ctime(&thetime));

	fprintf(save, "Date: %s\n", get_arpa_date());
			
	fprintf(save,"To: %s\nSubject: %s\n", 
		format_long(to,strlen("To: ")), subject);

	if (strlen(cc) > 0)
	  fprintf(save,"Cc: %s\n", 
		  format_long(cc, strlen("Cc:")));

	if (strlen(in_reply_to) > 0)
	  fprintf(save, "In-Reply-To: %s\n", in_reply_to);

	(void) putc('\n', save);	/* put another return, please! */

	/** now copy over the message... **/

	while (fgets(buffer, SLEN, message) != NULL) {
	  if (buffer[0] == '[') {
	    if (strncmp(buffer, START_ENCODE, strlen(START_ENCODE))==0)
	      crypted = 1;
	    else if (strncmp(buffer, END_ENCODE, strlen(END_ENCODE))==0)
	      crypted = 0;
	    else if (strncmp(buffer, DONT_SAVE, strlen(DONT_SAVE)) == 0) {
	      fclose(message);
	      fclose(save);
	      chown(savename, userid, groupid);	
	      return(TRUE);
	    }
	  }
	  else if (crypted) {
	    if (! gotten_key++)
	      getkey(ON);
	    encode(buffer);
	  }
	  fputs(buffer, save);
	}

	fclose(message);
	fclose(save);

	/* make sure save file isn't owned by root! */
	chown(savename, userid, groupid);	

	return(TRUE);
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 4059 ]
  then
    echo $filename changed - should be 4059 bytes, not $size bytes
  fi

  chmod 644 $filename
fi

echo end of this archive file....
exit 0