/*--------------------------------------------------------------------*/
/*    i m p o r t . c                                                 */
/*                                                                    */
/*    File name mapping routines for UUPC/extended                    */
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/
/*    Changes Copyright (c) 1989 by Andrew H. Derbyshire.             */
/*                                                                    */
/*    Changes Copyright (c) 1990-1993 by Kendra Electronic            */
/*    Wonderworks.                                                    */
/*                                                                    */
/*    All rights reserved except those explicitly granted by the      */
/*    UUPC/extended license agreement.                                */
/*--------------------------------------------------------------------*/

/*
 *    $Id: IMPORT.C 1.3 1993/04/11 00:31:31 dmwatt Exp $
 *
 *    $Log: IMPORT.C $
 *     Revision 1.3  1993/04/11  00:31:31  dmwatt
 *     Global edits for year, TEXT, etc.
 *
 * Revision 1.2  1992/11/22  21:06:14  ahd
 * Correct mapping of dos paths with trailing slashes
 *
 */

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "lib.h"
#include "import.h"
#include "arbmath.h"
#include "hostable.h"
#include "usertabl.h"
#include "security.h"

#define MAX_DIGITS 20         /* Number of digits for arb math */

/*--------------------------------------------------------------------*/
/*                    Internal function prototypes                    */
/*--------------------------------------------------------------------*/

#define min(x,y) (((x) < (y)) ? (x) : (y))

currentfile();

/*--------------------------------------------------------------------*/
/*                     Local function prototypes                      */
/*--------------------------------------------------------------------*/

static void ImportName( char *local, const char *canon, size_t charsetsize );

/*-------------------------------------------------------------------*/
/*                                                                   */
/*   i m p o r t p a t h                                             */
/*                                                                   */
/*   Convert a canonical name to a format the host can handle        */
/*                                                                   */
/*   These routines convert file name between canonical form, which  */
/*   is defined as a 'unix' style pathname, and the MS-DOS all       */
/*   uppercase "xxxxxxxx.xxx" format.                                */
/*                                                                   */
/*   If the canonical name does not have a path, that is the file is */
/*   destined for the local spool directory, we can assume the UNIX  */
/*   name will normally be in a format like this:                    */
/*                                                                   */
/*                                                                   */
/*       X.hostid#######            (Execute files)                  */
/*       C.hostid#######            (Call files)                     */
/*       D.hostid#######            (Data files)                     */
/*                                                                   */
/*   where "hostid" may be most, but not always all, of the local    */
/*   host or remote host (the file came from or is going to) and     */
/*   "######" can be any character valid for the UNIX file system.   */
/*   Note, however, that the routine has to be generic to allow for  */
/*   other file names to be placed in the spool directory without    */
/*   collisions.                                                     */
/*                                                                   */
/*   Avoiding collisions in the spool directory is important; when   */
/*   receiving files with mixed case names longer than 11            */
/*   characters, sooner or later a file name collision will occur.   */
/*                                                                   */
/*   We can also assume that only UUPC will see these names, which   */
/*   means we can transform the name using any method we choose, so  */
/*   long as the UUPC functions opening the file always call         */
/*   importpath, and that importpath is reducible (that is, two      */
/*   calls to importpath with the same argument always yield the     */
/*   same result).  Note that if end user really wanted the file in  */
/*   the spool directory, all he has to do is rename the file-- far  */
/*   better than losing the data because duplicate file names.       */
/*                                                                   */
/*   For these files, we map the name as follows:                    */
/*                                                                   */
/*   0 - If the name is a valid MS-DOS name, use it without changing */
/*                                                                   */
/*   1 - Begin the output name by inserting up to the first eight    */
/*       characters of the remote host name (followed by a slash) as */
/*       a subdirectory name.                                        */
/*                                                                   */
/*   2 - If the input name begins with an uppercase alphabetic       */
/*       character followed by a period, also insert the alphabetic  */
/*       (followed by a slash) to make this a second subdirectory.   */
/*       Then, move the logical start of the input name past the two */
/*       characters.                                                 */
/*                                                                   */
/*   3 - Determine the number of characters the local host and       */
/*       remote hosts have equal to the next characters of the input */
/*       name, up to a maximum of 8, and zero the lower of the two   */
/*       counts.  Then, step past the number of characters of the    */
/*       larger count.                                               */
/*                                                                   */
/*       For example, if the file name is X.keane22222 and the local */
/*       host name is kendra (2 characters match) and the remote     */
/*       host is keane1 (5 characters match), zero the number of     */
/*       characters matched by kendra, and make the new start of the */
/*       file name five characters further (at the first "2").       */
/*                                                                   */
/*   4 - Convert the remaining string using a base conversion, with  */
/*       the input character size being from ascii "#" to ascii "z"  */
/*       (88 characters) to the allowed set of characters in MS-DOS  */
/*       file names (charset, below, 52 characters).                 */
/*                                                                   */
/*   5 - Prepend to the string to be converted the length of the     */
/*       remote host added to the length of the local host           */
/*       multiplied by 8 (both lengths were computed in step 3,      */
/*       above).  The base conversion is also applied to this        */
/*       "character", we which know will be in the range 1-64.       */
/*                                                                   */
/*   6 - If the string created by steps 4 and 5 exceeds 8            */
/*       characters, insert a period after the eighth character to   */
/*       make it a valid MS-DOS file name.  If the string created by */
/*       steps 4 and 5 exceeds 11 characters, truncate the string by */
/*       using the first eight and last three characters.            */
/*                                                                   */
/*   7 - Append the string created in steps 4 through 6 to the path  */
/*       name created in steps 1 and 2.                              */
/*                                                                   */
/*   If the canonical name has a path, it is destined for an end     */
/*   user, so we should not radically transform it like we do for    */
/*   files in the spool directory.  Thus, if the canonical name has  */
/*   a path, mung the canonical file name as follows:                */
/*                                                                   */
/*   1 - skip any path from the canonical name                       */
/*                                                                   */
/*   2 - copy up to 8 character from the canonical name converting . */
/*       to _ and uppercase to lowercase.                            */
/*                                                                   */
/*   3 - if the name was longer than 8 character copy a . to the     */
/*       host name and then copy the up to three characters from     */
/*       the tail of the canonical name to the host name.            */
/*                                                                   */
/*   Note that this set of rules will cause a collision with names   */
/*   that only differ in case, but leaves the name in a recongizable */
/*   format for the user.                                            */
/*-------------------------------------------------------------------*/


