/*----------------------------------------------------------------------

            T H E    P I N E    M A I L   S Y S T E M

   Laurence Lundblade and Mike Seibel
   Networks and Distributed Computing
   Computing and Communications
   University of Washington
   Administration Building, AG-44
   Seattle, Washington, 98195, USA
   Internet: lgl@CAC.Washington.EDU
             mikes@CAC.Washington.EDU

   Please address all bugs and comments to "pine-bugs@cac.washington.edu"

   Date:
   Last Edited:
      
   Copyright 1989, 1990, 1991, 1992  University of Washington

    Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee to the University of
   Washington is hereby granted, provided that the above copyright notice
   appears in all copies and that both the above copyright notice and this
   permission notice appear in supporting documentation, and that the name
   of the University of Washington not be used in advertising or publicity
   pertaining to distribution of the software without specific, written
   prior permission.  This software is made available "as is", and
   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
   WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
   NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
   INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
   (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
   WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  

   Pine is in part based on The Elm Mail System:
    ***********************************************************************
    *  The Elm Mail System  -  $Revision: 2.13 $   $State: Exp $          *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
  ----------------------------------------------------------------------*/

/*======================================================================
      os-unx.c

 This contains most of Pine's interface to the local operating system
and hardware. Hopefully this file, os-xxx.h and makefile.xxx are the
only one that have to be modified for any port. signals.c, ttyin.c,
and ttyout.c also have some dependencies. See the doc/tech-notes for
notes on porting Pine to other platforms.


        File System Access
           can_access      -- See if a file can be accessed
           file_size       -- Return the number of bytes in the file
           is_writetable   -- Check to see id directory exists and is writable
           create_mail_dir -- what to do if there's not mkdir function
           create_folder   -- create an empty folder
           rename_file     -- replacement for rename system call
           build_path      -- Put together a file system path
           expand_foldername -- Expand a folder name to full path
           fnexpand        -- Do filename exansion for csh style "~"
           filter_filename -- Make sure file name hasn't got weird chars
           disk_space      -- Count up the disk space free for a given path
           disk_quota      -- Find out the users disk quota
           simple_opendir  -- Open a directory 
           simple_readdir  -- Read file names out of ope directory
           simple_closedir -- Close directory
           read_file       -- Read whole file into memory (for small files)

        Abort
           coredump        -- Abort running Pine dumping core if possible

        System Name and Domain
           gethostname     -- Figure out the systems host name, however
           gethostdomain   -- Figure out the systems domain name

        Berkeley UNIX Job Control
           have_job_control-- Returns 1 if job control exists
           stop_process    -- What to do to stop process when it's time to stop

        System Error Messages (in case given one is a problem)
           error_name      -- Return short string describing error
           error_description -- Return strings describing error

        System Password and Accounts
           get_system_login    -- Returns userid 
           get_system_fullname -- Returns full name 
           get_system_homedir  -- Returns users home directoru
           get_system_passwd   -- Returns enscrypted password
           gcos_name           -- Parses full name from system
           local_name_lookup   -- Get full name of user on system
           chagne_passwd       -- Calls system password change

  ====*/


#include "headers.h"
#include <pwd.h>
#include <sys/wait.h>
#include <sys/types.h> 
#include <sys/stat.h>
#include <fcntl.h>

#if (defined(DYN) || defined(SUN)) && defined(USE_QUOTAS)
char *device_name();
#endif





/*----------------------------------------------------------------------
       Check if we can access a file in a given way

   Args: filename  -- The file name to check
         mode      -- The mode ala the access() system call
 

 Result: returns 0 if the user can access the file
         errno if he can't. This routine used to fork of process and
         change uid and gid to make sure all went well. This was because
         elm, could be installed setuid. Pine cannot be. Maybe we'll need 
         more here some day.
 ----*/
int
can_access(file, mode)
char *file; 
int   mode;
{
	 return(access(file, mode));
}



/*----------------------------------------------------------------------
      Return the number of bytes in given file

    Args: name -- file name

  Result: the number of bytes in the file is returned or
          -1 on error in which case errno is valid
  ----------------------------------------------------------------------*/
long
file_size(name)
char *name;
{
    struct stat buffer;

    if (stat(name, &buffer) != 0)
        return(-1);
    return(buffer.st_size);
}



/*----------------------------------------------------------------------
      Check to see if a directory exists and is writable by us

   Args:  directory name

 Result:       returns 0 if it exists and is writable
                       1 if it is a directory, but is not writable
                       2 if it is not a directory
                       3 it doesn't exist.
  ----*/
