/* Copyright (c) 1998, 1999 Thorsten Kukuk
   Author: Thorsten Kukuk <kukuk@suse.de>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <locale.h>
#include <libintl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <rpcsvc/nis.h>
#include "nis_db.h"

#include "nisd.h"
#include "log_msg.h"

#ifndef _
#define _(String) gettext (String)
#endif

bool_t
nis_lookup_3_r (ns_request *argp, nis_result *result, struct svc_req *rqstp)
{
  SERVER_CLOCK_DECLS;
  DATABASE_CLOCK_DECLS;
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;
  char *principal;

  start_clock (SERVER_CLOCK);

  principal = get_nis_principal (rqstp);

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_lookup_3() [From: %s:%d,%s]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port),
	       principal ? principal : "{error}");

      log_msg (LOG_DEBUG, "\t\tns_name     = \"%s\"", argp->ns_name);
      log_msg (LOG_DEBUG, "\t\t# ns_object = %u",
	       argp->ns_object.ns_object_len);
    }

  memset (result, '\0', sizeof (nis_result));

  /* We should never see an indexed name. */
  if (strchr (argp->ns_name, '[') != NULL)
    {
      result->status = NIS_BADNAME;
      goto bailout;
    }

  /* Do some sanity checks */
  result->status = nis_validname (argp->ns_name);
  if (result->status != NIS_SUCCESS)
    goto bailout;

  /* Special handling for root object */
  if (strcmp (argp->ns_name, root_obj->DI_data.do_name) == 0)
    {
      if (!nis_can_read (principal, root_obj))
	result->status = NIS_PERMISSION;
      else
	{
	  start_clock (DATABASE_CLOCK);

	  NIS_RES_OBJECT (result) = nis_clone_object (root_obj, NULL);
	  if (NIS_RES_OBJECT (result) != NULL)
	    {
	      NIS_RES_NUMOBJ (result) = 1;
	      result->status = NIS_SUCCESS;
	    }
	  else
	    {
	      NIS_RES_NUMOBJ (result) = 0;
	      result->status = NIS_NOMEMORY;
	    }

	  result->dticks = stop_clock (DATABASE_CLOCK);
	}
    }
  else
    {
      /* If the name is 'above' the root directory, then the caller is
	 looking for a name outside the namespace that this server
	 recognizes. We return the parent object here. */

      if (nis_dir_cmp (argp->ns_name,
		       root_obj->DI_data.do_name) == HIGHER_NAME)
	{
	  if (parent_obj)
	    {
	      if (!nis_can_read (principal, parent_obj))
		result->status = NIS_PERMISSION;
	      else
		{
		  start_clock (DATABASE_CLOCK);

		  NIS_RES_OBJECT (result) =
		    nis_clone_object (parent_obj, NULL);

		  if (NIS_RES_OBJECT (result) != NULL)
		    {
		      NIS_RES_NUMOBJ (result) = 1;
		      result->status = NIS_SUCCESS;
		    }
		  else
		    {
		      NIS_RES_NUMOBJ (result) = 0;
		      result->status = NIS_NOMEMORY;
		    }

		  result->dticks = stop_clock (DATABASE_CLOCK);
		}
	    }
	  else
	    result->status = NIS_NOSUCHNAME;
        }
      else
	{
	  nis_object *obj;
	  nis_error err;

	  /* Find the directory object where this name resides. */
	  /* XXX not threadsafe */
	  err = nis_ocache_lookup (nis_domain_of (argp->ns_name), 0, 0, &obj);

	  if (err != NIS_SUCCESS)
	    {
	      nis_free_object (obj);
	      result->status = NIS_NOSUCHNAME;
	      goto bailout;
	    }

        /* Check access rights: caller must have read rights on the
	   parent directory. */
        if (!nis_can_read (principal, obj))
	  {
	    nis_free_object (obj);
	    result->status = NIS_PERMISSION;
	    goto bailout;
	  }
        /* Now find the name. It might be in the cache, so it wouldn't
	   hurt to look there first. Make sure not to let the cache code
	   roll over to nis_lookup() though. */
        err = nis_ocache_lookup (argp->ns_name, CACHE_ONLY|CHECK_SERVER, 0,
				 &obj);
        if (err != NIS_SUCCESS)
	  {
	    nis_result *res = db_lookup (argp->ns_name);

	    memcpy (result, res, sizeof (nis_result));
	    free (res);

	    if (result->status != NIS_SUCCESS)
	      {
		nis_free_object (obj);
		goto bailout;
	      }

	    /* Save the result in the cache */
	    nis_ocache_insert (NIS_RES_OBJECT(result));
	  }
	else
	  {
	    NIS_RES_OBJECT(result) = nis_clone_object (obj, NULL);
	    NIS_RES_NUMOBJ(result) = 1;
	  }
	}
    }

 bailout:
  free (principal);

  if (verbose)
    log_msg (LOG_DEBUG, "\tresult = %s", nis_sperrno (result->status));

  result->zticks = stop_clock (SERVER_CLOCK);

  nis_update_stats (NIS_LOOKUP, result->status == NIS_SUCCESS ? TRUE : FALSE,
		    result->zticks);

  return retval;
}