void importpath(char *local, char const *canon, char const *remote)
{
   char *s, *out;
   size_t charsetsize;     /* Number of allowed characters in
                              MS-DOS file names                   */

   out = local;

/*--------------------------------------------------------------------*/
/*                       Verify our parameters                        */
/*--------------------------------------------------------------------*/

   if ( local == NULL )
      panic();
   if ( canon == NULL )
      panic();

/*--------------------------------------------------------------------*/
/*                      Define our character set                      */
/*--------------------------------------------------------------------*/

    if ( E_charset == NULL )
      E_charset = DOSCHARS;
    charsetsize = strlen( E_charset );

/*--------------------------------------------------------------------*/
/*                 Determine if spool file directory                  */
/*--------------------------------------------------------------------*/

   if ((s = strrchr(canon, '/')) == (char *)NULL)
   {                          /* File for spooling directory, use
                                 internal character set to avoid
                                 collisons                           */
      static size_t range =  UNIX_END_C - UNIX_START_C + 1;
                              /* Determine unique number characters in
                                 the UNIX file names we are mapping  */

      size_t remlen = min(HOSTLEN, strlen(remote));
                              /* Length of the remote name passed
                                 in, shortened below to number of
                                 characters matched in name          */
      size_t nodelen = min(HOSTLEN, strlen(E_nodename));
                              /* Length of the local host name,
                                 shortened below to number of
                                 characters matched in name          */
      size_t subscript = 0;   /* Value of UNIX character to be
                                 converted to MS-DOS character set   */
      char *next        = local + remlen;
      char tempname[FILENAME_MAX];
      unsigned char number[MAX_DIGITS];
                              /* Arbitary length number, for base
                                 conversions                        */

/*--------------------------------------------------------------------*/
/*                    Verify we have a remote name                    */
/*--------------------------------------------------------------------*/

   if ( remote == NULL )
      panic();

/*--------------------------------------------------------------------*/
/*    Put the host name (up to six characters) at the beginning of    */
/*    the MS-DOS file name as a sub-directory name.                   */
/*--------------------------------------------------------------------*/

      strncpy(local, remote, remlen);
      *next++ = '/';          /* Add in the sub-directory seperator  */
      s = (char *) canon;     /* Get the beginnging of the UNIX name */

/*--------------------------------------------------------------------*/
/*    Files in the spooling directory generally start with "D.",      */
/*    "C.", or "X."; strip off any upper case letter followed by a    */
/*    period into its own directory.                                  */
/*--------------------------------------------------------------------*/

      if ((s[0] >= 'A') && (s[0] <= 'Z') && (s[1] == '.'))
      {
         *next++ = *s;        /* Copy the input character            */
         *next++ = '/';       /* Add the sub-directory indicator too */
         s += 2;              /* Step input string past the copied
                                 data                                */
      }

      while( remlen > 0 )
      {
         if (equaln(remote,s,remlen))
            break;
         remlen--;
      }

      while( nodelen > 0 )
      {
         if (equaln(E_nodename,s,nodelen))
            break;
         nodelen--;
      }

      if (nodelen > remlen )
      {
         remlen = 0;
         s += nodelen;
      }
      else {
         nodelen = 0;
         s += remlen;
      }

      *next  = '\0';          /* Terminate first part of host string */

/*--------------------------------------------------------------------*/
/*       Create a binary number which represents our file name        */
/*--------------------------------------------------------------------*/

      for (subscript = 0; subscript < MAX_DIGITS; subscript++ )
         number[subscript] = 0;  /* Initialize number to zero        */

      add(number, nodelen + remlen * HOSTLEN, MAX_DIGITS);
                                 /* Append host name info to the
                                    front of the converted string    */

      while( (*s != '\0') && (*number == '\0'))
      {
         mult(number, range, MAX_DIGITS); /* Shift the number over   */
         add(number, *s++  - UNIX_START_C , MAX_DIGITS);
                                          /* Add in new low order    */
      } /* while */

/*-------------------------------------------------------------------*/
/*   We now have stripped off the leading x. and host name, if any;  */
/*   now, convert the remaining characters in the name by doing a    */
/*   range to charset base conversion.                               */
/*-------------------------------------------------------------------*/

      out = &tempname[FILENAME_MAX];
      *--out = '\0';          /* Terminate the string we will build  */

/*--------------------------------------------------------------------*/
/*         Here's the loop to actually do the base conversion         */
/*--------------------------------------------------------------------*/

      while(adiv( number, charsetsize, &subscript, MAX_DIGITS))
            *--out = E_charset[ subscript ];

/*--------------------------------------------------------------------*/
/*    The conversion is done; now squeeze it into an 11 character     */
/*    MS-DOS name with period.                                        */
/*--------------------------------------------------------------------*/

      ImportName( next, out, charsetsize);

   }
   else {         /* Not file for spooling directory, convert it  */

      char *in = (char *) canon;

/*--------------------------------------------------------------------*/
/*      Handle leading drive letter (ignore it, assuming valid)       */
/*--------------------------------------------------------------------*/

      if ( isalpha( *in ) && (in[1] == ':'))
      {
         *out++ = *in++;      /* The drive letter                    */
         *out++ = *in++;      /* The colon making it a driver letter */
      } /* if */

      if ( *in == '/' )       /* Absolute path name?                 */
         *out++ = *in++;      /* Yes, step past it                   */

      while( *in == '/')      /* Additional slashes?                 */
         in++;                /* Skip them,  they mean nothing       */

      s = strchr( in, '/' );  /* Get end of next path segment        */

/*--------------------------------------------------------------------*/
/*              Now convert each simple name in the path              */
/*--------------------------------------------------------------------*/

      while ( *in )
      {
         if ( s != NULL )
            *s = '\0';        /* Truncate input string to simple name */

         ImportName( out, in , charsetsize );

         if ( s == NULL )
            break;
         out = out + strlen( out );
         *out++ = *s++ = '/'; /* Restore path to input and output    */
         in = s;              /* Remember start of this simple name  */
         while( *in == '/')   /* Additional slashes?                 */
            in++;             /* Skip them,  they mean nothing       */
         s = strchr( in , '/' );
      }

   } /* else */

   printmsg( 3, "ImportPath: Mapped %s to %s", canon, local );

} /*importpath*/