is_writable_dir(file)
     char *file;
{
    struct stat sb;

   if(stat(file, &sb) < 0)
     /*--- It doesn't exist ---*/
     return(3);


   if(!(sb.st_mode & S_IFDIR))
     /*---- it's not a directory ---*/
     return(2);


   if(access(file, 07))
     return(1);
   else
     return(0);
}



/*----------------------------------------------------------------------
      Create the mail subdirectory.

  Args: dir -- Name of the directory to make
 
 Result: Directory is created.
  ----*/
create_mail_dir(dir)
     char *dir;
{
    if(mkdir(dir, 0700) < 0)
      return(-1);

    chmod(dir, 0700);
    /* Some systems need this, on others we don't care if it fails */
    chown(dir, getuid(), getgid());
    return(0);
}



/*----------------------------------------------------------------------
      Create a folder (create a specific named file)

   Args dir  --  Directory to create folder in
        file --  File name to create

 ----*/
create_folder(dir, file)
    char *dir, *file;
{
    /* this creates a new folders directory */
    char  fn[MAXPATH+1];
    int   x;

    if((dir == NULL ? 0 : strlen(dir)) + strlen(file) + 1 > MAXPATH) {
        q_status_message1(1, 2,4,
           "\007Error creating folder: full path name would be longer than %s",
                          int2string(MAXPATH));
    }
    build_path(fn, dir, file);
    x = creat(fn, 0600);
    if(x < 0) {
        q_status_message2(1, 2, 4,"\007Error creating \"%s\" : %s\n",
                          pretty_fn(fn), error_description(errno));
        return(-1);
    } else {
        close(x);
    }
    return(0);
}



/*----------------------------------------------------------------------
      Rename a file. This is mostly a replacement for the rename system call.
If the system call exists we just use it.
  ----*/

int
rename_file(tmpfname, fname)
char *tmpfname, *fname;
{
#ifdef     NO_KNOWN_SYSTEM_YET
	int status;

	(void) unlink(fname);
	if ((status = link(tmpfname, fname)) != 0)
		return(status);
	(void) unlink(tmpfname);
	return(0);
#else
        return(rename(tmpfname, fname));
#endif
}



/*----------------------------------------------------------------------
   Isolate the putting a path name together so other file systems like DOS can 
   be dealt with easily

   Args: pathbuf -- Buffer to return formatted path in. Better be big enough!
         first_part - First part of path to construct
         second_part - second part, usually the file name

BUGS this is a first stab at dealing fs naming dependencies, and others 
still exist
 ----*/
void
build_path(pathbuf, first_part, second_part)
     char *pathbuf, *first_part, *second_part;
{
    if(first_part == NULL)
      strcpy(pathbuf, second_part);
    else
      sprintf(pathbuf, "%s/%s", first_part, second_part);
}


char *
last_cmpnt(filename)
     char *filename;
{
    register char *p;

    for(p = filename + strlen(filename) -1; p > filename; p--){
        if(*p == '/' && *(p+1) != '\0')
          return(p+1);
    }
    return(p);
}
          


/*----------------------------------------------------------------------
        expand the given file name considering ~ and the folders_dir

   Args: filename  -- The file/path name to expand (in place)

 Result: returns 0 and q's status message if unsuccessful.
         Input string is over written with expanded name.
         Returns 1 if successful

BUG should limit length to MAXPATH
  ----*/
int
expand_foldername(filename)
     char *filename;
{
    char         temp_filename[MAXPATH+1];

    dprint(5, (debugfile, "=== expand_foldername called (%s) ===\n",filename));

    if(filename[0] != '{') {
        char *ill = filter_filename(filename);
        if(ill != NULL) {
            q_status_message1(1, 1,3, "%s in folder name", ill);
            return(0);
        }
    }

    strcpy(temp_filename, filename);
    if(strucmp(temp_filename, "inbox") == 0) {
        strcpy(filename, ps_global->VAR_INBOX_PATH == NULL ? "inbox" :
               ps_global->VAR_INBOX_PATH);
    } else if(temp_filename[0] == '{') {
        strcpy(filename, temp_filename);
    } else if(strindex("./~", temp_filename[0]) != NULL 
	      && ps_global->restricted){
	q_status_message(1, 1,3,"Pine demo only opens local folders");
	return(0);
    } else if(temp_filename[0] == '*') {
        strcpy(filename, temp_filename);
    } else if (temp_filename[0] == '~'){
        if(fnexpand(temp_filename, sizeof(temp_filename)) == NULL) {
            char *p = strindex(temp_filename, '/');
    	    if(p != NULL)
    	      *p = '\0';
    	    q_status_message1(1, 1,2,
                    "Error expanding folder name: \"%s\" unknown user",
    	       temp_filename);
    	    return(0);
        }
        strcpy(filename, temp_filename);
    } else if(temp_filename[0] == '/') {
        strcpy(filename, temp_filename);
    } else {
        sprintf(filename,"%s/%s", ps_global->folders_dir, temp_filename);
    }
    dprint(5, (debugfile, "returning \"%s\"\n", filename));    
    return(1);
}



