/*--------------------------------------------------------------------*/
/*    d e l i v e r  . c                                              */
/*                                                                    */
/*    UUPC/extended mail delivery subroutines                         */
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/
/*    Changes Copyright (c) 1989-1993 by Kendra Electronic            */
/*    Wonderworks.                                                    */
/*                                                                    */
/*    All rights reserved except those explicitly granted by the      */
/*    UUPC/extended license agreement.                                */
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/
/*                          RCS Information                           */
/*--------------------------------------------------------------------*/

/*
 *    $Id: deliver.c 1.16 1993/09/23 03:26:51 ahd Exp $
 *
 *    $Log: deliver.c $
 * Revision 1.16  1993/09/23  03:26:51  ahd
 * Alter bounce message for "no path to host" error
 *
 * Revision 1.15  1993/09/20  04:41:54  ahd
 * OS/2 2.x support
 *
 * Revision 1.14  1993/08/02  03:24:59  ahd
 * Further changes in support of Robert Denny's Windows 3.x support
 *
 * Revision 1.13  1993/07/31  16:26:01  ahd
 * Changes in support of Robert Denny's Windows support
 *
 * Revision 1.12  1993/06/21  02:17:31  ahd
 * Correct errors in mail routing via HOSTPATH
 *
 * Revision 1.11  1993/06/13  14:06:00  ahd
 * Save invoked program name and use it for recursive calls
 * Loosen up bounced mail copy loop to avoid NT crashes
 *
 * Revision 1.10  1993/05/30  00:01:47  ahd
 * Expand path of system alias files to allow userid references
 *
 * Revision 1.9  1993/05/06  03:41:48  ahd
 * Don't rebounce mail to the postmaster
 * Change directories as needed to provide reasonable default drives
 * Do not use possibly invalid home directory to push directory on
 * system aliases
 *
 * Revision 1.8  1993/05/03  02:41:57  ahd
 * Make deliver not rebounce mail to the postmonstor
 *
 * Revision 1.7  1993/04/16  12:55:36  dmwatt
 * Windows/NT sound support
 *
 * Revision 1.6  1993/04/15  03:17:21  ahd
 * Basic bounce support
 *
 * Revision 1.5  1993/04/11  00:33:05  ahd
 * Global edits for year, TEXT, etc.
 *
 * Revision 1.4  1992/12/18  13:05:18  ahd
 * Use one token on request line for UUCP
 *
 * Revision 1.3  1992/12/05  23:38:43  ahd
 * Skip blanks as well as unprintable characters
 *
 * Revision 1.2  1992/12/04  01:00:27  ahd
 * Add system alias support
 *
 */

/*--------------------------------------------------------------------*/
/*    Embedded Japanese support provided by Kenji Rikitake            */
/*    28-AUG-1991                                                     */
/*                                                                    */
/*    On Japanese support:                                            */
/*                                                                    */
/*    Japanese MS-DOS uses a 2byte Kanji (Japanese ideogram) code     */
/*    called "Shift-JIS".  This cannot be delivered via SMTP since    */
/*    Shift-JIS maps its first byte from 0x80-0x9f and 0xe0-0xfc.     */
/*    JUNET requests all hosts to send Kanji in a 7bit subset of      */
/*    ISO2022.  This is commonly called "JIS 7bit".                   */
/*                                                                    */
/*    To provide Japanese functionality, you need to convert all      */
/*    remote delivery messages to JIS 7bit, and all local delivery    */
/*    messages to Shift-JIS.                                          */
/*--------------------------------------------------------------------*/

#define INCLUDE ":include:"

/*--------------------------------------------------------------------*/
/*                        System include files                        */
/*--------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <ctype.h>
#include <sys/types.h>
#include <string.h>
#include <process.h>
#include <limits.h>

/*--------------------------------------------------------------------*/
/*                    UUPC/extended include files                     */
/*--------------------------------------------------------------------*/