/*--------------------------------------------------------------------*/
/*    I m p o r t N a m e                                             */
/*                                                                    */
/*    Translate a simple DOS name without the path                    */
/*--------------------------------------------------------------------*/

static void ImportName( char *local, const char *canon, size_t charsetsize )
{

   char *in = (char *) canon;
   char *out = local;
   size_t len = strlen( canon );
   size_t column;
   char *best_period = NULL;     /* Assume no prince charming         */

   if ( strchr(canon,'/') != NULL )
   {
      printmsg(0,"ImportName: Parameter error, not simple name: %s",
            canon);
      panic();
   }

   if ( len == 0 )
   {
      printmsg(0,"ImportName: Parameter error, zero length input");
      panic();
   }

/*--------------------------------------------------------------------*/
/*                 If a valid DOS name, use it as-is                  */
/*--------------------------------------------------------------------*/

   if (ValidDOSName( canon ))
   {
      strcpy( local, canon );
      return;
   }

/*--------------------------------------------------------------------*/
/*    If the dataset name has a period, use it.  The rule we          */
/*    follow is use the last period in the second through ninth       */
/*    characters, otherwise use the last period in the dataset        */
/*    name with the exception of leading period.                      */
/*                                                                    */
/*    In any case, we only copy up to eight characters for the        */
/*    dataset name and up to three characters for the extension.      */
/*--------------------------------------------------------------------*/

   for ( column = 1; (column < 9) && (in[column] != '\0') ; column++)
   {
      if ( in[column] == '.')
      {
         strncpy( out, in, column + 5 );
                                    /* Period, 3 char extension,
                                       and terminating \0            */
         best_period = &out[column];/* Remember output location of
                                       period in name                */

         if ( len > (column + 4) )  /* Need to trunc extension to 3? */
            strcpy( out + column + 1, in + len - 3 ); /* Yes         */

         break;
      } /*if */
   }  /* if */

/*--------------------------------------------------------------------*/
/*    No period in the first eight characters, search the rest of     */
/*    the name for the last period (unless period is very last        */
/*    character in the string).                                       */
/*--------------------------------------------------------------------*/

   if ( best_period == NULL )
   {

      strncpy( out , in , 8);
      best_period = strrchr( in+1 , '.');

      if ( (best_period != NULL) && (best_period[1] != '\0') )
      {
         strncpy( &out[8], best_period, 4 ); /* Plus period and 3
                                                in extension         */

         if ( strlen( best_period) > 4 )     /* Long Extension?      */
            out[12] = '\0';                  /* Yes --> Truncate     */

      } /* if */
      else {                  /* No periods at all, generate one
                                 if needed for long name          */

         if ( len > 8 )
         {
            out[8] = '.';
            strcpy(&out[9], in + max(8,(len - 3))  );
         } /* if ( len > 9 ) */

      } /* else */

      best_period = &out[8];              /* Remember location of
                                             period, okay if past
                                             end of string           */

   } /* if ( best_period == NULL ) */

/*--------------------------------------------------------------------*/
/*                Now, clean up any invalid characters                */
/*--------------------------------------------------------------------*/

   if ( out[ strlen( out ) - 1 ] == '.' ) /* Trailing period?        */
      out[ strlen( out ) - 1 ] = '\0';    /* Just truncate string    */

   while( *out != '\0')
   {
      int c ;
      if ( isupper( *out ))
         c = tolower( *out );
      else
         c = *out;

      if ((out != best_period) && (strchr( E_charset, c ) == NULL ))
      {
         if ( c > 'z' )
            c -= 62;
         else if ( c > 'Z' )
            c -= 36;
         else if ( c > '9' )
            c -= 10;
         *out = E_charset[ (c - UNIX_START_C) % charsetsize ];
      }

      out++;                    /* Step to next character         */
   } /* while( *out != '\0') */

/*--------------------------------------------------------------------*/
/*                   Report our results and return                    */
/*--------------------------------------------------------------------*/

   printmsg( 5,
            "ImportName: Mapped %s to %s", canon, local );

} /* ImportName */