struct passwd *getpwnam();

/*----------------------------------------------------------------------
       Expand the ~ in a file ala the csh (as home directory)

   Args: buf --  The filename to expand (nothing happens unless begins with ~)
         len --  The length of the buffer passed in (expansion is in place)

 Result: expanded string is returned using same storage as passed in
         if expansion fails, NULL is returned
 ----*/

char *
fnexpand(buf, len)
     char *buf;
     int len;
{
    struct passwd *pw;
    register char *x,*y;
    char name[20];
    
    if(*buf == '~') {
        for(x = buf+1, y = name; *x != '/' && *x != '\0'; *y++ = *x++);
        *y = '\0';
        if(x == buf + 1) 
          pw = getpwuid(getuid());
        else
          pw = getpwnam(name);
        if(pw == NULL)
          return(NULL);
        if(strlen(pw->pw_dir) + strlen(buf) > len) {
            return(NULL);
        }
        rplstr(buf, x - buf, pw->pw_dir);
    }
    return(len ? buf : NULL);
}



/*----------------------------------------------------------------------
    Filter file names for strange characters

   Args:  file  -- the file name to check
 
 Result: Returns NULL if file name is OK
         Returns formatted error message if it is not

  ----*/

char *
filter_filename(file)
     char *file;
{
#ifdef ALLOW_WEIRD
    static char illegal[] = {'\177', '\0'};
#else
    static char illegal[] = {'"', '#', '$', '%', '&', '\'','(', ')','*',
                          ',', ':', ';', '<', '=', '>', '?', '[', ']',
                          '\\', '^', '|', '\177', '\0'};
#endif
    static char error[100];
    char ill_file[MAXPATH+1], *ill_char, *ptr, e2[10];

    ptr = file;
    while (*ptr == ' ') ptr++;	/* leading spaces GONE! */

    if(*ptr == '*')
      ptr++; /* Allow * at beginning for news groups */

    while(*ptr && *ptr > ' ' && strindex(illegal, *ptr) == 0)
      ptr++;

    if(*ptr != '\0') {
        if(*ptr == ' ') {
            ill_char = "<space>";
        } else if(*ptr == '\n') {
            ill_char = "<newline>";
        } else if(*ptr == '\r') {
            ill_char = "<carriage return>";
        } else if(*ptr == '\t') {
    	    ill_char = "<tab>";
        } else if(*ptr < ' ') {
            sprintf(e2, "control-%c", *ptr + '@');
            ill_char = e2;
        } else if (*ptr == '\177') {
    	    ill_char = "<del>";
        } else {
    	    e2[0] = *ptr;
    	    e2[1] = '\0';
    	    ill_char = e2;
        }
        if(ptr != file) {
            strncpy(ill_file, file, ptr - file);
            ill_file[ptr - file] = '\0';
            sprintf(error,"Character \"%s\" after \"%s\" not allowed",ill_char,
                    ill_file);
        } else {
            sprintf(error,
                    "First character, \"%s\", not allowed",
                    ill_char);
        }
            
        return(error);
    }
    return(NULL);
}
        


FILE *fdopen();

/*----------------------------------------------------------------------
       find how much free disk space is on a named paritition

    Args: The name of the device or name of file on which device of concern is
 
  Result: returns number of K bytes of free space

   Return the free space on the named disk in 1K blocks
  by running df(1). This is of course sensitive to the output of df
  which changes from one UNIX to another. We try to avoid this by
  looking for the first line beginning with a '/' which is the
  first line that's not header. (The main variation is header length)
  -----*/