bool_t
nis_modify_3_r (ns_request *argp, nis_result *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_modify_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));

      log_msg (LOG_DEBUG, "\t\tns_name     = \"%s\"", argp->ns_name);
      log_msg (LOG_DEBUG, "\t\t# ns_object = %u",
	       argp->ns_object.ns_object_len);
      if (argp->ns_object.ns_object_len > 0)
	nis_print_object (argp->ns_object.ns_object_val);
    }

  memset (result, '\0', sizeof (nis_result));

  /*
   *insert server code here
   */

  if (verbose)
    log_msg (LOG_DEBUG, _ ("\tresult = %s"), nis_sperrno (result->status));

  return retval;
}

bool_t
nis_iblist_3_r (ib_request *argp, nis_result *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_iblist_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));

      log_msg (LOG_DEBUG, "\t\tibr_name    = \"%s\"", argp->ibr_name);
      if (argp->ibr_srch.ibr_srch_len > 0)
	{
	  log_msg (LOG_DEBUG, "\t\tibr_srch.zattr_ndx = \"%s\"",
		   argp->ibr_srch.ibr_srch_val->zattr_ndx);
	  log_msg (LOG_DEBUG, "\t\tibr_srch->zattr_val.zattr_val = \"%s\"",
		   argp->ibr_srch.ibr_srch_val->zattr_val.zattr_val_val);
	}
      log_msg (LOG_DEBUG, "\t\tibr_flags   = \"%u\"", argp->ibr_flags);
#if 0
      log_msg (LOG_DEBUG, "\t\tibr_obj     = \"%s\"",
	       argp->ibr_obj.ibr_obj_val);
      log_msg (LOG_DEBUG, "\t\tibr_cbhost  = \"%s\"",
	       argp->ibr_cbhost.ibr_cbhost_val);
#endif
      log_msg (LOG_DEBUG, "\t\tibr_bufsize = \"%u\"", argp->ibr_bufsize);
    }

  memset (result, '\0', sizeof (nis_result));

  result->status = NIS_NOTFOUND;

  if (verbose)
    log_msg (LOG_DEBUG, "\tresult = %s", nis_sperrno (result->status));

  return retval;
}

bool_t
nis_ibadd_3_r (ib_request *argp, nis_result *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_ibadd_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr),
	       ntohs (rqhost->sin_port));

      log_msg (LOG_DEBUG, "\t\tibr_name   = \"%s\"", argp->ibr_name);
      if (argp->ibr_srch.ibr_srch_len > 0)
	log_msg (LOG_DEBUG, "\t\tibr_srch   = \"%s\"",
		 argp->ibr_srch.ibr_srch_val);
      log_msg (LOG_DEBUG, "\t\tibr_flags  = \"%u\"", argp->ibr_flags);
      if (argp->ibr_obj.ibr_obj_len > 0)
	log_msg (LOG_DEBUG, "\t\tibr_obj    = \"%s\"",
		 argp->ibr_obj.ibr_obj_val);
      if (argp->ibr_cbhost.ibr_cbhost_len > 0)
	log_msg (LOG_DEBUG, "\t\tibr_cbhost = \"%s\"",
		 argp->ibr_cbhost.ibr_cbhost_val);
      log_msg (LOG_DEBUG, "\t\tibr_bufsize= \"%u\"", argp->ibr_bufsize);
    }

  memset (result, '\0', sizeof (nis_result));

  /*
   * insert server code here
   */

  if (verbose)
    log_msg (LOG_DEBUG, "\tresult = %s", nis_sperrno (result->status));

  return retval;
}

