/*
 * $Id: update.c,v 1.6 2002/10/17 20:02:32 ljb Exp $
 * originally Id: update.c,v 1.46 1998/07/29 21:15:18 gerald Exp 
 */

#include <stdio.h>
#include <string.h>
#include "mrt.h"
#include "trace.h"
#include <time.h>
#include <signal.h>
#include "config_file.h"
#include <fcntl.h>
#include "irrd.h"
#include "irrd_prototypes.h"

extern trace_t *default_trace;

static int build_secondary_keys (irr_database_t *db, irr_object_t *object);

void mark_deleted_irr_object (irr_database_t *database, u_long offset) {
  char *xx = "*xx";

  if (fseek (database->db_fp, offset, SEEK_SET) < 0) {
    perror ("seek error");
    trace (NORM, default_trace, "** Error ** fseek failed in mark_deleted");
  }
  if (fwrite (xx, 1, 3, database->db_fp) == 0) 
    perror ("frwite failed");

  fflush (database->db_fp);
}

void add_spec_keys (irr_database_t *db, irr_object_t *object) {
  char *maint, *as_set, new_key[BUFSIZE];

  if (object->ll_mbr_of == NULL)
    return;

  /* this key is for (maint|as-set) hash lookups from set objects */

  if (object->ll_mnt_by != NULL) {
    LL_ContIterate (object->ll_mnt_by, maint) {
      LL_ContIterate (object->ll_mbr_of, as_set) {
        make_spec_key (new_key, maint, as_set);
        if (object->mode == IRR_DELETE)
          memory_hash_spec_remove (db, new_key, SET_MBRSX, object->name);
        else
          memory_hash_spec_store (db, new_key, SET_MBRSX, object);
      }
    }
  }

  /* add this key for fast ANY lookups */

  LL_ContIterate (object->ll_mbr_of, as_set) {
    make_spec_key (new_key, NULL, as_set);
    if (object->mode == IRR_DELETE)
      memory_hash_spec_remove (db, new_key, SET_MBRSX, object->name);
    else
      memory_hash_spec_store (db, new_key, SET_MBRSX, object);
  }
}

/* load_irr_object
 * We need to find an object before we can delete it 
 * Basically, we need to fill in the object structure (with all of the old keys)
 */
irr_object_t *load_irr_object (irr_database_t *database, irr_object_t *irr_object) {
  irr_object_t *old_irr_object = NULL;
  int ret;
  u_long offset, len;

  /* route objects are special because we need both the key AND the autnum */
  if (irr_object->type == ROUTE) 
    ret = seek_route_object (database, irr_object->name, irr_object->origin,
                             &offset, &len, 1);
  else /* fill in object->offset and object->len */
    ret = find_object_offset_len (database, irr_object->name, 
                                  irr_object->type, &offset, &len);

  /* object doesn't really exist -- nothing to delete */
  if (ret < 1)
    return (NULL);

  /* JW: I'm wondering about locking?  Should we lock? */
  if (fseek(database->db_fp, offset, SEEK_SET) < 0)
    trace (NORM, default_trace, "** Error ** fseek failed in load_irr_obj");
  old_irr_object = (irr_object_t *) scan_irr_file_main (database->db_fp, database, 0, SCAN_OBJECT);
  if (old_irr_object)
    old_irr_object->offset = offset;

  return (old_irr_object);
}

/* irr_special_indexing_store
 * store "special" keys, or keys that we cannot just dump into the
 * the generic hash table. These include route and inetnum objects (which
 * need special radix magic).
 *
 * return 1 is we still need to store this object in the hash
 */