disk_space(device_name)
     char *device_name;
{
    int            pipe_ends[2], pid, child;
    int            n;
#ifdef DPX
    int            stx;
#else
    union wait     stx; 
#endif
    FILE          *df_s;
    register char *p;
    register char *q;
    char           buf[1000];

    /*---- The number of lines to skip in the header -----*/
#ifdef ULT
    n = 2;
#else
    n = 1;
#endif


    if(pipe(pipe_ends)< 0) {
        perror("pipe");
        return(-1);
    }

    if((child = fork()) == 0) {
	close(1);
	dup2(pipe_ends[1], 1);
	if(execl("/bin/df","df", device_name, NULL) < 0)
	  perror("exec df");
    }

    close(pipe_ends[1]);
    df_s = fdopen(pipe_ends[0], "r");
    while(n >= 0) {
	if(fgets(buf, sizeof(buf), df_s) == NULL)
	  return(-2);
	n--;
    }
    
    dprint(8, (debugfile, "DF output, --%s--\n", buf));
    /* Skip the filesystem */
    p = buf;
    for(;*p != ' '; p++);
    for(;*p == ' '; p++);
    /* Skip over the Kbytes field */
    for(;*p != ' '; p++);
    for(;*p == ' '; p++);
    /* Skip over used field */
#ifndef AIX370
    for(;*p != ' '; p++);
    for(;*p == ' '; p++);
#endif

    for(q = p;*q != ' ';q++);
    *q = '\0';


    pid = 0;
    while(pid != child)
        pid = wait(&stx);

    fclose(df_s);
    dprint(8, (debugfile, "Disk ready for atoi  --%s--\n", p));
    return(atoi(p));
}


    
#if defined(USE_QUOTAS)

#if defined(DYN) || defined(SUN)
#include <ufs/quota.h>
#else
#include <sys/stat.h>    
#include <sys/param.h>
#include <sys/quota.h>
#endif

/*----------------------------------------------------------------------
   Return space left in disk quota on file system given files is in

    Args: path - Path name of file or directory on file system of concern
                   (any file will do)
          over - pointer to flag that is set if the user is over quota


 Returns: The number of bytes free in disk quota as per the soft limit.
          -1 is returned on an error looking up quota
           0 is returned if there is no quota

BUG:  If there's more than 2.1Gb free this function will break
  ----*/
long
disk_quota(path, over)
     char *path;
     int  *over;
{
    static int   no_quota = 0;
    struct stat  statx;
/*    struct dqblk quotax; ### */
    long         q;
#if defined(DYN) || defined(SUN)
    char        *dname;
#endif

    if(no_quota)
      return(0); /* If no quota the first time, then none the second,
                    Also Ultrix seems to give the wrong answer the second
                    time */

    dprint(5, (debugfile, "quota_check path: %s\n", path));
    if(stat(path, &statx) < 0) {
        return(-1);
    }

    *over = 0;
    errno = 0;
#ifdef NXT
    return(0); /* No quotas on the next, at least no documented quotas */
#else
#if !defined(DYN) && !defined(SUN)

    dprint(7, (debugfile, "Quota check: UID:%d  stat: %d %x\n", 
           getuid(), statx.st_dev, statx.st_dev));
    if(quota(Q_GETDLIM, getuid(), statx.st_dev, &quotax) < 0) {
        dprint(5, (debugfile, "Quota failed : %s\n",
                   error_description(errno)));
        if(errno == ESRCH){
            no_quota = 1;
            return(0); /* No limit */
        } else {
            return(-1); /* Some thing went wrong */
        }
    }

    dprint(5,(debugfile,"Quota: bsoftlimit:%d  bhardlimit:%d  curblock:%d\n",
          quotax.dqb_bsoftlimit, quotax.dqb_bhardlimit, quotax.dqb_curblocks));

    /* Some confusion on the type of bsoftlimit. The include file says
       unsigned, but -1 seems to indicate no quota */
    if(quotax.dqb_bsoftlimit == 0 || (long)quotax.dqb_bsoftlimit == -1) {
        no_quota = 1;
        return(0);
    }

    q = (quotax.dqb_bsoftlimit - quotax.dqb_curblocks) * 1024;
#else 
    dname = device_name(statx.st_dev);
    if(dname == NULL)
      return(-1);

    dprint(7, (debugfile, "Quota check: UID:%d  device: %s\n", 
           getuid(), dname));
    if(quotactl(Q_GETQUOTA, dname, getuid(), &quotax) < 0) {
        dprint(5, (debugfile, "Dynix3 Quota failed : %s\n",
                   error_description(errno)));
        return(-1); /* Some thing went wrong */
    }

    dprint(5,(debugfile,"Quota: bsoftlimit:%d  bhardlimit:%d  curblock:%d\n",
          quotax.dqb_bsoftlimit, quotax.dqb_bhardlimit, quotax.dqb_curblocks));

    if(quotax.dqb_bsoftlimit == -1)
      return(-1);

    q = (quotax.dqb_bsoftlimit - quotax.dqb_curblocks) * 512;    
#endif
    if(q < 0) {
        q = -q;
        *over = 1;
    }
    dprint(5, (debugfile, "disk_quota returning :%d,  over:%d\n", q, *over));
    return(q);
#endif  /* NXT */
}