bool_t
nis_ibmodify_3_r (ib_request *argp, nis_result *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_iblist_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));

      log_msg (LOG_DEBUG, "\t\tibr_name    = \"%s\"", argp->ibr_name);
      if (argp->ibr_srch.ibr_srch_len > 0)
	{
	  log_msg (LOG_DEBUG, "\t\tibr_srch.zattr_ndx = \"%s\"",
		   argp->ibr_srch.ibr_srch_val->zattr_ndx);
	  log_msg (LOG_DEBUG, "\t\tibr_srch->zattr_val.zattr_val = \"%s\"",
		   argp->ibr_srch.ibr_srch_val->zattr_val.zattr_val_val);
	}
      log_msg (LOG_DEBUG, "\t\tibr_flags   = \"%u\"", argp->ibr_flags);
#if 0
      log_msg (LOG_DEBUG, "\t\tibr_obj     = \"%s\"",
	       argp->ibr_obj.ibr_obj_val);
      log_msg (LOG_DEBUG, "\t\tibr_cbhost  = \"%s\"",
	       argp->ibr_cbhost.ibr_cbhost_val);
#endif
      log_msg (LOG_DEBUG, "\t\tibr_bufsize = \"%u\"", argp->ibr_bufsize);
    }

  memset (result, '\0', sizeof (nis_result));

  if (verbose)
    log_msg (LOG_DEBUG, "\tresult = %s", nis_sperrno (result->status));

  return retval;
}

bool_t
nis_ibremove_3_r (ib_request *argp, nis_result *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_ibremove_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));

      log_msg (LOG_DEBUG, "\t\tibr_name    = \"%s\"", argp->ibr_name);
      if (argp->ibr_srch.ibr_srch_len > 0)
	{
	  log_msg (LOG_DEBUG, "\t\tibr_srch.zattr_ndx = \"%s\"",
		   argp->ibr_srch.ibr_srch_val->zattr_ndx);
	  log_msg (LOG_DEBUG, "\t\tibr_srch->zattr_val.zattr_val = \"%s\"",
		   argp->ibr_srch.ibr_srch_val->zattr_val.zattr_val_val);
	}
      log_msg (LOG_DEBUG, "\t\tibr_flags   = \"%u\"", argp->ibr_flags);
#if 0
      log_msg (LOG_DEBUG, "\t\tibr_obj     = \"%s\"",
	       argp->ibr_obj.ibr_obj_val);
      log_msg (LOG_DEBUG, "\t\tibr_cbhost  = \"%s\"",
	       argp->ibr_cbhost.ibr_cbhost_val);
#endif
      log_msg (LOG_DEBUG, "\t\tibr_bufsize = \"%u\"", argp->ibr_bufsize);
    }

  memset (result, '\0', sizeof (nis_result));

  if (verbose)
    log_msg (LOG_DEBUG, "\tresult = %s", nis_sperrno (result->status));

  return retval;
}

bool_t
nis_ibfirst_3_r (ib_request *argp, nis_result *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_ibfirst_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr),
	       ntohs (rqhost->sin_port));

      log_msg (LOG_DEBUG, "\t\tibr_name   = \"%s\"", argp->ibr_name);
      log_msg (LOG_DEBUG, "\t\tibr_srch   = \"%s\"",
	       argp->ibr_srch.ibr_srch_val);
      log_msg (LOG_DEBUG, "\t\tibr_flags  = \"%u\"", argp->ibr_flags);
      log_msg (LOG_DEBUG, "\t\tibr_obj    = \"%s\"",
	       argp->ibr_obj.ibr_obj_val);
      log_msg (LOG_DEBUG, "\t\tibr_cbhost = \"%s\"",
	       argp->ibr_cbhost.ibr_cbhost_val);
      log_msg (LOG_DEBUG, "\t\tibr_bufsize= \"%u\"", argp->ibr_bufsize);
    }

  memset (result, '\0', sizeof (nis_result));
  result->status = NIS_SYSTEMERROR;
  /*
   * insert server code here
   */

  if (verbose)
    {
      log_msg (LOG_DEBUG, "\tresult = %s", nis_sperrno (result->status));
    }

  return retval;
}

