

#include <db.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

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

#ifdef MULTI_DB
#include "kd_generic_multi.h"
#include "multidbutil.h"
#endif

/*
 * This duplicated typedef is very bad manner (see kd_search.c)
 */
typedef struct _akte_state {
   xbuffer entries;
   error *err;
} akte_state;
typedef struct _ftf_state {
   llist_iter func;
   void *state;
} ftf_state;

extern int func_then_free(void *e, void *c);
extern int add_key_to_keylist(void *e, void *c);
extern int add_key_to_entrylist(void *e, void *c);
extern DB *keydb_array[];

/*
 * Bad manner, duplicated function (see kd_search.c)
 */
static int sort_twelvebytes(const void *a, const void *b)
{
   /* the result should be most recent first, so reverse args to memcmp */
   return(memcmp(b, a, 12));
}


int multi_do_all_keys(int flags, llist_iter func, void *c, error *err)
{
  int db_index;
  akte_state aktes;
  int ret, i;
  DBC *cursor;
  DBT key, data;
  ow_state ows;
  ftf_state ftfs;
   
  memset(&key, 0, sizeof(key));
  memset(&data, 0, sizeof(data));

  xbuffer_alloc(&(aktes.entries));
  aktes.err = err;

  ftfs.func = func;
  ftfs.state = c;

  for ( db_index = 0; db_index < DB_ARRAY_SIZE; db_index++) {
    keydb=keydb_array[db_index];
    if ((ret = (*(keydb->cursor))(keydb, NULL, &cursor,0))) {
      err->fatal = 1;
      sprintf(err->buf, "error creating keydb cursor: error = %d", ret);
      fail();
    }

    for (ret = (*(cursor->c_get))(cursor, &key, &data, DB_FIRST);
	 ret == 0;
	 ret = (*(cursor->c_get))(cursor, &key, &data, DB_NEXT)) {
      if (flags & KD_SEARCH_STDOUT) {
	/* just iterate the database calling the iterator function.
	   don't bother collecting id's or sorting */
	if (kd_keyblock_iterate((unsigned char *) data.data,
				(long) data.size,
				func_then_free, &ftfs, err))
	   continue;
      } else {
	if (kd_keyblock_iterate((unsigned char *) data.data,
				(long) data.size,
				add_key_to_entrylist, &aktes, err))
	  continue;
      }

      /* if the loop gets here, there was an error in the iteration. */

      if ((flags & KD_SEARCH_IGNORE_ERRORS) && (err->fatal == 0)) {
	char buf[1024];

	sprintf(buf, "ignoring error in kd_keyblock_iterate, "
		"keydb%3.3d keyid %02X%02X%02X%02X: %s",
		db_index,
		((unsigned char *) key.data)[0],
		((unsigned char *) key.data)[1],
		((unsigned char *) key.data)[2],
		((unsigned char *) key.data)[3],
		err->str);

	log_error("multi_do_all_keys", buf);
       } else {
	 xbuffer_free(&(aktes.entries));
	 (*(cursor->c_close))(cursor);
	 
	 /* a "non-fatal" error here indicates a user err, which
	    indicates db corruption, which is fatal */
	 err->fatal = 1;
	 fail();
       }
     }
     
     (*(cursor->c_close))(cursor);

     if (ret != DB_NOTFOUND) {
       err->fatal = 1;
       sprintf(err->buf, "error iterating keydb%3.3d: error = %d", db_index,ret);
       fail();
     }
   }

   if (!(flags & KD_SEARCH_STDOUT)) {
      if (aktes.entries.len == 0) {
	 /* the database is empty */

	 err->fatal = 0;
	 err->str = "The database is empty";
	 return(0);
      }

      qsort(aktes.entries.buf, (size_t) (aktes.entries.len/12), 12, 
	    sort_twelvebytes);

      ows.userid = NULL;
      ows.userid_len = 0;
      ows.filter = NULL;
      ows.return_disabled = (flags & KD_SEARCH_RETURN_DISABLED)?1:0;
      ows.c = c;
      ows.err = err;
      ows.append = func;

      for (i=0; i<aktes.entries.len; i+=12)
	 if (!kd_output_wde((void *) (aktes.entries.buf+i), (void *) &ows))
	    return(0);
   }

   xbuffer_free(&(aktes.entries));

   return(1);
}