#if   defined(DYN) || defined(SUN)

/*----------------------------------------------------------------------
 *		devNumToName
 *
 *	This routine is here so that ex can get a device name to check
 *	disk quotas.  One might wonder, why not use getmntent(), rather
 *	than read /etc/mtab in this crude way?  The problem with getmntent
 *	is that it uses stdio, and ex/vi pointedly doesn't.
 ----*/

static  char
*device_name(st_devArg)
     dev_t st_devArg;
{
#define MTABNAME "/etc/mtab"
    char *mtab;
    int tableUnit = -1;
    static char devName[48];
    static char *answer = (char *) 0;
    struct stat devStat;
    static dev_t st_dev;
    int nb, cur, bol;
    char c;
    int dname;

    if (st_devArg == st_dev)
      return answer;

    mtab = read_file(MTABNAME);
    if(mtab == NULL)
      return(NULL);

    /* Initialize save data. */
    st_dev = st_devArg;
    answer = (char *) 0;
    nb = strlen(mtab);

    for (cur=bol=0, dname=1; cur < nb; ++cur) {

	if (dname && (mtab[cur] <= ' ')) {
	/*	Space, tab or other such character has been found,
		presumably marking the end of the device name string. */
	
	    dname = 0;
	    c = mtab[cur];	/* Save current character. */
	    mtab[cur] = 0;	/* C zero-terminated string. */

	    /*	Get device number, via stat().  If it's the right
		number, copy the string and return its address. */
	    if (stat (&mtab[bol], &devStat) == 0) {
		if (devStat.st_rdev == st_dev) {
		    if ((cur - bol + 1) < sizeof (devName)) {
			strcpy (devName, &mtab[bol]);
                        answer = &devName[0];
			return(answer);
		    }
		}
	    }
	    mtab[cur] = c;
	}
	if (mtab[cur] == '\n') {
	    dname = 1;
	    bol = cur + 1;
	}
    }
    answer = NULL;

    return(answer);
}

#endif /*DYN */
#endif /* USE_QUOTAS */




/*----------------------------------------------------------------------
      Very simple, minimal and machine independent directory access
  
  This just allows opening of one directory at a time and getting the 
files in that directory.
 ----*/

#ifdef DPX
#include <dirent.h>
#else
#include <sys/dir.h>
#endif


static DIR *directory;

/*----------------------------------------------------------------------
     Open a directory for subsequent reading of filenames within  

    Args: dir -- The name of the directory to open

  Result: 0 is successful, -1 if not
  ---*/
simple_opendir(dir)
     char *dir;
{
    directory = opendir(dir);
    if(directory == NULL)
      return(-1);
    return(0);
}

/*----------------------------------------------------------------------
    Returns the next file name in the directory opened by simple_opendir

     Args:  

  Returns: string with name of file or NULL
 ----*/
char *
simple_readdir()
{
#if defined(DPX) || defined(AIX) 
    register struct dirent    *dp;
#else
    register struct direct    *dp;
#endif

    if(directory == NULL)
      return(NULL);
    dp = readdir(directory);
    if(dp == NULL)
      return(NULL);
    return(dp->d_name);
}

/*----------------------------------------------------------------------
   Close the currently open directory  
 ----*/
void
simple_closedir()
{
    closedir(directory);
}





/*----------------------------------------------------------------------
    Read whole file into memory

  Args: filename -- path name of file to read

  Result: Returns pointer to malloced memory with the contents of the file
          or NULL

This won't work very well if the file has NULLs in it and is mostly
intended for fairly small text files.
  ----------------------------------------------------------------------*/
char *
read_file(filename)
     char *filename;
{
    int         fd;
    struct stat statbuf;
    char       *buf;

    fd = open(filename, O_RDONLY);
    if(fd < 0)
      return(NULL);

    fstat(fd, &statbuf);

    buf = fs_get(statbuf.st_size + 1);

    if(read(fd, buf, statbuf.st_size) != statbuf.st_size) {
        close(fd);
        return(NULL);
    }
    close(fd);

    buf[statbuf.st_size]= '\0';

    return(buf);
}








/*----------------------------------------------------------------------
     Abort with a core dump

  Some systems can't abort() from a signal handler if the signal handler installed catchs the signal abort() uses to actually to the abort. That is even resetting the signal default doesn't occur until the program leaves the handler routine. This winds up causing infinite loops. To avoid this we just never catch SIGFPE which is assumed to be very rare in Pine and just kill ourselves with it when we want to abort with a core dump.
 ----*/