#include "lib.h"
#include "address.h"
#include "deliver.h"
#include "expath.h"
#include "execute.h"
#include "getseq.h"
#include "kanjicnv.h"
#include "hlib.h"
#include "hostable.h"
#include "import.h"
#include "pushpop.h"
#include "security.h"
#include "stater.h"
#include "usertabl.h"
#include "sysalias.h"
#include "timestmp.h"
#include "trumpet.h"

/*--------------------------------------------------------------------*/
/*        Define current file name for panic() and printerr()         */
/*--------------------------------------------------------------------*/

currentfile();

/*--------------------------------------------------------------------*/
/*                        Internal prototypes                         */
/*--------------------------------------------------------------------*/

static size_t DeliverLocal( const char *input,  /* Input file name    */
                          char *user,     /* Target address           */
                          const boolean sysalias,
                                             /* Already sys alias     */
                          boolean validate); /* Validate/forward
                                                local mail            */

static int DeliverFile( const char *input,
                        const char *mboxname,
                        const long start,
                        const long end,
                        boolean *announce,
                        struct UserTable *userp,
                        const boolean sysalias,  /* Already sys alias     */
                        const boolean validate,
                        const char *user );

static size_t DeliverRemote( const char *input, /* Input file name    */
                             const char *address,  /* Target address  */
                             const char *path);

static size_t DeliverGateway(   const char *input,
                                const char *user,
                                const char *node,
                                const struct HostTable *hostp,
                                const boolean validate );

static int CopyData(   const boolean remotedelivery,
                       const char *input,
                       FILE *mbox);

static char *stats( const char *fname );

size_t Bounce( const char *input,
               const char *text,
               const char *data,
               const char *address,
               const boolean validate );

/*--------------------------------------------------------------------*/
/*   Global (set by rmail.c) for number of hops this mail has seen    */
/*--------------------------------------------------------------------*/

 KEWSHORT hops = 0;

 boolean remoteMail = FALSE;

 char *ruser = NULL;
 char *rnode = NULL;
 char *uuser = NULL;

/*--------------------------------------------------------------------*/
/*    D e l i v e r                                                   */
/*                                                                    */
/*    Deliver mail to one user                                        */
/*--------------------------------------------------------------------*/

size_t Deliver(       const char *input,    /* Input file name       */
                            char *address,  /* Target address           */
                      const boolean sysalias,  /* Already sys alias     */
                          boolean validate)  /* Validate/forward
                                                local mail            */
{
   char node[MAXADDR];
   char path[MAXADDR];
   char user[MAXADDR];
   char *token;
   struct HostTable *hostp;

   if ( strlen( address ) >= MAXADDR )
      return Bounce( input,
                     "Excessive address length",
                     address,
                     address,
                     validate );

   user_at_node(address, path, node, user);

/*--------------------------------------------------------------------*/
/*                       Handle local delivery                        */
/*--------------------------------------------------------------------*/

   if (equal(path, E_nodename)) /* Local node?                       */
   {
      struct HostTable *hostx = checkname( node );
      if (hostx->hstatus == localhost)  /* Really the local node?    */
         return DeliverLocal( input, user, sysalias, validate );
                                 /* Yes!                             */
      else
         return Bounce( input,
                 "No known delivery path for host",
                  address,
                  address,
                  validate );
   }  /* if */

/*--------------------------------------------------------------------*/
/*                    Do we need loop protection?                     */
/*--------------------------------------------------------------------*/

   if (hops > E_maxhops)
      return Bounce(input,
             "Excessive number of hops",
             address,
             address,
             validate );

/*--------------------------------------------------------------------*/
/*                   Deliver to a gateway if needed                   */
/*--------------------------------------------------------------------*/

   hostp = checkname( path );
   if ( (hostp != BADHOST) && (hostp->hstatus == gatewayed))
      return DeliverGateway( input, user, node, hostp, validate );

/*--------------------------------------------------------------------*/
/*         Deliver mail to a system directory connected to us         */
/*--------------------------------------------------------------------*/

   if (equal(path,node))   /* Directly connected system?          */
      return DeliverRemote( input, user, path); /* Yes            */

/*--------------------------------------------------------------------*/
/*   Default delivery; strip any this node and the directly           */
/*   connected system from the address, then deliver to the next      */
/*   hop on the route                                                 */
/*--------------------------------------------------------------------*/

   strcpy(node,address);
   token = strtok(node,"!");  /* Get first host in path        */
   if (equal( HostAlias(token), E_nodename)) /* Local system?  */
   {
      token =  strtok(NULL,"");  /* Yes --> Get rest of addr   */
      strcpy(address, token);    /* Use it for address         */
      token = strtok(token,"!"); /* Get next host in path      */
   } /* if */

   if (equal( HostAlias(token), path ))  /* Next system?       */
   {
      token =  strtok(NULL,"");  /* Yes --> Get rest of addr   */
      strcpy(address, token);    /* Use it for address         */
   } /* if */

   if (!strpbrk(address,"!@"))   /* Any host delimiters?       */
   {                             /* No --> Check for % routing */
      token = strrchr(address,'%'); /* Get last percent sign   */
      if (token != NULL)
         *token = '@';           /* Make it an RFC-822 address */
      else
         printmsg(0,"Deliver: Cannot find node in \"%s\"",
               address);         /* That's odd, it should not  */
                                 /* be a local address!        */
   } /* if */

   return DeliverRemote( input, address, path );

} /* Deliver */