int irr_special_indexing_store (irr_database_t *database, 
                                irr_object_t *irr_object) {
  char *key, key_buf[BUFSIZE], str_origin[16];
  int store_hash = 1;


  /* first add/delete object from hash associated with maintainers */
  if (irr_object->ll_mnt_by != NULL) {
    LL_ContIterate (irr_object->ll_mnt_by, key) {
      make_mntobj_key (key_buf, key);
      if (irr_object->mode == IRR_DELETE)
        memory_hash_spec_remove (database, key_buf, MNTOBJS, &irr_object->offset);
      else
        memory_hash_spec_store (database, key_buf, MNTOBJS, irr_object);
    }
  }

  switch (irr_object->type) {
  case ROUTE:
    add_spec_keys (database, irr_object);
    sprintf (str_origin, "%d", irr_object->origin);
    make_gas_key (key_buf, str_origin);
    if (irr_object->mode == IRR_DELETE) {
      memory_hash_spec_remove (database, key_buf, GASX, irr_object->name);
      delete_irr_route (database, irr_object->name, irr_object);
    } else {
      if (!irr_object->withdrawn)
         memory_hash_spec_store (database, key_buf, GASX, irr_object);
      add_irr_route (database, irr_object->name, irr_object);
    }
    store_hash = 0;
    break;
  case AUT_NUM:
    add_spec_keys (database, irr_object);
    break;
  case IPV6_SITE:
    LL_Iterate (irr_object->ll_prefix, key) {
      if (irr_object->mode == IRR_DELETE)
	delete_irr_route (database, key, irr_object);
      else
	add_irr_route (database, key, irr_object); 
    }
    store_hash = 0;
    break;
  case AS_SET: case RS_SET:
    make_setobj_key (key_buf, irr_object->name);
    if (irr_object->mode == IRR_DELETE)
      memory_hash_spec_remove (database, key_buf, SET_OBJX, NULL);
    else
      memory_hash_spec_store (database, key_buf, SET_OBJX, irr_object);
    break;
  default:
    store_hash = 1;
    break;
  }
  return (store_hash);
}

void back_out_secondaries (irr_database_t *db, irr_object_t *object, char *buf) {
  char *p, *q;

  for (q = buf; (p = strchr (q, ' ')) != NULL; q = p) {
    *p++ = '\0';
    irr_database_remove (db, q, SECONDARY, object->type, 
			 object->offset, object->len);
  }
}

/* JW It looks like the irr_database_store () routine allows 
 * duplicate PRIMARY keys.  Need to fix so it returns 
 * an error
 */
/* Given a PERSON object, make its secondary keys and it's complete
 * 'person: nic-hdl:' key (which is a primary key).  So this routine
 * inappropriately named.
 *
 * Return:
 *   void
 */
int build_secondary_keys (irr_database_t *db, irr_object_t *object) {
  char buf[256], buf1[256], *cp, *last, *p;
  int n = 0, ret_code = 1;
  int (*func)(irr_database_t *, char *, u_short, 
	      enum IRR_OBJECTS, u_long,  u_long);

  switch (object->type) {
  case PERSON:
    if (object->mode == IRR_DELETE)
      func = &irr_database_remove;
    else
      func = &irr_database_store;

    p = buf;
    strcpy (buf1, object->name);
    cp = buf1;
    strtok_r (cp, " ", &last);
    while (cp != NULL) {
      whitespace_remove (cp);

      if ((ret_code = (*func) (db, cp, SECONDARY, object->type, 
			       object->offset, object->len)) < 0) {
	/* an error occured, remove any secondary key's we have added */
	if (object->mode != IRR_DELETE) {
	  if (p != buf) {
	    *p++ = ' ';
	    *p = '\0';
	    back_out_secondaries (db, object, buf);
	  }
	  break;
	}
      }
      n = strlen (cp);
      if (p != buf)
	*p++ = ' ';
      memcpy (p, cp, n);
      p += n;	
      cp = strtok_r (NULL, " ", &last);
    }
    if (object->nic_hdl) {
      n = strlen (object->nic_hdl);
      *p++ = ' ';
      memcpy (p, object->nic_hdl, n);
      *(p + n) = '\0';
      if ((ret_code = (*func) (db, buf, PRIMARY, object->type, 
			       object->offset, object->len)) < 0) {
	if (object->mode != IRR_DELETE) {
	  *p = '\0';
	  back_out_secondaries (db, object, buf);
	}
      }
      else if ((*func) (db, object->nic_hdl, SECONDARY, object->type, 
			object->offset, object->len) < 0 &&
	       object->mode != IRR_DELETE) {
	  ret_code = -1;
	  irr_database_remove (db, buf, PRIMARY, object->type,  
			       object->offset, object->len);
      }
    }
    break;
  default:
    break;
  }

  return ret_code;
}