void
coredump()
{
#if  defined(ULT) || defined(DYN) || defined(SUN)
    kill(0, SIGFPE);
    exit(0); /* Just in case */
#else
    abort();
#endif
}
    







#ifdef NO_SYSTEM_KNOWN_YET
/*----------------------------------------------------------------------
       Supplimentary gethostname if the system doesn't have one

  Args: hostname -- buffer to return host name in 
        size     -- Size of buffer hostname is to be returned in
 Result:
  ----------------------------------------------------------------------*/
static 
gethostname(hostname,size) 
     char *hostname;
     int size;
{
	/** Return the name of the current host machine. **/

#ifdef SYSTEMID
	char    buf[32];
	FILE    *fp;
	char    *p;

	if ((fp = fopen("/etc/systemid", "r")) != 0) {
	  fgets(buf, sizeof(buf) - 1, fp);
	  fclose(fp);
	  if ((p = strindex(buf, '\n')) != NULL)
	    *p = '\0';
	  (void) strncpy(hostname, buf, size - 1);
	  hostname[size - 1] = '\0';
	  return 0;
	}

#else   /* SYSTEMID */

#ifdef DOUNAME
	/** 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);
#else
	(void) strncpy(hostname, HOSTNAME, size-1);
#endif	/* DOUNAME */

	hostname[size - 1] = '\0';
	return 0;

#endif  /* XENIX */
}
#endif



#include <netdb.h>

/*----------------------------------------------------------------------
       Get the current host and domain names

    Args: hostname   -- buffer to return the hostname in
          hsize      -- size of buffer above
          domainname -- buffer to return domain name in
          dsize      -- size of buffer above

  Result: The system host and domain names are returned. If the full host
          name is akbar.cac.washington.edu then the hostname is
          cac.washington.edu.

On Internet connected hosts this look up uses /etc/hosts and DNS to
figure all this out. On other less well connected machines some other
file may be read. If there is no notion of a domain name the domain
name maybe left blank. On a PC where there really isn't a host name
this should return blank strings. The .pinerc will take care of
configuring the domain names. That is this should only return the
native systems idea of what the names are if the system has such

The unused routine above might provide some clues for Xenix and such.
 ----*/
void
getdomainnames(hostname, hsize, domainname, dsize)
     char *hostname, *domainname;
     int   hsize, dsize;
{
    char           *dn, hname[MAX_ADDRESS+1];
    struct hostent *he;

    gethostname(hname, MAX_ADDRESS);

    he = gethostbyname(hname);

    if(he == NULL && strlen(hname) == 0) {
        strcpy(hostname, "");
    } else if(he == NULL) {
        strncpy(hostname, hname, hsize - 1);
    } else {
        strncpy(hostname, he->h_name, hsize-1);
    }
    hostname[hsize-1] = '\0';


    if((dn = strindex(hostname, '.')) != NULL) {
        strncpy(domainname, dn+1, dsize-1);
    } else {
        strncpy(domainname, hostname, dsize-1);
    }
    domainname[dsize-1] = '\0';

}









/*----------------------------------------------------------------------
     This routine returns 1 if jobs control SIGTSTP / ^Z is available
  ----*/
have_job_control()
{
    return(1);
}
  


/*----------------------------------------------------------------------
    If we don't have job control, this routine is never called.
  ----*/
stop_process()
{
    kill(0, SIGSTOP); 
}








/*----------------------------------------------------------------------
   This is here in case errno on your system is so brain damaged that it's 
 unusuable. Hopefully it'll get very little use.


    This routine maps error numbers to error names and error messages.
    These are all directly ripped out of the include file errno.h, and
    are HOPEFULLY standardized across the different breeds of Unix!!

    If (alas) yours are different, you should be able to use awk to
    mangle your errno.h file quite simply...

**/

extern char *sys_errlist[];

#ifdef NO_KNOWN_SYSTEM_WITHOUT_ERRNO_YET