/*--------------------------------------------------------------------*/
/*    D e l i v e r L o c a l                                         */
/*                                                                    */
/*    Handle local delivery, including optional forwarding            */
/*--------------------------------------------------------------------*/

static size_t DeliverLocal( const char *input,
                                          /* Input file name          */
                          char *user,     /* Target address           */
                          const boolean sysalias,
                                          /* Already sys alias     */
                          boolean validate)  /* TRUE = validate,
                                                forward user's mail   */
{
   char mboxname[FILENAME_MAX];
   struct UserTable *userp = NULL;
   ALIASTABLE *aliasp = NULL;
   int delivered = 0;
   boolean announce = FALSE;
   FILE *mbox;

/*--------------------------------------------------------------------*/
/*    If the parameter is the postmaster, use the configuration       */
/*    defined value for the postmaster                                */
/*--------------------------------------------------------------------*/

   if (equali(user, POSTMASTER))
      user = E_postmaster;

/*--------------------------------------------------------------------*/
/*             Validate user id and check for forwarding              */
/*--------------------------------------------------------------------*/

   if (validate)
   {
      validate = strcmp( E_postmaster , user);
                                 /* Don't loop delivering to postmast*/

      userp = checkuser(user);   /* Locate user id in host table     */

/*--------------------------------------------------------------------*/
/*                     Process any system aliases                     */
/*--------------------------------------------------------------------*/

      if ( ! sysalias )
      {
         aliasp = checkalias( user );  /* System alias?             */

         if ( aliasp != NULL )
         {
            delivered += DeliverFile( input,
                                      SysAliases,
                                      aliasp->start,
                                      aliasp->end,
                                      &announce ,
                                      userp,
                                      TRUE,
                                      validate,
                                      user );

            if ( announce && ( userp != BADUSER ) && remoteMail )
               trumpet( userp->beep);  /* Yes --> Inform the user    */
            return delivered;

         } /* if */
      } /* if */

/*--------------------------------------------------------------------*/
/*             No system alias, verify the user is valid              */
/*--------------------------------------------------------------------*/

      if ( userp == BADUSER )    /* Invalid user id?                 */
      {                          /* Yes --> Dump in trash bin        */
         return Bounce( input,
                        "Invalid local address (not defined in PASSWD or ALIASES)",
                        user,
                        user,
                        validate );
      } /* if */

/*--------------------------------------------------------------------*/
/*               The user id validated; handle the mail               */
/*--------------------------------------------------------------------*/

      mkfilename(mboxname, userp->homedir, DOTFORWARD);

      if (access( mboxname, 0 )) /* The .forward file exists?        */
         announce = TRUE;        /* No --> Fall through              */
      else {
         delivered += DeliverFile( input,
                                   mboxname,
                                   0,
                                   LONG_MAX,
                                   &announce,
                                   userp,
                                   FALSE,
                                   validate,
                                   user );

         if (announce && remoteMail)   /* Did we deliver mail locally?        */
            trumpet( userp->beep);     /* Yes --> Inform the user       */
         return delivered;

      } /* if */

   } /* if (validate) */

/*--------------------------------------------------------------------*/
/*       The user is valid (or not validated) and not forwarded       */
/*--------------------------------------------------------------------*/

   if ((*user == '/') || (isalpha( *user ) && user[1] == ':'))
                              /* Absolute path from recursive call?   */
      strcpy(mboxname, user); /* Yes --> Use it as-is                 */
   else
      mkmailbox(mboxname, user);
                              /* No --> Build normal name             */

   printmsg(1,"Delivering mail %sfrom %s%s%s to %s",
                        stats( input ),
                        ruser,
                        remoteMail ? "@" : "",
                        remoteMail ? rnode : "",
                         user );

   if ( announce && remoteMail )
      trumpet( userp->beep);  /* Local delivery, inform the user     */

   mbox = FOPEN( mboxname , "a",TEXT_MODE );
   if (mbox == NULL )
   {
      printerr(mboxname);
      printmsg(0,"Cannot open mailbox \"%s\" for output",
                  mboxname);
      panic();
   }

   if (!isatty(fileno(mbox)))
      fputs(MESSAGESEP,mbox); /* Write separator line                 */

   return CopyData( FALSE, input , mbox );

} /* DeliverLocal */

