const char rcsid_kd_delete_c[] = "$Id: kd_delete.c,v 1.15 1997/06/15 06:01:38 marc Exp $";

/* 
 * Copyright (c) 1996, Marc Horowitz.  All rights reserved.
 * See the LICENSE file in the release for redistribution information.
 */


#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>

#include "database.h"
#include "globals.h"
#include "llist.h"
#include "kd_types.h"
#include "kd_internal.h"
#include "kd_search.h"
#include "pgputil.h"

typedef struct _dwfw_state {
   long key_id;
   error *err;
} dwfw_state;

#if 0

/* this isn't particularly efficient, but delete's aren't all that
   common. */

int delete_word_from_worddb(void *e, void *c)
{
   words_elem *we = (words_elem *) e;
   dwfw_state *s = (dwfw_state *) c;

   unsigned char word[256], buf[512];
   DBT key, data;
   unsigned char *here;
   int i, ret;

   for (i=0; i<we->len; i++)
      word[i] = tolower((we->ptr)[i]);

   key.data = (void *) word;
   key.size = (size_t) we->len;

   ret = (*(worddb->get))(worddb, &key, &data, 0);

   if (ret == -1) {
      s->err->fatal = 1;
      sprintf(s->err->buf,
	      "error reading worddb entry for delete (errno = %d)", errno);
      fail();
   }

   if (ret == 1) {
      s->err->fatal = 1;
      sprintf(s->err->buf,
	      "consistency error reading worddb for delete: %.*s not found ",
	      (int) we->len, we->ptr);
      fail();
   }

   for (here = (unsigned char *) data.data;
	here = my_memmem((void *) here, s->entry, 
			 data.size - (here - ((unsigned char *) data.data)),
			 sizeof(s->entry)),
	here && (here-((unsigned char *) data.data))%12;
	here++
	)
      /* SUPPRESS 570 */
      ;

   if (!here) {
      char buf[1024];

      sprintf(buf,
	      "consistency error: word \"%.*s\" in key id %02X%02X%02X%02X\n"
	      "does not refer back to key", (int) key.size, (char *) key.data,
	      s->entry[8], s->entry[9], s->entry[10], s->entry[11]);
      log_error("delete_word_from_worddb", buf);

      return(1);
   }

   if (data.size == 12) {
      if ((*(worddb->del))(worddb, &key, 0) < 0) {
	 s->err->fatal = 1;
	 s->err->str = "failed deleting worddb entry from database";
	 fail();
      }
   } else {
      here += 12;

      if (here < (((unsigned char *) data.data) + data.size))
	 memmove((void *) (here-12), (void *) here,
		 data.size - (here - ((unsigned char *) data.data)));

      data.size -= 12;

      if ((*(worddb->put))(worddb, &key, &data, 0) < 0) {
	 s->err->fatal = 1;
	 s->err->str = "failed replacing worddb entry in database";
	 fail();
      }
   }

   return(1);
}

#endif

int delete_key_from_worddb(void *e, llist *new_list, void *c, error *err)
{
   keys_elem *ke = (keys_elem *) e;
   xbuffer *xb = (xbuffer *) c;
   dwfw_state dwfws;
   char keyidstr[9], buf[128];
   MYSQL_RES *res;
   MYSQL_ROW row;

   /* XXX Not sure why this doesn't use ke->keyidbits */

   if (ke->keytype == 1) {			/* RSA */
      sprintf(keyidstr, "%02X%02X%02X%02X",
	      ke->modbits.buf[ke->modbits.len-4],
	      ke->modbits.buf[ke->modbits.len-3],
	      ke->modbits.buf[ke->modbits.len-2],
	      ke->modbits.buf[ke->modbits.len-1]);
   } else {
      mpidesc keyid;
      ddesc data;
#if 0
      mpidesc modulus;

      modulus.nbits =  ke->modbits.len * 8;
      modulus.number.data = ke->modbits.buf;
      modulus.number.size = ke->modbits.len;
      modulus.number.offset = 0;
#endif

      data.data = ke->pubkey.buf;
      data.size = ke->pubkey.len;
      data.offset = 0;

      generate_DSA_keyid(&data, &keyid);

      sprintf(keyidstr, "%02X%02X%02X%02X",
	      keyid.number.data[4],
	      keyid.number.data[5],
	      keyid.number.data[6],
	      keyid.number.data[7]);
   }

   sprintf(buf, "select id from keys where keyid = \"%s\"", keyidstr);

   if (!mysql_query(&mysql, buf) ||
       !(res = mysql_use_result(&mysql))) {
      err->fatal = 1;
      sprintf(err->buf, "Error getting database key id: %s",
	      mysql_error(&mysql));
      fail();
   }

   if (mysql_num_rows(res) == 0) {
      err->fatal = 1;
      sprintf(err->buf, "Consistency error deleting %s: key id not found",
	      keyidstr);
      fail();
   }
   
   row = mysql_fetch_row(res);

   sprintf(buf, "delete from words where key_id = %d", atol(row[0]));

   mysql_free_result(res);

   if (!mysql_query(&mysql, buf)) {
      err->fatal = 1;
      sprintf(err->buf, "Error removing words for key: %s",
	      mysql_error(&mysql));
      fail();
   }

   sprintf(buf, "key id %s deleted\n", keyidstr);

   if (!xbuffer_append_str(xb, buf))
      return(0);

   keys_elem_free(e, NULL);

   return(1);
}

int kd_delete_1(unsigned char *userid, long len, int flags,
		xbuffer *deleted, error *err)
{
   int ret;

   ret = kd_search_1(userid, len, KD_SEARCH_EXACT,
		     NULL, delete_key_from_worddb, deleted, err);

   kd_sync();

   if (!ret)
      return(0);

   if (deleted->len == 0) {
      /* no matching keys in database */

      err->fatal = 0;
      err->str = "No matching keys in database";
      return(0);
   }

   return(1);
}

int kd_delete(unsigned char *userid, long len, int flags,
              unsigned char **ret, long *retlen)
{
   error err;
   xbuffer deleted;

   err.str = err.buf;

   xbuffer_alloc(&deleted);

   kd_log_start("kd_delete", userid, len, flags);

   if (kd_delete_1(userid, len, flags, &deleted, &err)) {
      *ret = deleted.buf;
      *retlen = deleted.len;

      kd_log_finish("kd_delete", 1);

      return(1);
   } else if (!err.fatal) {
      if (!(*ret = (unsigned char *) my_strdup(err.str))) {
         err.fatal = 1;
         err.str = "Failed allocating space for error string";
	 dabort();

         /* fall through to fatal error handler */
      } else {
         *retlen = strlen((char *) *ret);

	 kd_log_finish("kd_delete", 0);

         return(0);
      }
   }

   /* fatal errors */

   if (err.fatal) {
      log_fatal("ks_delete", err.str);
      /* never returns */
   }

   /* keep the compiler quiet */

   return(0);
}   
