/* Copyright (C) 1993,1994 by the author(s).
 
 This software is published in the hope that it will be useful, but
 WITHOUT ANY WARRANTY for any part of this software to work correctly
 or as described in the manuals. See the ShapeTools Public License
 for details.

 Permission is granted to use, copy, modify, or distribute any part of
 this software but only under the conditions described in the ShapeTools 
 Public License. A copy of this license is supposed to have been given
 to you along with ShapeTools in a file named LICENSE. Among other
 things, this copyright notice and the Public License must be
 preserved on all copies.
 */
/*
 * AtFS -- Attribute Filesystem
 *
 * afenviron.c -- communication with the UNIX-Environment
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: afenviron.c[7.1] Thu Aug  4 16:02:51 1994 andy@cs.tu-berlin.de frozen $
 */

#include <pwd.h>
#include <grp.h>
#include "atfs.h"
#include "afarchive.h"

/*========================================================================
 *	af_getuid - returns uid of user if from local host
 *                  AF_ERROR if user is unknown
 *                  AF_REMOTE if user is not local
 *========================================================================*/

EXPORT uid_t af_getuid (name, host, domain)
     char *name, *host, *domain;
{
  struct passwd *pwent;

  if (name == NULL) /* in this case, name and host are null pointers */
    return ((uid_t) ERROR);

  if (strcmp (af_getdomain(), NOTNIL(domain)))
    return ((uid_t) AF_REMOTE);

  if ((pwent = getpwnam (name)) == NULL)
    FATAL ("getuid", "cannot get user ID", AF_EINTERNAL, (uid_t) ERROR);

  return (pwent->pw_uid);
}


/*========================================================================
 *	af_getgid - returns gid of user if from local host
 *                  AF_ERROR if user is unknown
 *                  AF_REMOTE if user is not local
 *========================================================================*/

EXPORT gid_t af_getgid (name, host, domain)
     char *name, *host, *domain;
{
  register struct passwd *pwent;

  if (name == NULL) /* in this case, name and host are null pointers */
    return ((gid_t) ERROR);

  if (strcmp (af_getdomain(), NOTNIL(domain)))
    return ((gid_t) AF_REMOTE);

  if ((pwent = getpwnam (name)) == NULL)
    FATAL ("getgid", "cannot get group ID", AF_EINTERNAL, (gid_t) ERROR);

  return (pwent->pw_gid);
}

/*========================================================================
 *	af_afuser - returns name, host, and domain of caller
 *========================================================================*/

static uid_t   calleruid;
static Af_user caller;
static bool    initcaller = FALSE;

#ifdef __STDC__
EXPORT Af_user *af_afuser (uid_t uid)
#else
EXPORT Af_user *af_afuser (uid)
     uid_t uid;
#endif
{
  static Af_user result;
  register struct passwd *pwent;
 
  if (!initcaller) /* if caller struct is not yet initialized */ {
    calleruid = geteuid();
    if ((pwent = getpwuid (calleruid)) == NULL)
      FAIL ("getuser", "", AF_EINVUSER, NULL);
    strcpy (caller.af_username, pwent->pw_name);
    strcpy (caller.af_userhost, af_gethostname ()); 
    strcpy (caller.af_userdomain, af_getdomain ()); 
    initcaller = TRUE;
  }
  if (uid == calleruid)
    return (&caller);

  if ((pwent = getpwuid (uid)) == NULL)
    FAIL ("getuser", "", AF_EINVUSER, NULL);
  strcpy (result.af_username, pwent->pw_name);
  strcpy (result.af_userhost, af_gethostname ()); 
  strcpy (result.af_userdomain, af_getdomain ()); 

  return (&result);
}

/*====================================================================
 *   af_checkread -- see if AF-file is readable
 *====================================================================*/