/*--------------------------------------------------------------------*/
/*       D e l i v e r F i l e                                        */
/*                                                                    */
/*       Process a local or system aliases file                       */
/*--------------------------------------------------------------------*/

static int DeliverFile( const char *input,
                        const char *fwrdname,
                        const long start,
                        const long end,
                        boolean *announce,
                        struct UserTable *userp,
                        const boolean sysalias,  /* Already sys alias     */
                        const boolean validate,
                        const char *user )
{
   char buf[BUFSIZ];
   FILE *fwrd = FOPEN(fwrdname, "r",TEXT_MODE);
   char *cwd = sysalias ? E_tempdir : userp->homedir;
   int delivered = 0;

   if ( fwrd == NULL )
   {
      printerr( fwrdname );
      return Bounce( input,
                     "Cannot open forward file",
                     fwrdname,
                     user,
                     validate );
   }

   if ( start != 0 )
      fseek( fwrd, start, SEEK_SET);

   while((ftell(fwrd) < end) && (fgets( buf, BUFSIZ, fwrd) != NULL ))
   {
      char *s = buf;
      char c;
      char *nextfile = NULL;

      if ( buf[ strlen(buf) - 1 ]== '\n')
         buf[ strlen(buf) - 1 ] = '\0';

      while( *s && ! isgraph( *s ))    /* Trim leading white space      */
         s++;

      printmsg(8,"Forwarding to \"%s\"", s);
      if ( equalni( buf, INCLUDE, strlen(INCLUDE)))
      {
         nextfile = strtok( s + strlen(INCLUDE), WHITESPACE );
         if ( nextfile == NULL )
         {
            return Bounce(input,
                          "Missing forwarding file for alias",
                          fwrdname,
                          user,
                          validate );
         }
         else
            c = ':';
      } /* if */
      else if ( isalpha(*s ) && (s[1] == ':'))  /* Drive name?    */
         c = '/';             /* Yes --> flag as absolute path    */
      else if ( *s == ':')    /* Avoid false triggers ...         */
         c = ' ';             /* ... by making it general case    */
      else                    /* Handle other cases in switch ... */
         c = *s;

      switch(c)
      {
         case '#':
            break;            /* Comment, ignore            */

         case '\0':
            break;            /* Empty line, ignore         */

         case '|':               /* Pipe mail into a command   */
         {
            long here = ftell(fwrd);

            fclose(fwrd);
            PushDir( cwd );
            printmsg(1,"Piping mail%s from %s@%s for %s into %s",
                        stats( input ),
                        ruser,
                        rnode,
                        user,
                        s + 1 );

            executeCommand( s + 1, input, NULL, TRUE, FALSE );
            PopDir();
            delivered += 1;
            fwrd = FOPEN(fwrdname, "r",TEXT_MODE);
            fseek( fwrd, here, SEEK_SET);
            break;
         } /* case */

         case '\\':              /* Deliver without forwarding */
            delivered += Deliver( input, &s[1], TRUE, FALSE );
            *announce = TRUE;
            break;

         case ':':
         {
            char fname[FILENAME_MAX];
            strcpy( fname, nextfile);
            expand_path(nextfile, NULL, cwd, E_mailext);
            delivered += DeliverFile( input, nextfile, 0, LONG_MAX,
                                      announce, userp,
                                      FALSE, TRUE, user );
            break;
         }

         case '/':               /* Save in absolute path name */
         case '~':
            if (expand_path(s, NULL, cwd, E_mailext) == NULL )
            {
               return Bounce(input,
                             "Invalid path in forwarding file name",
                             s,
                             user,
                             validate );

            }
            else
               delivered += DeliverLocal( input, s, sysalias, FALSE );
            *announce = TRUE;
            break;

         default:                /* Deliver normally           */
              delivered += Deliver( input, s, sysalias, validate );
      } /* switch */
   } /* while */

   fclose( fwrd );

   return delivered;

} /* DeliverFile */