char *err_name[] = { 
/* 0 */	        "NOERROR", "No error status currently",
/* 1 */		"EPERM",   "Not super-user",
/* 2 */		"ENOENT",  "No such file or directory",
/* 3 */		"ESRCH",   "No such process",
/* 4 */		"EINTR",   "Interrupted system call",
/* 5 */		"EIO",     "I/O error",
/* 6 */		"ENXIO",   "No such device or address",
/* 7 */		"E2BIG",   "Arg list too long",
/* 8 */		"ENOEXEC", "Exec format error",
/* 9 */		"EBADF",   "Bad file number",
/* 10 */	"ECHILD",  "No children",
/* 11 */	"EAGAIN",  "No more processes",
/* 12 */	"ENOMEM",  "Not enough core",
/* 13 */	"EACCES",  "Permission denied",
/* 14 */	"EFAULT",  "Bad address",
/* 15 */	"ENOTBLK", "Block device required",
/* 16 */	"EBUSY",   "Mount device busy",
/* 17 */	"EEXIST",  "File exists",
/* 18 */	"EXDEV",   "Cross-device link",
/* 19 */	"ENODEV",  "No such device",
/* 20 */	"ENOTDIR", "Not a directory",
/* 21 */	"EISDIR",  "Is a directory",
/* 22 */	"EINVAL",  "Invalid argument",
/* 23 */	"ENFILE",  "File table overflow",
/* 24 */	"EMFILE",  "Too many open files",
/* 25 */	"ENOTTY",  "Not a typewriter",
/* 26 */	"ETXTBSY", "Text file busy",
/* 27 */	"EFBIG",   "File too large",
/* 28 */	"ENOSPC",  "No space left on device",
/* 29 */	"ESPIPE",  "Illegal seek",
/* 30 */	"EROFS",   "Read only file system",
/* 31 */	"EMLINK",  "Too many links",
/* 32 */	"EPIPE",   "Broken pipe",
/* 33 */	"EDOM",    "Math arg out of domain of func",
/* 34 */	"ERANGE",  "Math result not representable",
/* 35 */	"ENOMSG",  "No message of desired type",
/* 36 */	"EIDRM",   "Identifier removed"
	};
#endif

char *strcpy();

/*----------------------------------------------------------------------
      Return short error name string for error number
 
   Args: errnumber -- The system error number (errno)

 Result: error name
  ----*/
char *error_name(errnumber)
int errnumber;
{
    static char buffer[50];
#ifdef NO_KNOWN_SYSTEM_WITHOUT_ERRNO_YET
    if (errnumber < 0 || errnumber > 36) 
#endif
      sprintf(buffer,"ERR-UNKNOWN (%d)", errnumber);
#ifdef NO_KNOWN_SYSTEM_WITHOUT_ERRNO_YET
    else
      strcpy(buffer, err_name[2*errnumber]);
#endif

    return( (char *) buffer);
}



/*----------------------------------------------------------------------
       return string describing the error

   Args: errnumber -- The system error number (errno)(

 Result:  sentence long string describing the error 
  ----*/

char *error_description(errnumber)
int errnumber;
{
    static char buffer[50];

#ifdef NO_KNOWN_SYSTEM_WITHOUT_ERRNO_YET
    if (errnumber < 0 || errnumber > 36) 
      sprintf(buffer,"Unknown error - %d - No description", errnumber);
    else
      strcpy(buffer, err_name[2*errnumber + 1]);
#else
    strcpy(buffer, sys_errlist[errnumber]);
#endif

    return ( (char *) buffer);
}








/*----------------------------------------------------------------------
  This collection of routines looks up the login name and password on the
system. For things like PC's it's OK for these to return NULL system there
is no system login. Other code will figure out who the user actually is.
  ----*/
static struct passwd *unix_pwd = NULL;
static int            pwd_looked_up = 0;

#ifdef ANSI
static char *gcos_name(char *, char *);
#else
static char *gcos_name();
#endif

char *
get_system_login()
{
    if(unix_pwd == NULL) {
        if(!pwd_looked_up) {
            unix_pwd = getpwuid(getuid());
            pwd_looked_up = 1;
        }
        if(unix_pwd == NULL)
          return(NULL);
    }
    return(unix_pwd->pw_name);
}

char *
get_system_fullname()
{
    if(unix_pwd == NULL) {
        if(!pwd_looked_up) {
            unix_pwd = getpwuid(getuid());
            pwd_looked_up = 1;
        }
        if(unix_pwd == NULL)
          return(NULL);
    }
    return(gcos_name(unix_pwd->pw_gecos, unix_pwd->pw_name));
}

char *
get_system_homedir()
{
    if(unix_pwd == NULL) {
        if(!pwd_looked_up){
            unix_pwd = getpwuid(getuid());
            pwd_looked_up = 1;
        }
        if(unix_pwd == NULL)
          return(NULL);
    }
    return(unix_pwd->pw_dir);
}

char *
get_system_passwd()
{
    if(unix_pwd == NULL) {
        unix_pwd = getpwuid(getuid()); /* Always do real lookup */
        pwd_looked_up = 1;
        if(unix_pwd == NULL)
          return(NULL);
    }
    return(unix_pwd->pw_passwd);
}