EXPORT int af_checkread (key)
     Af_key *key;
{
  uid_t uid, auuid, ownuid;

  if ((VATTR(key).af_mode & 0004) == 0004) /* readable for world */
    return (AF_OK);

  if ((VATTR(key).af_mode & 0040) == 0040) /* readable for group */ {
    struct group  *groupEntry;
    Af_user       *myself;
    gid_t         augid;
    int           i=0;

    augid = af_getgid (VATTR(key).af_auname, VATTR(key).af_auhost, VATTR(key).af_audomain);
    /* check effective group id */
    if (augid == getegid())
      return (AF_OK);
    /* check supplementary groups */
    if ((groupEntry = getgrgid (augid))) { /* if we find the author's group ... */
      if ((myself = af_afuser (geteuid()))) { /* ... and my password entry ... */
	while (groupEntry->gr_mem[i]) { /* ... then check if my name is in the group's members list */
	  if (!strcmp (myself->af_username, groupEntry->gr_mem[i++]))
	    return (AF_OK);
	}
      }
    }
  }
  
  if ((VATTR(key).af_mode & 0400) == 0400) /* readable by owner */ {
    uid = geteuid();
    auuid = af_getuid (VATTR(key).af_auname, VATTR(key).af_auhost, VATTR(key).af_audomain);
    ownuid = af_getuid (CATTR(key).af_ownname, CATTR(key).af_ownhost, CATTR(key).af_owndomain);
    if ((auuid == uid) || (ownuid == uid))
      return (AF_OK);
  }
  
  return (ERROR);
}


/*====================================================================
 *   af_checkperm -- check access permissions for AF-file
 *====================================================================*/

EXPORT int af_checkperm (key, mode)
     Af_key *key;
     int    mode;
{
  uid_t uid = geteuid(), lockeruid;
  bool ok = FALSE;

  if (mode & AF_OWNER) {
    if (uid == af_getuid (CATTR(key).af_ownname, CATTR(key).af_ownhost, CATTR(key).af_owndomain))
      ok = TRUE;
  }
  if (!ok && (mode & AF_LOCKHOLDER)) {
    if ((lockeruid = af_getuid (VATTR(key).af_lckname, VATTR(key).af_lckhost, VATTR(key).af_lckdomain)) == uid)
      ok = TRUE;
    else {
      /* if object is locked by someone else */
      if (lockeruid != (uid_t) ERROR) 
	goto exit;
    }
  }
  if (!ok && (mode & AF_AUTHOR)) {
    if (uid == af_getuid (VATTR(key).af_auname, VATTR(key).af_auhost, VATTR(key).af_audomain))
      ok = TRUE;
  }
  if (!ok && (mode & AF_WORLD)) {
    ok = TRUE;
  }
  
 exit:
  /* if access is not ok, or AtFS subdir is not writable */
  if (!ok)
    FAIL ("checkperm", "", AF_EACCES, ERROR);
  if (!(key->af_ldes->af_extent & AF_UXWRITE))
    FAIL ("checkperm", "", AF_ENOATFSDIR, ERROR);
  return (AF_OK);
}

/*======================
 *	af_gethostname
 *======================*/

static char *hostSym = NULL;

EXPORT char *af_gethostname ()
{
  char hostName[HOST_MAX];

  if (!hostSym) {
    gethostname (hostName, HOST_MAX);
    hostSym = af_entersym (hostName);
  }
  return (hostSym);
}

/*======================
 *	af_getdomain
 *======================*/

static char *domainSym = NULL;

EXPORT char *af_getdomain ()
{
  char domainName[DOMAIN_MAX+1];

  if (domainSym)
    return (domainSym);

  getdomainname (domainName, DOMAIN_MAX);
  if (!domainName[0]) {
    domainSym = af_gethostname ();
    return (domainSym);
  }

  domainName[DOMAIN_MAX] = '\0';
  if ((domainName[0] == '.') || (domainName[0] == '+'))
    domainSym = af_entersym (&domainName[1]);
  else
    domainSym = af_entersym (domainName);

  return (domainSym);
}