/*--------------------------------------------------------------------*/
/*    D e l i v e r G a t e w a y                                     */
/*                                                                    */
/*    Deliver mail via a gateway program                              */
/*--------------------------------------------------------------------*/

static size_t DeliverGateway(   const char *input,
                                const char *user,
                                const char *node,
                                const struct HostTable *hostp,
                                const boolean validate )
{
   char command[BUFSIZ];
   int rc;

/*--------------------------------------------------------------------*/
/*    Format the command and tell the user what we're going to do     */
/*--------------------------------------------------------------------*/

   sprintf(command , "%s %s %s %s",
                     hostp->via,          /* Program to perform forward */
                     hostp->hostname,     /* Nominal host routing via   */
                     node ,               /* Final destination system   */
                     user );              /* user on "node" for delivery*/

   printmsg(3,"DeliverGateway: %s", command);

   printmsg(1,
      "Gatewaying mail %sfrom %s@%s to %s@%s via %s using \"%s\"",
       stats( input ),
       ruser, rnode, user, node, hostp->hostname, hostp->via);

/*--------------------------------------------------------------------*/
/*  Run the command and return caller with count of mail delivered    */
/*--------------------------------------------------------------------*/

   rc = executeCommand( command, input, NULL, TRUE, FALSE );

   if ( rc == 0 )
      return 1;
   else {
      char who[MAXADDR];

      sprintf( who, "%s@%s", user, node );
      return Bounce( input,
                     "Gateway command returned non-zero exit status",
                     command,
                     who,
                     validate );
   } /* else */

} /* DeliveryGateway */

/*--------------------------------------------------------------------*/
/*    D e l i v e r R e m o t e                                       */
/*                                                                    */
/*    Queue mail for delivery on another system via UUCP              */
/*--------------------------------------------------------------------*/