bool_t
nis_ibnext_3_r (ib_request *argp, nis_result *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_ibnext_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr),
	       ntohs (rqhost->sin_port));
    }

  memset (result, '\0', sizeof (nis_result));
  result->status = NIS_SYSTEMERROR;

  /*
   * insert server code here
   */

  if (verbose)
    {
      log_msg (LOG_DEBUG, "\tresult = %s", nis_sperrno (result->status));
    }

  return retval;
}

bool_t
nis_finddirectory_3_r (fd_args *argp, fd_result *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_finddirectory_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr),
	       ntohs (rqhost->sin_port));
      log_msg (LOG_DEBUG, "\t\tdir_name  : \"%s\"", argp->dir_name);
      log_msg (LOG_DEBUG, "\t\trequester : \"%s\"", argp->requester);
    }

  memset (result, '\0', sizeof (fd_result));
  result->status = NIS_NOTFOUND;

  return retval;
}

bool_t
nis_status_3_r (nis_taglist *argp, nis_taglist *result, struct svc_req *rqstp)
{
  SERVER_CLOCK_DECLS;
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  start_clock(SERVER_CLOCK);

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_status_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));
    }

  nis_getstate (argp, result);

  nis_update_stats (NIS_STATUS, TRUE, stop_clock (SERVER_CLOCK));

  if (verbose)
    log_msg (LOG_DEBUG, "leave nis_status_3 ()");

  return retval;
}

bool_t
nis_dumplog_3_r (dump_args *argp, log_result *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_dumplog_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));
    }

  result->lr_status = NIS_SYSTEMERROR;

  /*
   * insert server code here
   */

  if (verbose)
    log_msg (LOG_DEBUG, "\tresult = %s", nis_sperrno (result->lr_status));

  return retval;
}

bool_t
nis_dump_3_r (dump_args *argp, log_result *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_dump_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));
    }

  memset (result, '\0', sizeof (log_result));
  result->lr_status = NIS_SYSTEMERROR;
  /*
   * insert server code here
   */

  if (verbose)
    log_msg (LOG_DEBUG, "\tresult = %s", nis_sperrno (result->lr_status));

  return retval;
}

bool_t
nis_callback_3_r (netobj *argp, bool_t *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_callback_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));
    }

  *result = FALSE;

  /*
   * insert server code here
   */

  if (verbose)
    log_msg (LOG_DEBUG, "\tresult = %d", *result);

  return retval;
}

bool_t
nis_cptime_3_r (nis_name *argp, uint32_t *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_cptime_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));
    }

  *result = 0;

  /*
   * insert server code here
   */

  if (verbose)
    log_msg (LOG_DEBUG, "\tresult = %u", *result);

  return retval;
}

bool_t
nis_checkpoint_3_r (nis_name *argp, cp_result *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_checkoint_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));
    }

  memset (result, '\0', sizeof (cp_result));

  /*
   * insert server code here
   */

  if (verbose)
    log_msg (LOG_DEBUG, "\tresult = %s", nis_sperrno (result->cp_status));

  return retval;
}

bool_t
nis_ping_3_r (ping_args *argp, void *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_ping_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));
    }

  result = NULL;

  /*
   * insert server code here
   */

  return retval;
}

bool_t
nis_servstate_3_r (nis_taglist *argp, nis_taglist *result,
		   struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_servstate_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));
    }

  result->tags.tags_len = 0;
  result->tags.tags_val = NULL;

  /*
   * insert server code here
   */

  return retval;
}

bool_t
nis_updkeys_3_r (nis_name *argp, nis_error *result, struct svc_req *rqstp)
{
  bool_t retval = TRUE;
  struct sockaddr_in *rqhost;

  if (verbose)
    {
      rqhost = svc_getcaller (rqstp->rq_xprt);
      log_msg (LOG_DEBUG, "nis_updkeys_3() [From: %s:%d]",
	       inet_ntoa (rqhost->sin_addr), ntohs (rqhost->sin_port));
    }

  *result = NIS_SYSTEMERROR;

  /*
   * insert server code here
   */

  if (verbose)
    log_msg (LOG_DEBUG, "\tresult = %s", nis_sperrno (*result));

  return retval;
}

int
nis_prog_3_freeresult (SVCXPRT *transp __attribute__ ((unused)),
		       xdrproc_t xdr_result, caddr_t result)
{
  xdr_free (xdr_result, result);

  return 1;
}