/*--------------------------------------------------------------------*/
/*    V a l i d D O S N a m e                                         */
/*                                                                    */
/*    Validate an MS-DOS file name                                    */
/*--------------------------------------------------------------------*/

boolean ValidDOSName( const char *s)
{
   char *ptr;
   size_t len = strlen ( s );
   char tempname[FILENAME_MAX];

/*--------------------------------------------------------------------*/
/*                      Define our character set                      */
/*--------------------------------------------------------------------*/

    if ( E_charset == NULL )
      E_charset = DOSCHARS;

/*--------------------------------------------------------------------*/
/*                 Name must be 12 characters or less                 */
/*--------------------------------------------------------------------*/

   if (len > 12)
      return FALSE;

   strcpy( tempname, s);      /* Make a temp copy we can alter       */

/*--------------------------------------------------------------------*/
/*    Simple file name without extension must be eight chracters      */
/*    or less                                                         */
/*--------------------------------------------------------------------*/

   ptr = strrchr(tempname, '.');
   if (ptr == NULL)
   {
      if (len > 8)
         return FALSE;
   }

/*--------------------------------------------------------------------*/
/*          Period must be in second through ninth character          */
/*--------------------------------------------------------------------*/

   else {
      if ((ptr == tempname) || (ptr > &tempname[8]))
         return FALSE;

/*--------------------------------------------------------------------*/
/*             Extension must be three characters or less             */
/*--------------------------------------------------------------------*/

      if ( strlen( ptr ) > 4) /* Three characters plus the period?   */
         return FALSE;        /* No --> Too much                     */

/*--------------------------------------------------------------------*/
/*                          Only one period                           */
/*--------------------------------------------------------------------*/

      if (ptr != strchr(tempname, '.'))
         return FALSE;
   } /* else */

/*--------------------------------------------------------------------*/
/*                Must only be valid MS-DOS characters                */
/*--------------------------------------------------------------------*/

   strlwr( tempname );        /* Map into our desired character set  */
   if ( ptr != NULL )
      *ptr = 'x';             /* We've already accounted for the
                                 period, don't let it ruin our day   */

   if (strspn(tempname, E_charset ) == len)
   {
      printmsg(9,"ValidDOSName: \"%s\" is valid", s);
      return TRUE;
   }
   else
      return FALSE;

} /* ValidateDOSName */