/* scan_process_object
 * We have scanned an entire object from the db, update, journal, whatever file
 * Now process the darn thing
 */
int add_irr_object (irr_database_t *database, irr_object_t *irr_object) {
  long svoffset = 0, offset;
  int ret_code = 1, store_hash = 0;

  /* if update (e.g. mirror or user update), save to end of main database */
  /* append the new object to the end of the db file */
  if (irr_object->mode == IRR_UPDATE) {
    offset = copy_irr_object (irr_object->fp, (long) irr_object->offset, 
			      database, irr_object->len);
    if (offset < 0) {
      trace (ERROR, default_trace, 
	     "DB (%s) file error, could not add object (%s) to EOF\n",
	     database->name, irr_object->name);
      return -1;
    }
    svoffset = irr_object->offset;
    irr_object->offset = (u_long) offset;
  }

  /* deal with primary key; JW later need to change to detect error */
  store_hash = irr_special_indexing_store (database, irr_object); 
  
  if (store_hash) {
    if ((ret_code = irr_database_store (database, irr_object->name, PRIMARY, 
					irr_object->type, irr_object->offset, 
					irr_object->len)) > 0) {
      /* Routine will build the PERSON secondary and 'person: hic-hdl:' primary key */
      if (irr_object->type == PERSON &&
	  (ret_code = build_secondary_keys (database, irr_object)) < 0)
	irr_database_remove (database, irr_object->name, PRIMARY, 
			     irr_object->type, irr_object->offset, irr_object->len);
    }
  }

  if (irr_object->mode == IRR_UPDATE) {
    if (ret_code > 0)
      trace (NORM, default_trace, "Object %s added\n", irr_object->name);
    else
      trace (ERROR, default_trace, 
	     "ERROR: Disk or indexing error: key (%s)\n", irr_object->name);
  }

  /* statistics */
  if (ret_code > 0) {
    database->num_objects[irr_object->type]++;
    database->bytes += irr_object->len;
  }

  if (irr_object->mode == IRR_UPDATE) 
    irr_object->offset = svoffset;

  return ret_code;
}

/* delete_irr_object 
 * First, load the current version of the object. We need to check a) that it
 * exists, and b) we need to get the current offset/len and the secondary keys
 * Once we have all of this, delete the indicies, mark the database file on disk
 */
int delete_irr_object (irr_database_t *database, irr_object_t *irr_object,
                       u_long *db_offset) {
  int ret_code = 1, store_hash = 0;

  irr_object_t *stored_irr_object;

  if ((stored_irr_object = load_irr_object (database, irr_object)) == NULL) {
    if (irr_object->mode == IRR_DELETE)
      trace (NORM, default_trace, "Object %s not found -- delete failed\n",
	     irr_object->name);
    return -1;
  }

  /* remove the special indexes for this object */
  stored_irr_object->mode = IRR_DELETE;
  store_hash = irr_special_indexing_store (database, stored_irr_object); 

  if (store_hash) {
    if ((ret_code = irr_database_remove (database, irr_object->name, PRIMARY, 
					 stored_irr_object->type, stored_irr_object->offset, 
					 stored_irr_object->len)) > 0)
      if (stored_irr_object->type == PERSON &&
	  (ret_code = build_secondary_keys (database, stored_irr_object)) < 0)
	irr_database_store (database, irr_object->name, PRIMARY, 
			    stored_irr_object->type, stored_irr_object->offset, 
			    stored_irr_object->len);
  }
  
  /* statistics */
  if (ret_code > 0) {
    database->num_objects[irr_object->type]--;
    *db_offset = stored_irr_object->offset;
    trace (NORM, default_trace, "Object %s deleted!\n", irr_object->name);
  }

  /* release memory */
  Delete_IRR_Object (stored_irr_object);

  return ret_code;
}



syntax highlighted by Code2HTML, v. 0.9.1