#include <stddef.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "registry.h"
#include "profile.h"
#include "a1serverP.h"
#include "OSinter.h"
#include "RSreg.h"
#include "memory.h"
#include "al.h"

static const char *rule_drop;
static const char *rule_lib;
static int rule_lib_len;
static const char *log_file;

Bool
AlA1S_init(NOARGS)
{
  if (AlProfile_has_value(ALPROF_ANYONE_DROP_DIR) == Bool_FALSE ||
      AlProfile_has_value(ALPROF_ANYONE_LIB_DIR) == Bool_FALSE ||
      AlProfile_has_value(ALPROF_ANYONE_LOG_FILE) == Bool_FALSE)
    Al_fatal_error("Anyone server not configured in Lens Profile");

  rule_drop = AlProfile_get_CP_val(ALPROF_ANYONE_DROP_DIR);
  rule_lib = AlProfile_get_CP_val(ALPROF_ANYONE_LIB_DIR);
  rule_lib_len = strlen(rule_lib);
  log_file = AlProfile_get_CP_val(ALPROF_ANYONE_LOG_FILE);

  return Bool_TRUE;
}

Bool
AlA1S_read_rules(reg)
     Registry reg;
{
  AlRuleSet temp_rs;
  Darray user_names = Darray_create();
  int i, num_users;
  char wd[OSINTER_MAXPATHLEN+1];

  assert(Registry_entry_count(reg) == 0);

  if (OSinter_get_cwd(&(wd[0])) == Bool_FALSE)
    Al_fatal_error("Could not determine current directory");
  OSinter_set_cwd(rule_lib);
  if (OSinter_get_directory(user_names, OSINTER_CUR_DIR) == Bool_FALSE)
    Al_fatal_error("Could not open Rule Library Directory");
  num_users = Darray_len(user_names);
  for (i = 0; i < num_users; ++i) {
    char *user_name = (char *)Darray_get(user_names, i);

    if (*user_name != '.') {
      temp_rs = AlRSreg_read_from_file(user_name);
  
      if (temp_rs == (AlRuleSet)NULL)
	Al_fatal_error2("Could not read rules for user %s in %s", 
			user_name, 
			rule_lib);
      if (Registry_add(reg, user_name, temp_rs) != Bool_TRUE)
	Al_fatal_error1("Could not index rules for user %s", user_name);
    }
  }
  Darray_destroy(user_names);
  OSinter_set_cwd(&(wd[0]));
  return Bool_TRUE;
}     


/* This somewhat weak locking scheme only protects against client-server 
 * interaction.  If there are multiple servers handling the same anyone service * (a plausible situation), this needs to be replaced with something better
 *
 * This system dependency needs to be replaced, but I'm not sure how.
 */

#ifdef unix
#include "unixprotos.h"
Bool
AlA1S_lock_changes(NOARGS)
{
  chmod(rule_drop, 0700);	/* 0700 is octal */
  return Bool_TRUE;
}

Bool
AlA1S_unlock_changes(NOARGS)
{
  chmod(rule_drop, 0722);	/* 0722 is octal */
  return Bool_TRUE;
}
#endif

/* This routines needs to be careful about the authenticity of the files
 * in the dropdir
 */

Bool
AlA1S_process_changes(reg)
     Registry reg;
{
  Darray update_file_list = Darray_create();
  int i, num_drops;
  char wd[OSINTER_MAXPATHLEN+1];
  
  if (OSinter_get_cwd(&(wd[0])) == Bool_FALSE)
    Al_fatal_error("Could not determine current directory");
  OSinter_set_cwd(rule_drop);
  if (OSinter_get_directory(update_file_list, OSINTER_CUR_DIR) == Bool_FALSE)
    Al_fatal_error("Could not open Rule Drop Directory");
  num_drops = Darray_len(update_file_list);

  for (i = 0; i < num_drops; ++i) {
    const char *file_name = (const char *)Darray_get(update_file_list, 
						      (unsigned)i);
    OSinter_UIDtype actual_file_owner, actual_user_uid;
    char *user_name = (char *)Memory_allocate(strlen(file_name)+1);
    char *end_of_user_name;

    if (*file_name != '.') {
      (NORET)strcpy(user_name, file_name);
      end_of_user_name = strchr(user_name, ':');
      if (end_of_user_name != (char *)NULL) 
	*end_of_user_name = '\0';
      actual_file_owner = OSinter_get_file_uid(file_name);
      actual_user_uid = OSinter_name_to_uid(user_name);
      if (actual_file_owner == OSINTER_ERROR_UID ||
	  actual_user_uid == OSINTER_ERROR_UID ||
	  actual_file_owner != actual_user_uid) {
	Al_warning1("Invalid owner or name on %s -- removed", user_name);
	if (OSinter_remove(file_name) != Bool_TRUE)
	  Al_fatal_error1("Could not remove drop file %s", file_name);
      } else {
	AlRuleSet new_rs;

	/* check for magic deletion string */

	new_rs = AlRSreg_read_from_file(user_name);

	if (new_rs == (AlRuleSet)NULL) {
	  Al_warning2("Could not read rules for user %s in %s - removed", 
		     user_name, 
		     rule_drop);
	  if (OSinter_remove(file_name) != Bool_TRUE)
	    Al_fatal_error1("Could not remove drop file %s", file_name);
	} else {
	  char name_buf[255];
	  AlRuleSet old_rs = 
	    (AlRuleSet)Registry_replace_value(reg, user_name, new_rs);
#ifdef unix
	  sprintf(name_buf, "%s/%s", rule_lib, user_name);
	  chmod(user_name, 0640);
	  if (rename(user_name, name_buf) != 0)
	    Al_fatal_error2("Could not store updated rules for %s in %s",
			    user_name, 
			    rule_lib);
#else
	  assert(0);
#endif
	  if (old_rs != (AlRuleSet)NULL) {
	    AlRuleSet_destroy(old_rs);
	    Memory_free(user_name);
	  }
	  OSinter_set_cwd(rule_drop);
	}
      }
    }
    Memory_free((VOIDP)file_name);
  }     
  return Bool_TRUE;
}