static size_t DeliverRemote( const char *input, /* Input file name    */
                    const char *address,  /* Target address           */
                    const char *path)
{
   static char *spool_fmt = SPOOLFMT;              /* spool file name */
   static char *dataf_fmt = DATAFFMT;
   static char *send_cmd  = "S %s %s %s - %s 0666\n";
   static long seqno = 0;
   static char *SavePath = NULL;
   FILE *stream;              /* For writing out data                */
   static char everyone[500]; /* 512, with room for "rmail "         */

   char msfile[FILENAME_MAX]; /* MS-DOS format name of files         */
   char msname[22];           /* MS-DOS format w/o path name         */

   char tmfile[15];           /* Call file, UNIX format name         */
   static char ixfile[15];    /* eXecute file for remote system,
                                UNIX format name for local system   */
   static char idfile[15];    /* Data file, UNIX format name         */
   static char rdfile[15];    /* Data file name on remote system,
                                 UNIX format                         */
   static char rxfile[15];    /* Remote system UNIX name of eXecute
                                 file                                */

   printmsg(1,"Spooling mail %sfrom %s%s%s to %s via %s",
               stats( input ),
               ruser,
               remoteMail ? "@" : "",
               remoteMail ? rnode : "",
               address ,
               path);

/*--------------------------------------------------------------------*/
/*          Create the UNIX format of the file names we need          */
/*--------------------------------------------------------------------*/

   if ((seqno == 0) ||
       (SavePath == NULL) ||
       !equal(SavePath, path) ||
       ((int) (strlen(everyone) + strlen(address) + 2) > (int) sizeof everyone))
   {
      char *seq;
      seqno = getseq();
      seq = JobNumber( seqno );

      if  (SavePath != NULL )
      {
         free(SavePath);
         SavePath = NULL;
      } /* if */

      sprintf(tmfile, spool_fmt, 'C', path,     'C' , seq);
      sprintf(idfile, dataf_fmt, 'D', E_nodename , seq, 'd');
      sprintf(rdfile, dataf_fmt, 'D', E_nodename , seq, 'r');
      sprintf(ixfile, dataf_fmt, 'D', E_nodename , seq, 'e');
      sprintf(rxfile, dataf_fmt, 'X', E_nodename , seq, 'r');
      strcpy(everyone,address);

   } /* if */
   else {
      strcat(everyone," ");
      strcat(everyone,address);
   } /* else */

/*--------------------------------------------------------------------*/
/*                     create remote X (xqt) file                     */
/*--------------------------------------------------------------------*/

   importpath( msname, ixfile, path);
   mkfilename( msfile, E_spooldir, msname);

   stream = FOPEN(msfile, "w", BINARY_MODE);
   if ( stream == NULL )
   {
      printerr(msfile);
      printmsg(0, "DeliverRemote: cannot open X file %s", msfile);
      return 0;
   } /* if */


   fprintf(stream, "R %s@%s\nU %s %s\nF %s\nI %s\nC rmail %s\n",
               ruser, rnode, uuser , E_nodename,
               rdfile, rdfile, everyone);
   fclose(stream);

   if (SavePath != NULL)
      return 1;

/*--------------------------------------------------------------------*/
/*  Create the data file with the mail to send to the remote system   */
/*--------------------------------------------------------------------*/

   importpath(msname, idfile, path);
   mkfilename( msfile, E_spooldir, msname);

   stream = FOPEN(msfile, "w", BINARY_MODE);
   if (stream == NULL )
   {
      printerr(msfile);
      printmsg(0,
               "DeliverRemote: Cannot open spool file \"%s\" for output",
                msfile);
      return 0;
   }

   if (!CopyData( TRUE, input , stream ))
   {
      remove( msfile );
      return 0;
   }

/*--------------------------------------------------------------------*/
/*                     create local C (call) file                     */
/*--------------------------------------------------------------------*/

   importpath( msname, tmfile, path);
   mkfilename( msfile, E_spooldir, msname);

   stream = FOPEN(msfile, "w",TEXT_MODE);
   if (stream == NULL)
   {
      printerr( msname );
      printmsg(0, "DeliverRemote: cannot open C file %s", msfile);
      return 0;
   }

   fprintf(stream, send_cmd, idfile, rdfile, uuser, idfile);
   fprintf(stream, send_cmd, ixfile, rxfile, uuser, ixfile);
   fclose(stream);

   if (bflag[F_MULTI])        /* Deliver to multiple users at once?  */
      SavePath = strdup(path);   /* Yes --> Save routing info        */

   return 1;
} /* DeliverRemote */

/*--------------------------------------------------------------------*/
/* C o p y D a t a                                                    */
/*                                                                    */
/* Copy data into its final resting spot                              */
/*--------------------------------------------------------------------*/