/*----------------------------------------------------------------------
      Pull the name out of the gcos field if we have that sort of /etc/passwd

   Args: gcos_field --  The long name or GCOS field to be parsed
         logname    --  Replaces occurances of & with logname string

 Result: returns pointer to buffer with name
  ----*/
static char *
gcos_name(gcos_field, logname)
char *logname, *gcos_field;
{


#ifdef NO_USG_SYSTEMS_KNOWN_YET

    char *firstcp, *lastcp;

    /* The last character of the full name is the one preceding the first
     * '('. If there is no '(', then the full name ends at the end of the
     * gcos field.
     */
    if(lastcp = strindex(gcos_field, '('))
	*lastcp = '\0';

    /* The first character of the full name is the one following the 
     * last '-' before that ending character. NOTE: that's why we
     * establish the ending character first!
     * If there is no '-' before the ending character, then the fullname
     * begins at the beginning of the gcos field.
     */
    if(firstcp = strrindex(gcos_field, '-'))
	firstcp++;
    else
	firstcp = gcos_field;

    return(firstcp);

#else /* NO_USG_SYSTEMS_KNOWN_YET */

#ifdef NO_PLAIN_SYSTEMS_KNOWN_YET
    /*---- Default case -- use full gcos field ----*/
    return(gcos_field);
#else /* NO_PLAIN_SYSTEMS_KNOWN_YET */

    /*----- Berkeley style long names  (default) ------*/ 

    static char fullname[MAX_FULLNAME+1];
    register char *fncp, *gcoscp, *lncp, *end;


    /* full name is all chars up to first ',' (or whole gcos, if no ',') */
    /* replace any & with logname in upper case */

    for(fncp = fullname, gcoscp= gcos_field, end = fullname + MAX_FULLNAME - 1;
        (*gcoscp != ',' && *gcoscp != '\0' && fncp != end);
	gcoscp++) {

	if(*gcoscp == '&') {
	    for(lncp = logname; *lncp; fncp++, lncp++)
		*fncp = toupper(*lncp);
	} else {
	    *fncp++ = *gcoscp;
	}
    }
    
    *fncp = '\0';
    return(fullname);
#endif /* NO_PLAIN_SYSTEMS_KNOWN_YET */
#endif /* NO_USG_SYSTEMS_KNOWN_YET */
}

    

/*----------------------------------------------------------------------
      Look up a userid on the local system and return rfc822 address

 Args: name  -- possible login name on local system

 Result: returns NULL or pointer to static string rfc822 address.
  ----*/
char *
local_name_lookup(name)
     char *name;
{
    struct passwd *pw;
    static char    buf[100];

    pw = getpwnam(name);
    if(pw == NULL)
      return(NULL);

    strcpy(buf, gcos_name(pw->pw_gecos, name));

    return(buf);
}



/*----------------------------------------------------------------------
       Call the system to change the passwd
 
It would be nice to talk to the passwd program via a pipe or ptty so the
user interface could be consistent, but we can't count on the the prompts
and responses from the passwd program to be regular so we just let the user 
type at the passwd program with some screen space, hope he doesn't scroll 
off the top and repaint when he's done.
 ----*/        
change_passwd()
{
    char cmd_buf[100];

    int i;
    for(i = 1; i < ps_global->ttyo->screen_rows; i ++) {
        MoveCursor(i, 0);
        CleartoEOLN();
    }

    MoveCursor(5, 0);
    fflush(stdout);

    Raw(0);
#ifdef NXT     
    sprintf(cmd_buf, "passwd %s" , ps_global->VAR_USER_ID);
#else
    strcpy(cmd_buf, "passwd");
#endif
    system(cmd_buf);
    sleep(3);
    Raw(1);
    init_username(ps_global);
}


    
mime_can_display(type, subtype, params)
     int type;
     char *subtype;
     PARAMETER *params;
{
    switch(type) {
    case TYPETEXT:
        if(ps_global->show_all_characters)
          return(1);
        while(params != NULL && strucmp(params->attribute,"charset") != 0)
          params = params->next;
        if(params != NULL) {
            return(match_charset(params->value,ps_global->VAR_CHAR_SET) != 2);
        } else {
            return(0);
        }

    case TYPEAUDIO:
     return(0);

    case TYPEIMAGE:
      if(getenv("DISPLAY") == NULL)
        return(0);
      if(strucmp(subtype, "gif") == 0 ||
         strucmp(subtype, "pgm") == 0 ||
         strucmp(subtype, "pbm") == 0)
          return(1);
      else
        return(0);


    case TYPEAPPLICATION:
      return(0);

    case TYPEMULTIPART:
      return(1);

    case TYPEMESSAGE:
      return(1);

    default:
      return(0);
  }
}
    