static int CopyData( const boolean remotedelivery,
                     const char *input,
                     FILE *dataout)
{
   FILE *datain = FOPEN(input, "r",TEXT_MODE);
   char buf[BUFSIZ];
   int column = 0;
   boolean success = TRUE;

   int (*put_string) (char *, FILE *) = (int (*)(char *, FILE *)) fputs;
                              /* Assume no Kanji translation needed  */

/*--------------------------------------------------------------------*/
/*                      Verify the input opened                       */
/*--------------------------------------------------------------------*/

   if (datain == NULL)
   {
      printerr(input);
      printmsg(0,"Unable to open input file \"%s\"", input);
      fclose(dataout);
      return 0;
   } /* datain */

/*--------------------------------------------------------------------*/
/*    When we do the From line, we also determine if we must          */
/*    translate the data.  Note that the default is initialized to    */
/*    fputs() above.                                                  */
/*                                                                    */
/*    If Kanji is not enabled, don't translate it                     */
/*                                                                    */
/*    If local mail queued for local delivery, the data is already    */
/*    in Shift JIS, so don't translate it.                            */
/*                                                                    */
/*    If remote mail is queued for remote delivery, the data is       */
/*    already in JIS 7bit, so don't translate it.                     */
/*                                                                    */
/*    If delivering remote mail locally, translate to Shift JIS       */
/*                                                                    */
/*    If delivering local mail remotely, translate to JIS 7 bit       */
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/
/*                        Generate a FROM line                        */
/*--------------------------------------------------------------------*/

   switch( (int) remoteMail * 2 + (int) remotedelivery )
   {
      case 3:                 /* Remote sender, remote delivery      */
         strcpy( buf, fromuser );
         strtok( buf, "!");   /* Get first host in list              */

         if ( equal(HostAlias( buf ), fromnode ))
                              /* Host already in list?               */
         {                    /* Yes --> Don't do it twice           */
            fprintf(dataout, "From %s %s remote from %s\n",
                     fromuser, now, E_nodename);
            break;
         }
         else {                /* No --> Insert it                    */
            fprintf(dataout, "From %s!%s %s remote from %s\n",
                     fromnode, fromuser, now, E_nodename);
            break;
         }

/*--------------------------------------------------------------------*/
/*    Note:  For the Kanji translation we re-check the                */
/*    remoteDelivery flag since we do the fall through from above.    */
/*--------------------------------------------------------------------*/

      case 2:                 /* Remote sender, local delivery       */
         if ( bflag[ F_KANJI ] )
                              /* Kanji from remote node?             */
            put_string = (int (*)(char *, FILE *)) fputs_shiftjis;
                              /* Yes --> Translate it                */

         fprintf(dataout, "From %s %s remote from %s\n",
                  fromuser, now, fromnode);
         break;

      case 1:                 /* Local sender, remote delivery       */
         if ( bflag[F_KANJI]) /* Translation enabled?                */
            put_string = (int (*)(char *, FILE *)) fputs_jis7bit;
                              /* Translate into 7 bit Kanji          */

         column = strlen(E_domain) - 5;
         if ((column > 0) && equali(&E_domain[column],".UUCP"))
                              /* UUCP domain?                        */
            fprintf(dataout, "From %s %s remote from %s\n",
                             fromuser, now, E_nodename);
                              /* Yes --> Use simple address          */
         else
            fprintf(dataout, "From %s!%s %s remote from %s\n",
                  E_domain, fromuser, now, E_nodename);
                              /* No --> Use domain address           */
         break;

      case 0:                 /* Local sender, local delivery        */
         fprintf(dataout, "From %s %s\n", fromuser, now);
         break;

   } /* switch */

/*--------------------------------------------------------------------*/
/*                       Loop to copy the data                        */
/*--------------------------------------------------------------------*/

   while (fgets(buf, BUFSIZ, datain) != NULL)
   {
      if ((*put_string)(buf, dataout) == EOF)     /* I/O error? */
      {
         printerr("output");
         printmsg(0,"I/O error on \"%s\"", "output");
         fclose(dataout);
         return 0;
      } /* if */
   } /* while */

/*--------------------------------------------------------------------*/
/*                      Close up shop and return                      */
/*--------------------------------------------------------------------*/

   if (ferror(datain))        /* Clean end of file on input?         */
   {
      printerr(input);
      clearerr(datain);
      success = FALSE;
   }

   fclose(datain);
   fclose(dataout);
   return success;

} /* CopyData */

/*--------------------------------------------------------------------*/
/*       b o u n c e                                                  */
/*                                                                    */
/*       Report failed mail to a user.  Based on code contributed     */
/*       by Kevin Meyer <kmeyer@sauron.alt.za>                        */
/*                                                                    */
/*       This code has a major hole in that the address it replies    */
/*       to is weak, really having been previously only been used     */
/*       for internal messages.  Perhaps the full address from the    */
/*       UUCP From line should be used.                               */
/*--------------------------------------------------------------------*/

size_t Bounce( const char *input,
               const char *text,
               const char *data,
               const char *address ,
               const boolean validate )
{
   FILE *newfile, *otherfile;
   char tname[FILENAME_MAX]; /* name of temporary file used */
   char buf[BUFSIZ];
   char sender[MAXADDR];

   boolean bounce = bflag[F_BOUNCE];

   sprintf(sender, "%s%s%s",
               ruser,
               remoteMail ? "@" : "",
               remoteMail ? rnode : "" );

    printmsg(0,"Bounce: Mail from %s for %s failed, %s: %s",
               sender,
               address,
               text,
               (data == NULL) ? "(no data)" : data );

/*--------------------------------------------------------------------*/
/*           Never bounce mail to a select list of user ids           */
/*--------------------------------------------------------------------*/

   if ( equali( ruser, "postmaster") ||
        equali( ruser, "uucp") ||
        equali( ruser, "root") ||
        equali( ruser, "mmdf") ||
        equali( ruser, "mailer-daemon"))
      bounce = FALSE;

   if ( ! bounce )
     return Deliver( input, E_postmaster, FALSE, validate );

   mktempname( tname , "TMP");  // Generate a temp file name

   if ((otherfile = FOPEN(input,"r", TEXT_MODE ))==NULL)
   {
       printerr( input );
       panic();
   };

   if ((newfile = FOPEN(tname, "w", TEXT_MODE ))==NULL)
   {
       printerr( tname );
       panic();
   };

   fprintf(newfile,
     "Dear %s,\n"
     "Your message for address <%s> could not be delivered at system\n"
     "%s (uucp node %s) for the following reason:\n\t\t%s.\n",
                  ruser,
                  address, E_domain, E_nodename, text );

   if ( data != NULL )
      fprintf(newfile,
             "The problem address or file in question was:  %s\n",
             data );

      fprintf(newfile,
              "\nA copy of the failed mail follows.\n\n"
              "Electronically Yours,\n"
              "%s %s UUCP mailer daemon\n",
              compilep, compilev );

    fputs("\n------ Failed Message Follows -----\n", newfile);

    while ( fgets(buf, sizeof buf, otherfile) != NULL)
      fputs(buf, newfile);

    fclose(newfile);
    fclose(otherfile);

/*--------------------------------------------------------------------*/
/*          Recursively invoke RMAIL to deliver our message           */
/*--------------------------------------------------------------------*/

   putenv("LOGNAME=uucp");

   sprintf( buf, "-w -F %s -s \"Failed mail for %.20s\" %s -c postmaster",
            tname,
            address,
            sender );

    if ( execute( myProgramName, buf, NULL, NULL, TRUE, FALSE ))
         DeliverLocal( input, E_postmaster, FALSE, validate);

    return (1);

} /* Bounce */


/*--------------------------------------------------------------------*/
/*    s t a t s                                                       */
/*                                                                    */
/*    Report size of file in message, if desired                      */
/*--------------------------------------------------------------------*/

static char *stats( const char *fname )
{
   if (bflag[ F_COLLECTSTATS ] )
   {
      long size;
      time_t ltime = stater(fname, &size);

      if ( ltime == -1 )
      {
         printerr( fname );
         return "(unknown size)";
      }
      else {
         static char buf[25];  /* "(nnnnnnn bytes) " */
                               /*  ....+....+....+.. */
         sprintf(buf,   "(%ld bytes) ",size );
         return buf;
      } /* else */
   } /* if */
   else
      return "";              /* Pretend we were never here       */

} /* stats */
