From: Jonathan McDowell <noodles@earth.li>
Date: Mon, 31 May 2004 23:46:54 +0000 (+0000)
Subject: cscvs to tla changeset 3
X-Git-Url: https://git.sommitrealweird.co.uk/onak.git/commitdiff_plain/e02c731dfbb288c736f2cd09a9b6df0507c59ddd?ds=inline

cscvs to tla changeset 3
Author: noodles
Date: 2002/09/08 09:16:22


Committing onak 0.0.3.
---

diff --git a/HISTORY b/HISTORY
index e72f3df..8cc8eb2 100644
--- a/HISTORY
+++ b/HISTORY
@@ -21,3 +21,22 @@
 * Added searching on uids to lookup & keydb_pg.
 * Changed Postgres backend to use 64 bit keyids instead of 32 bit.
 * Made dearmor ignore CRs when looking for 2 blank lines.
+
+0.0.3 - 2nd July 2002
+
+* Added index on keyid for key table in Postgres backend.
+* Twiddled transaction support in an attempt to speed up mass key adding.
+* Changed putchar_func to take a character count rather than doing one char at
+  a time (massive speed up in Postgres case).
+* Introduced onak binary for general keyserver operations.
+* Changed all version number references to use VERSION macro.
+* Made lldel free the unused list structure. (pointed out by Simon Huggins)
+* Made llfind assert that the cmp function is non NULL. (Simon Huggins again)
+* Fixed gpgwww; after the move to 64bit keyids internally it was trying to
+  compare the 32bit user supplied keyid to the retrieved 64bit one and never
+  finding paths.
+* Various tidying up I've forgotten.
+* Fixed bug with removing signed packets while merging.
+* Fixed bug with potentially adding an already existing uid to a key when
+  merging.
+* Fixed stupid typo bug in merging keys.
diff --git a/Makefile b/Makefile
index 115ebe0..9f432b8 100644
--- a/Makefile
+++ b/Makefile
@@ -2,27 +2,25 @@
 
 CC = gcc
 CFLAGS += -Wall -pedantic -g -I/usr/local/include
+#LDFLAGS += -pg
 # Can be "pg" for Postgresql, "file" for flat files or "db2" for pksd db2 style.
 DBTYPE = pg
 # If using DBTYPE of "file" then comment the following line out.
 LIBS = -L/usr/local/lib -lpq
 #LIBS = -L/usr/local/lib -ldb2
 
-PROGS = keymerge add lookup gpgwww
+PROGS = add lookup gpgwww onak
 OBJS = armor.o parsekey.o keydb_$(DBTYPE).o merge.o keyid.o md5.o sha.o \
-	getcgi.o keyindex.o mem.o stats.o ll.o hash.o onak_conf.o
+	getcgi.o keyindex.o mem.o stats.o ll.o hash.o onak-conf.o
 SRCS = armor.c parsekey.c merge.c keyid.c md5.c sha.c main.c getcgi.c stats.c \
 	keyindex.c mem.c lookup.c add.c keydb_$(DBTYPE).c ll.c hash.c \
-	pathtest.c gpgwww.c onak_conf.c
+	gpgwww.c onak-conf.c
 
-all: $(PROGS) testparse pathtest maxpath
+all: $(PROGS) testparse maxpath
 
 testparse: main.o $(OBJS)
 	$(CC) -o testparse main.o $(OBJS) $(LIBS)
 
-pathtest: pathtest.o $(OBJS)
-	$(CC) -o pathtest pathtest.o $(OBJS) $(LIBS)
-
 maxpath: maxpath.o $(OBJS)
 	$(CC) -o maxpath maxpath.o $(OBJS) $(LIBS)
 
@@ -30,35 +28,30 @@ gpgwww: gpgwww.o $(OBJS)
 	$(CC) -o gpgwww gpgwww.o $(OBJS) $(LIBS)
 
 lookup: lookup.o getcgi.o keyindex.o keydb_$(DBTYPE).o keyid.o sha.o \
-		parsekey.o mem.o armor.o ll.o hash.o onak_conf.o
+		parsekey.o mem.o armor.o ll.o hash.o onak-conf.o
 	$(CC) -o lookup lookup.o getcgi.o keyindex.o keydb_$(DBTYPE).o keyid.o \
-		sha.o parsekey.o mem.o armor.o ll.o hash.o onak_conf.o $(LIBS)
+		sha.o parsekey.o mem.o armor.o ll.o hash.o onak-conf.o $(LIBS)
 
 add: add.o getcgi.o armor.o parsekey.o keydb_$(DBTYPE).o keyid.o sha.o mem.o \
-		keyindex.o ll.o hash.o merge.o onak_conf.o
+		keyindex.o ll.o hash.o merge.o onak-conf.o
 	$(CC) -o add add.o getcgi.o armor.o parsekey.o keydb_$(DBTYPE).o \
-		keyid.o sha.o mem.o keyindex.o ll.o hash.o merge.o onak_conf.o \
+		keyid.o sha.o mem.o keyindex.o ll.o hash.o merge.o onak-conf.o \
 		$(LIBS)
 
-keymerge: keymerge.o merge.o keyid.o sha.o armor.o parsekey.o ll.o \
-		keydb_$(DBTYPE).o mem.o keyindex.o hash.o getcgi.o onak_conf.o
-	$(CC) -o keymerge keymerge.o merge.o keyid.o sha.o armor.o parsekey.o \
-		keydb_$(DBTYPE).o mem.o keyindex.o ll.o hash.o getcgi.o \
-		onak_conf.o $(LIBS)
-
 onak: onak.o merge.o keyid.o sha.o armor.o parsekey.o ll.o \
-		keydb_$(DBTYPE).o mem.o keyindex.o hash.o getcgi.o onak_conf.o
-	$(CC) -o onak onak.o merge.o keyid.o sha.o armor.o parsekey.o \
+		keydb_$(DBTYPE).o mem.o keyindex.o hash.o getcgi.o onak-conf.o
+	$(CC) $(LDFLAGS) -o onak onak.o merge.o keyid.o sha.o armor.o parsekey.o \
 		keydb_$(DBTYPE).o mem.o keyindex.o ll.o hash.o getcgi.o \
-		onak_conf.o $(LIBS)
+		onak-conf.o $(LIBS)
 
 
 clean:
-	rm -f $(PROGS) $(OBJS) Makefile.bak testparse pathtest maxpath \
-		*.core core \
-		keymerge.o pathtest.o gpgwww.o add.o lookup.o main.o maxpath.o
+	rm -f $(PROGS) $(OBJS) Makefile.bak testparse maxpath *.core core \
+		gpgwww.o add.o lookup.o main.o maxpath.o onak.o
 
 depend:
 	makedepend $(SRCS)
 
+keydb_pg.o: keydb.o
+
 # DO NOT DELETE
diff --git a/PERFORMANCE b/PERFORMANCE
new file mode 100644
index 0000000..3221831
--- /dev/null
+++ b/PERFORMANCE
@@ -0,0 +1,39 @@
+Performance of adding 2M key chunks (~ 1700 keys).
+
+Originally ~ 30 mins with 0.0.2.
+
+After adding an index on keyid for onak_keys:
+        Command being timed: "/u2/noodles/onak-0.0.3/onak"
+        User time (seconds): 75.12
+        System time (seconds): 76.08
+        Percent of CPU this job got: 3%
+        Elapsed (wall clock) time (h:mm:ss or m:ss): 1:18:17
+        Major (requiring I/O) page faults: 630
+        Minor (reclaiming a frame) page faults: 1238
+
+Making deletion in the same transaction as readding:
+        Command being timed: "/u2/noodles/onak-0.0.3/onak"
+        User time (seconds): 67.28
+        System time (seconds): 75.74
+        Percent of CPU this job got: 2%
+        Elapsed (wall clock) time (h:mm:ss or m:ss): 1:22:08
+        Major (requiring I/O) page faults: 617
+        Minor (reclaiming a frame) page faults: 1241
+
+Making merge_keys all one transaction:
+        Command being timed: "/u2/noodles/onak-0.0.3/onak"
+        User time (seconds): 74.45
+        System time (seconds): 69.82
+        Percent of CPU this job got: 2%
+        Elapsed (wall clock) time (h:mm:ss or m:ss): 1:29:28
+        Major (requiring I/O) page faults: 610
+        Minor (reclaiming a frame) page faults: 1237
+
+Only delete old key if we know it exists:
+	Command being timed: "/u2/noodles/onak-0.0.3/onak"
+        User time (seconds): 77.47
+        System time (seconds): 75.06
+        Percent of CPU this job got: 3%
+        Elapsed (wall clock) time (h:mm:ss or m:ss): 1:16:41
+        Major (requiring I/O) page faults: 610
+        Minor (reclaiming a frame) page faults: 1239
diff --git a/TODO b/TODO
index 72dc698..d06b23b 100644
--- a/TODO
+++ b/TODO
@@ -2,10 +2,12 @@
 * Check keys on import?
 * Test library?
 * Better signature subpacket parsing (primary UID for example).
+* Better merging of signatures; need to decode subpackets so we can make sure
+  we don't add a signature that's already on a key but has different
+  subpackets.
 * Better txt2html routine.
 * Remove bithelp.h (i386 only at present & inlined).
   - Build and test on non-i386.
-* BSD license? (Needs md5 & sha routines rewritten/replaced then)
 * Pathfinder - graphical as well? Multiple paths?
 * Do pathlengths for similar email addresses to help aide keysigning.
   (ie "Find me the keys furthest from mine that end ox.ac.uk'")
@@ -22,8 +24,9 @@
 * Change over to PQescapeString in PostgreSQL backend once Woody releases.
 * Check freeing.
 * More comments.
-* Sort out merging (use keymerge + some Perl to answer incoming email. Not
-  sure about keys via hkp yet though).
+* Sort out merging (use onak + some Perl to answer incoming email. Not
+  sure about keys via hkp yet though - need to send updates out).
 * Look at db2 backend - is it db2's fault? (Well, deadlock in that the library
   probably is...). Is there an alternative library that would provide us with
   similar features but be more reliable? gdbm?
+* Honor no-modify keyserver flag ("Brian M. Carlson" <karlsson@hal-pc.org>)
diff --git a/add-imp.pl b/add-imp.pl
new file mode 100755
index 0000000..09470c3
--- /dev/null
+++ b/add-imp.pl
@@ -0,0 +1,12 @@
+#!/usr/bin/perl
+
+while (<>) {
+	/(........)$/;
+	print "Attempting to get $1\n";
+	$key = `./onak-db2 get $1`;
+	open (ONAK, "| ./onak -v add");
+	print ONAK $key;
+	close ONAK;
+
+	sleep 60;
+}
diff --git a/add.c b/add.c
index d820b52..3af9309 100644
--- a/add.c
+++ b/add.c
@@ -66,7 +66,7 @@ int main(int argc, char *argv[])
 			parse_keys(packets, &keys);
 			initdb();
 			printf("Got %d new keys.\n",
-					update_keys(&keys));
+					update_keys(&keys, false));
 			cleanupdb();
 		} else {
 			puts("No OpenPGP packets found in input.");
diff --git a/armor.c b/armor.c
index d811e50..9f5c19a 100644
--- a/armor.c
+++ b/armor.c
@@ -11,6 +11,7 @@
 
 #include "armor.h"
 #include "keystructs.h"
+#include "onak-conf.h"
 #include "parsekey.h"
 
 #define ARMOR_WIDTH 64
@@ -62,21 +63,6 @@ static unsigned char decode64(unsigned char c) {
 	return c;
 }
 
-
-void putstring(int (*putchar_func)(void *ctx, unsigned char c),
-			void *ctx,
-			const char *string)
-{
-	int i;
-
-	assert(putchar_func != NULL);
-	assert(string != NULL);
-
-	for (i = 0; string[i] != 0; i++) {
-		putchar_func(ctx, string[i]);
-	}
-}
-
 /**
  *	@lastoctet: The last octet we got.
  *	@curoctet: The current octet we're expecting (0, 1 or 2).
@@ -90,7 +76,7 @@ struct armor_context {
 	int curoctet;
 	int count;
 	long crc24;
-	int (*putchar_func)(void *ctx, unsigned char c);
+	int (*putchar_func)(void *ctx, size_t count, unsigned char *c);
 	void *ctx;
 };
 
@@ -104,36 +90,44 @@ static void armor_init(struct armor_context *ctx)
 
 static void armor_finish(struct armor_context *state)
 {
+	unsigned char c;
+
 	switch (state->curoctet++) {
 	case 0:
 		break;
 	case 1:
-		state->putchar_func(state->ctx,
-			encode64((state->lastoctet & 3) << 4));
-		state->putchar_func(state->ctx, '=');
-		state->putchar_func(state->ctx, '=');
+		c = encode64((state->lastoctet & 3) << 4);
+		state->putchar_func(state->ctx, 1, &c);
+		state->putchar_func(state->ctx, 1, (unsigned char *) "=");
+		state->putchar_func(state->ctx, 1, (unsigned char *) "=");
 		break;
 	case 2:
-		state->putchar_func(state->ctx,
-			encode64((state->lastoctet & 0xF) << 2));
-		state->putchar_func(state->ctx, '=');
+		c = encode64((state->lastoctet & 0xF) << 2);
+		state->putchar_func(state->ctx, 1, &c);
+		state->putchar_func(state->ctx, 1, (unsigned char *) "=");
 		break;
 	}
 
 	state->crc24 &= 0xffffffL;
-	state->putchar_func(state->ctx, '\n');
-	state->putchar_func(state->ctx, '=');
-	state->putchar_func(state->ctx, encode64(state->crc24 >> 18));
-	state->putchar_func(state->ctx, encode64((state->crc24 >> 12) & 0x3F));
-	state->putchar_func(state->ctx, encode64((state->crc24 >> 6) & 0x3F));
-	state->putchar_func(state->ctx, encode64(state->crc24 & 0x3F));
-	state->putchar_func(state->ctx, '\n');
+	state->putchar_func(state->ctx, 1, (unsigned char *) "\n");
+	state->putchar_func(state->ctx, 1, (unsigned char *) "=");
+	c = encode64(state->crc24 >> 18);
+	state->putchar_func(state->ctx, 1, &c);
+	c = encode64((state->crc24 >> 12) & 0x3F);
+	state->putchar_func(state->ctx, 1, &c);
+	c = encode64((state->crc24 >> 6) & 0x3F);
+	state->putchar_func(state->ctx, 1, &c);
+	c = encode64(state->crc24 & 0x3F);
+	state->putchar_func(state->ctx, 1, &c);
+	state->putchar_func(state->ctx, 1, (unsigned char *) "\n");
 
 }
 
-static int armor_putchar(void *ctx, unsigned char c)
+
+static int armor_putchar_int(void *ctx, unsigned char c)
 {
 	struct armor_context *state;
+	unsigned char t;
 	int i;
 
 	assert(ctx != NULL);
@@ -141,18 +135,20 @@ static int armor_putchar(void *ctx, unsigned char c)
 
 	switch (state->curoctet++) {
 	case 0:
-		state->putchar_func(state->ctx, encode64(c >> 2));
+		t = encode64(c >> 2);
+		state->putchar_func(state->ctx, 1, &t);
 		state->count++;
 		break;
 	case 1:
-		state->putchar_func(state->ctx,
-			encode64(((state->lastoctet & 3) << 4) + (c >> 4)));
+		t = encode64(((state->lastoctet & 3) << 4) + (c >> 4));
+		state->putchar_func(state->ctx, 1, &t);
 		state->count++;
 		break;
 	case 2:
-		state->putchar_func(state->ctx,
-			encode64(((state->lastoctet & 0xF) << 2) + (c >> 6)));
-		state->putchar_func(state->ctx, encode64(c & 0x3F));
+		t = encode64(((state->lastoctet & 0xF) << 2) + (c >> 6));
+		state->putchar_func(state->ctx, 1, &t);
+		t = encode64(c & 0x3F);
+		state->putchar_func(state->ctx, 1, &t);
 		state->count += 2;
 		break;
 	}
@@ -168,12 +164,24 @@ static int armor_putchar(void *ctx, unsigned char c)
 	}
 
 	if ((state->count % ARMOR_WIDTH) == 0) {
-		state->putchar_func(state->ctx, '\n');
+		state->putchar_func(state->ctx, 1, (unsigned char *) "\n");
 	}
 
 	return 0;
 }
 
+
+static int armor_putchar(void *ctx, size_t count, unsigned char *c)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		armor_putchar_int(ctx, c[i]);
+	}
+	
+	return 0;
+}
+
 /**
  *	@lastoctet: The last octet we got.
  *	@curoctet: The current octet we're expecting (0, 1 or 2).
@@ -285,18 +293,20 @@ static int dearmor_getchar_c(void *ctx, size_t count, unsigned char *c)
  *	This function ASCII armors a list of OpenPGP packets and outputs it
  *	using putchar_func.
  */
-int armor_openpgp_stream(int (*putchar_func)(void *ctx, unsigned char c),
+int armor_openpgp_stream(int (*putchar_func)(void *ctx, size_t count,
+						unsigned char *c),
 				void *ctx,
 				struct openpgp_packet_list *packets)
 {
 	struct armor_context armor_ctx;
 
-
 	/*
 	 * Print armor header
 	 */
-	putstring(putchar_func, ctx, "-----BEGIN PGP PUBLIC KEY BLOCK-----\n");
-	putstring(putchar_func, ctx, "Version: onak 0.0.1\n\n");
+	putchar_func(ctx, sizeof("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"),
+		(unsigned char *) "-----BEGIN PGP PUBLIC KEY BLOCK-----\n");
+	putchar_func(ctx, sizeof("Version: onak " VERSION "\n\n"),
+		(unsigned char *) "Version: onak " VERSION "\n\n");
 	
 	armor_init(&armor_ctx);
 	armor_ctx.putchar_func = putchar_func;
@@ -307,7 +317,8 @@ int armor_openpgp_stream(int (*putchar_func)(void *ctx, unsigned char c),
 	/*
 	 * Print armor footer
 	 */
-	putstring(putchar_func, ctx, "-----END PGP PUBLIC KEY BLOCK-----\n");
+	putchar_func(ctx, sizeof("-----END PGP PUBLIC KEY BLOCK-----\n"),
+		(unsigned char *) "-----END PGP PUBLIC KEY BLOCK-----\n");
 
 	return 0;
 }
diff --git a/armor.h b/armor.h
index 6b58a34..85ac6a6 100644
--- a/armor.h
+++ b/armor.h
@@ -20,7 +20,8 @@
  *	This function ASCII armors a list of OpenPGP packets and outputs it
  *	using putchar_func.
  */
-int armor_openpgp_stream(int (*putchar_func)(void *ctx, unsigned char c),
+int armor_openpgp_stream(int (*putchar_func)(void *ctx, size_t count,
+						unsigned char *c),
 				void *ctx,
 				struct openpgp_packet_list *packets);
 
diff --git a/gpgwww.c b/gpgwww.c
index 5f7da46..dc79135 100644
--- a/gpgwww.c
+++ b/gpgwww.c
@@ -14,7 +14,7 @@
 #include "getcgi.h"
 #include "hash.h"
 #include "keydb.h"
-#include "onak_conf.h"
+#include "onak-conf.h"
 #include "stats.h"
 
 void dofindpath(uint64_t have, uint64_t want, bool html)
@@ -23,6 +23,9 @@ void dofindpath(uint64_t have, uint64_t want, bool html)
 	int rec;
 	char *uid;
 
+	have = getfullkeyid(have);
+	want = getfullkeyid(want);
+
 	/*
 	 * Make sure the keys we have and want are in the cache.
 	 */
@@ -53,30 +56,31 @@ void dofindpath(uint64_t have, uint64_t want, bool html)
 				want);
 	} else {
 		printf("%d steps from 0x%llX to 0x%llX\n",
-				keyinfoa->colour, have, want);
+				keyinfoa->colour, have & 0xFFFFFFFF,
+				want & 0xFFFFFFFF);
 		curkey = keyinfoa;
 		while (curkey != NULL && curkey->keyid != 0) {
 			uid = keyid2uid(curkey->keyid);
 			if (html && uid == NULL) {
 				printf("<a href=\"lookup?op=get&search=%llX\">"
 					"0x%llX</a> ([User id not found])%s)%s\n",
-					curkey->keyid,
-					curkey->keyid,
+					curkey->keyid & 0xFFFFFFFF,
+					curkey->keyid & 0xFFFFFFFF,
 					(curkey->keyid == want) ? "" :
 					 " signs");
 			} else if (html && uid != NULL) {
 				printf("<a href=\"lookup?op=get&search=%llX\">"
 					"0x%llX</a> (<a href=\"lookup?op=vindex"
 					"&search=0x%llX\">%s</a>)%s\n",
-					curkey->keyid,
-					curkey->keyid,
-					curkey->keyid,
+					curkey->keyid & 0xFFFFFFFF,
+					curkey->keyid & 0xFFFFFFFF,
+					curkey->keyid & 0xFFFFFFFF,
 					txt2html(keyid2uid(curkey->keyid)),
 					(curkey->keyid == want) ? "" :
 					 " signs");
 			} else {
 				printf("0x%llX (%s)%s\n",
-					curkey->keyid,
+					curkey->keyid & 0xFFFFFFFF,
 					(uid == NULL) ? "[User id not found]" :
 						uid,
 					(curkey->keyid == want) ? "" :
diff --git a/hash.c b/hash.c
index 82dfcc7..47ec2d8 100644
--- a/hash.c
+++ b/hash.c
@@ -11,6 +11,7 @@
 
 #include "hash.h"
 #include "keydb.h"
+#include "keyid.h"
 #include "ll.h"
 #include "stats.h"
 
diff --git a/keydb.c b/keydb.c
index a9e4cef..3ebb056 100644
--- a/keydb.c
+++ b/keydb.c
@@ -35,7 +35,7 @@ char *keyid2uid(uint64_t keyid)
 	static char buf[1024];
 
 	buf[0]=0;
-	if (fetch_key(keyid, &publickey) && publickey != NULL) {
+	if (fetch_key(keyid, &publickey, false) && publickey != NULL) {
 		curuid = publickey->uids;
 		while (curuid != NULL && buf[0] == 0) {
 			if (curuid->packet->tag == 13) {
@@ -70,7 +70,7 @@ struct ll *getkeysigs(uint64_t keyid)
 	struct openpgp_signedpacket_list *uids = NULL;
 	struct openpgp_publickey *publickey = NULL;
 
-	fetch_key(keyid, &publickey);
+	fetch_key(keyid, &publickey, false);
 	
 	if (publickey != NULL) {
 		for (uids = publickey->uids; uids != NULL; uids = uids->next) {
@@ -82,3 +82,25 @@ struct ll *getkeysigs(uint64_t keyid)
 	return sigs;
 }
 #endif
+
+#ifdef NEED_GETFULLKEYID
+/**
+ *	getfullkeyid - Maps a 32bit key id to a 64bit one.
+ *	@keyid: The 32bit keyid.
+ *
+ *	This function maps a 32bit key id to the full 64bit one. It returns the
+ *	full keyid.
+ */
+uint64_t getfullkeyid(uint64_t keyid)
+{
+	struct openpgp_publickey *publickey = NULL;
+
+	if (keyid < 0x100000000) {
+		fetch_key(keyid, &publickey, false);
+		keyid = get_keyid(publickey);
+		free_publickey(publickey);
+	}
+	
+	return keyid;
+}
+#endif
diff --git a/keydb.h b/keydb.h
index 9a248e3..e9bcb67 100644
--- a/keydb.h
+++ b/keydb.h
@@ -32,38 +32,60 @@ void initdb(void);
  */
 void cleanupdb(void);
 
+/**
+ *	starttrans - Start a transaction.
+ *
+ *	Start a transaction. Intended to be used if we're about to perform many
+ *	operations on the database to help speed it all up, or if we want
+ *	something to only succeed if all relevant operations are successful.
+ */
+bool starttrans(void);
+
+/**
+ *	endtrans - End a transaction.
+ *
+ *	Ends a transaction.
+ */
+void endtrans(void);
+
 /**
  *	fetch_key - Given a keyid fetch the key from storage.
  *	@keyid: The keyid to fetch.
  *	@publickey: A pointer to a structure to return the key in.
+ *	@intrans: If we're already in a transaction.
  *
  *	This function returns a public key from whatever storage mechanism we
  *	are using.
  *
  *      TODO: What about keyid collisions? Should we use fingerprint instead?
  */
-int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey);
+int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey, bool intrans);
 
 /**
  *	store_key - Takes a key and stores it.
  *	@publickey: A pointer to the public key to store.
+ *	@intrans: If we're already in a transaction.
+ *	@update: If true the key exists and should be updated.
  *
  *	This function stores a public key in whatever storage mechanism we are
- *	using.
+ *	using. intrans indicates if we're already in a transaction so don't
+ *	need to start one. update indicates if the key already exists and is
+ *	just being updated.
  *
  *	TODO: Do we store multiple keys of the same id? Or only one and replace
  *	it?
  */
-int store_key(struct openpgp_publickey *publickey);
+int store_key(struct openpgp_publickey *publickey, bool intrans, bool update);
 
 /**
  *	delete_key - Given a keyid delete the key from storage.
  *	@keyid: The keyid to delete.
+ *	@intrans: If we're already in a transaction.
  *
  *	This function deletes a public key from whatever storage mechanism we
  *	are using. Returns 0 if the key existed.
  */
-int delete_key(uint64_t keyid);
+int delete_key(uint64_t keyid, bool intrans);
 
 /**
  *	fetch_key_text - Trys to find the keys that contain the supplied text.
@@ -93,4 +115,13 @@ char *keyid2uid(uint64_t keyid);
  */
 struct ll *getkeysigs(uint64_t keyid);
 
+/**
+ *	getfullkeyid - Maps a 32bit key id to a 64bit one.
+ *	@keyid: The 32bit keyid.
+ *
+ *	This function maps a 32bit key id to the full 64bit one. It returns the
+ *	full keyid.
+ */
+uint64_t getfullkeyid(uint64_t keyid);
+
 #endif /* __KEYDB_H__ */
diff --git a/keydb_db2.c b/keydb_db2.c
index 771b2a5..3d66c5a 100644
--- a/keydb_db2.c
+++ b/keydb_db2.c
@@ -21,7 +21,7 @@
 #include "keyindex.h"
 #include "keystructs.h"
 #include "mem.h"
-#include "onak_conf.h"
+#include "onak-conf.h"
 #include "parsekey.h"
 
 #define KEYDB_KEYID_BYTES 4
@@ -155,10 +155,33 @@ void cleanupdb(void)
 	db_appexit(&db2_env);
 }
 
+/**
+ *	starttrans - Start a transaction.
+ *
+ *	Start a transaction. Intended to be used if we're about to perform many
+ *	operations on the database to help speed it all up, or if we want
+ *	something to only succeed if all relevant operations are successful.
+ */
+bool starttrans(void)
+{
+	return true;
+}
+
+/**
+ *	endtrans - End a transaction.
+ *
+ *	Ends a transaction.
+ */
+void endtrans(void)
+{
+	return;
+}
+
 /**
  *	fetch_key - Given a keyid fetch the key from storage.
  *	@keyid: The keyid to fetch.
  *	@publickey: A pointer to a structure to return the key in.
+ *	@intrans: If we're already in a transaction.
  *
  *	We use the hex representation of the keyid as the filename to fetch the
  *	key from. The key is stored in the file as a binary OpenPGP stream of
@@ -166,7 +189,8 @@ void cleanupdb(void)
  *	in and then parse_keys() to parse the packets into a publickey
  *	structure.
  */
-int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey)
+int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
+		bool intrans)
 {
 	struct openpgp_packet_list *packets = NULL;
 	int ret;
@@ -187,7 +211,6 @@ int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey)
 
 	ret = (*(keydb(&key)->get))(keydb(&key), NULL, &key, &data, 0);
 	if (ret == 0) {
-		//do stuff with data.
 		fetchbuf.buffer = data.data;
 		fetchbuf.offset = 0;
 		fetchbuf.size = data.size;
@@ -198,16 +221,31 @@ int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey)
 	return (!ret);
 }
 
+/**
+ *	fetch_key_text - Trys to find the keys that contain the supplied text.
+ *	@search: The text to search for.
+ *	@publickey: A pointer to a structure to return the key in.
+ *
+ *	This function searches for the supplied text and returns the keys that
+ *	contain it.
+ */
+int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
+{
+	return 0;
+}
+
 /**
  *	store_key - Takes a key and stores it.
  *	@publickey: A pointer to the public key to store.
+ *	@intrans: If we're already in a transaction.
+ *	@update: If true the key exists and should be updated.
  *
  *	Again we just use the hex representation of the keyid as the filename
  *	to store the key to. We flatten the public key to a list of OpenPGP
  *	packets and then use write_openpgp_stream() to write the stream out to
  *	the file.
  */
-int store_key(struct openpgp_publickey *publickey)
+int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
 {
 	return 0;
 }
@@ -215,11 +253,12 @@ int store_key(struct openpgp_publickey *publickey)
 /**
  *	delete_key - Given a keyid delete the key from storage.
  *	@keyid: The keyid to delete.
+ *	@intrans: If we're already in a transaction.
  *
  *	This function deletes a public key from whatever storage mechanism we
  *	are using. Returns 0 if the key existed.
  */
-int delete_key(uint64_t keyid)
+int delete_key(uint64_t keyid, bool intrans)
 {
 	return (1);
 }
@@ -229,4 +268,5 @@ int delete_key(uint64_t keyid)
  */
 #define NEED_KEYID2UID 1
 #define NEED_GETKEYSIGS 1
+#define NEED_GETFULLKEYID 1
 #include "keydb.c"
diff --git a/keydb_file.c b/keydb_file.c
index 9784ecf..1d9b081 100644
--- a/keydb_file.c
+++ b/keydb_file.c
@@ -20,7 +20,7 @@
 #include "keystructs.h"
 #include "ll.h"
 #include "mem.h"
-#include "onak_conf.h"
+#include "onak-conf.h"
 #include "parsekey.h"
 
 /**
@@ -34,9 +34,9 @@ static int keydb_fetchchar(void *fd, size_t count, unsigned char *c)
 /**
  *	keydb_putchar - Puts a char to a file.
  */
-static int keydb_putchar(void *fd, unsigned char c)
+static int keydb_putchar(void *fd, size_t count, unsigned char *c)
 {
-	return !(write( *(int *) fd, &c, sizeof(c)));
+	return !(write( *(int *) fd, c, count));
 }
 
 /**
@@ -143,4 +143,5 @@ int delete_key(uint64_t keyid)
  */
 #define NEED_KEYID2UID 1
 #define NEED_GETKEYSIGS 1
+#define NEED_GETFULLKEYID 1
 #include "keydb.c"
diff --git a/keydb_pg.c b/keydb_pg.c
index d2a2a55..3f519bc 100644
--- a/keydb_pg.c
+++ b/keydb_pg.c
@@ -25,7 +25,7 @@
 #include "keyindex.h"
 #include "keystructs.h"
 #include "mem.h"
-#include "onak_conf.h"
+#include "onak-conf.h"
 #include "parsekey.h"
 
 /**
@@ -44,9 +44,9 @@ static int keydb_fetchchar(void *fd, size_t count, unsigned char *c)
 /**
  *	keydb_putchar - Puts a char to a file.
  */
-static int keydb_putchar(void *fd, unsigned char c)
+static int keydb_putchar(void *fd, size_t count, unsigned char *c)
 {
-	return !(lo_write(dbconn, *(int *) fd, &c, sizeof(c)));
+	return !(lo_write(dbconn, *(int *) fd, c, count));
 }
 
 /**
@@ -87,10 +87,43 @@ void cleanupdb(void)
 	dbconn = NULL;
 }
 
+/**
+ *	starttrans - Start a transaction.
+ *
+ *	Start a transaction. Intended to be used if we're about to perform many
+ *	operations on the database to help speed it all up, or if we want
+ *	something to only succeed if all relevant operations are successful.
+ */
+bool starttrans(void)
+{
+	PGresult *result = NULL;
+	
+	result = PQexec(dbconn, "BEGIN");
+	PQclear(result);
+
+	return true;
+}
+
+/**
+ *	endtrans - End a transaction.
+ *
+ *	Ends a transaction.
+ */
+void endtrans(void)
+{
+	PGresult *result = NULL;
+
+	result = PQexec(dbconn, "COMMIT");
+	PQclear(result);
+
+	return;
+}
+
 /**
  *	fetch_key - Given a keyid fetch the key from storage.
  *	@keyid: The keyid to fetch.
  *	@publickey: A pointer to a structure to return the key in.
+ *	@intrans: If we're already in a transaction.
  *
  *	We use the hex representation of the keyid as the filename to fetch the
  *	key from. The key is stored in the file as a binary OpenPGP stream of
@@ -98,7 +131,7 @@ void cleanupdb(void)
  *	in and then parse_keys() to parse the packets into a publickey
  *	structure.
  */
-int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey)
+int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey, bool intrans)
 {
 	struct openpgp_packet_list *packets = NULL;
 	PGresult *result = NULL;
@@ -109,8 +142,10 @@ int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey)
 	int numkeys = 0;
 	Oid key_oid;
 
-	result = PQexec(dbconn, "BEGIN");
-	PQclear(result);
+	if (!intrans) {
+		result = PQexec(dbconn, "BEGIN");
+		PQclear(result);
+	}
 	
 	if (keyid > 0xFFFFFFFF) {
 		snprintf(statement, 1023,
@@ -146,8 +181,10 @@ int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey)
 
 	PQclear(result);
 
-	result = PQexec(dbconn, "COMMIT");
-	PQclear(result);
+	if (!intrans) {
+		result = PQexec(dbconn, "COMMIT");
+		PQclear(result);
+	}
 	return (numkeys);
 }
 
@@ -228,13 +265,16 @@ int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
 /**
  *	store_key - Takes a key and stores it.
  *	@publickey: A pointer to the public key to store.
+ *	@intrans: If we're already in a transaction.
+ *	@update: If true the key exists and should be updated.
  *
  *	Again we just use the hex representation of the keyid as the filename
  *	to store the key to. We flatten the public key to a list of OpenPGP
  *	packets and then use write_openpgp_stream() to write the stream out to
- *	the file.
+ *	the file. If update is true then we delete the old key first, otherwise we
+ *	trust that it doesn't exist.
  */
-int store_key(struct openpgp_publickey *publickey)
+int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
 {
 	struct openpgp_packet_list *packets = NULL;
 	struct openpgp_packet_list *list_end = NULL;
@@ -248,6 +288,10 @@ int store_key(struct openpgp_publickey *publickey)
 	char *dodgychar = NULL;
 	int i;
 
+	if (!intrans) {
+		result = PQexec(dbconn, "BEGIN");
+		PQclear(result);
+	}
 
 	/*
 	 * Delete the key if we already have it.
@@ -257,10 +301,9 @@ int store_key(struct openpgp_publickey *publickey)
 	 * of difference though - the largest chunk of data is the keydata and
 	 * it definitely needs updated.
 	 */
-	delete_key(get_keyid(publickey));
-
-	result = PQexec(dbconn, "BEGIN");
-	PQclear(result);
+	if (update) {
+		delete_key(get_keyid(publickey), true);
+	}
 
 	next = publickey->next;
 	publickey->next = NULL;
@@ -328,8 +371,10 @@ int store_key(struct openpgp_publickey *publickey)
 		}
 	}
 
-	result = PQexec(dbconn, "COMMIT");
-	PQclear(result);
+	if (!intrans) {
+		result = PQexec(dbconn, "COMMIT");
+		PQclear(result);
+	}
 	
 	return 0;
 }
@@ -337,11 +382,12 @@ int store_key(struct openpgp_publickey *publickey)
 /**
  *	delete_key - Given a keyid delete the key from storage.
  *	@keyid: The keyid to delete.
+ *	@intrans: If we're already in a transaction.
  *
  *	This function deletes a public key from whatever storage mechanism we
  *	are using. Returns 0 if the key existed.
  */
-int delete_key(uint64_t keyid)
+int delete_key(uint64_t keyid, bool intrans)
 {
 	PGresult *result = NULL;
 	char *oids = NULL;
@@ -350,8 +396,10 @@ int delete_key(uint64_t keyid)
 	int i;
 	Oid key_oid;
 
-	result = PQexec(dbconn, "BEGIN");
-	PQclear(result);
+	if (!intrans) {
+		result = PQexec(dbconn, "BEGIN");
+		PQclear(result);
+	}
 	
 	snprintf(statement, 1023,
 			"SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
@@ -386,8 +434,10 @@ int delete_key(uint64_t keyid)
 
 	PQclear(result);
 
-	result = PQexec(dbconn, "COMMIT");
-	PQclear(result);
+	if (!intrans) {
+		result = PQexec(dbconn, "COMMIT");
+		PQclear(result);
+	}
 	return (found);
 }
 
@@ -430,4 +480,5 @@ char *keyid2uid(uint64_t keyid)
  * Include the basic keydb routines.
  */
 #define NEED_GETKEYSIGS 1
+#define NEED_GETFULLKEYID 1
 #include "keydb.c"
diff --git a/keyid.c b/keyid.c
index 8131e16..65a0363 100644
--- a/keyid.c
+++ b/keyid.c
@@ -26,6 +26,8 @@ uint64_t get_keyid(struct openpgp_publickey *publickey)
 	unsigned char c;
 	unsigned char *buff = NULL;
 
+	assert(publickey != NULL);
+
 	switch (publickey->publickey->data[0]) {
 	case 2:
 	case 3:
diff --git a/keyindex.c b/keyindex.c
index dfa0d44..a1bbe2e 100644
--- a/keyindex.c
+++ b/keyindex.c
@@ -237,7 +237,7 @@ int key_index(struct openpgp_publickey *keys, bool verbose, bool fingerprint,
 	if (html) {
 		puts("<pre>");
 	}
-	puts("Type  bits/keyID    Date       User ID");
+	puts("Type   bits/keyID    Date       User ID");
 	while (keys != NULL) {
 		created_time = (keys->publickey->data[1] << 24) +
 					(keys->publickey->data[2] << 16) +
diff --git a/keymerge.c b/keymerge.c
deleted file mode 100644
index fb95c2d..0000000
--- a/keymerge.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * keymerge.c - Takes a key on stdin, merges it and outputs the difference.
- *
- * Jonathan McDowell <noodles@earth.li>
- * 
- * Copyright 2002 Project Purple
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "armor.h"
-#include "keydb.h"
-#include "keyid.h"
-#include "keystructs.h"
-#include "mem.h"
-#include "merge.h"
-#include "parsekey.h"
-
-int stdin_getchar(void *ctx, size_t count, unsigned char *c)
-{
-	int ic;
-
-	do {
-		ic = getchar();
-		*c = ic;
-		c++;
-	} while ((ic != EOF) && (--count > 0));
-	return (ic == EOF);
-}
-
-int stdout_putchar(void *ctx, unsigned char c)
-{
-	return (putchar(c));
-}
-
-
-int main(int argc, char *argv[])
-{
-	struct openpgp_packet_list	*packets = NULL;
-	struct openpgp_packet_list	*list_end = NULL;
-	struct openpgp_publickey	*keys = NULL;
-	int				 rc = EXIT_SUCCESS;
-
-	dearmor_openpgp_stream(stdin_getchar, NULL, &packets);
-	if (packets != NULL) {
-		parse_keys(packets, &keys);
-		free_packet_list(packets);
-		packets = NULL;
-
-		initdb();
-		fprintf(stderr, "Got %d new keys.\n",
-				update_keys(&keys));
-		cleanupdb();
-	} else {
-		rc = 1;
-		fprintf(stderr, "No keys read.\n");
-	}
-
-	if (keys != NULL) {
-		flatten_publickey(keys, &packets, &list_end);
-		free_publickey(keys);
-		keys = NULL;
-
-		armor_openpgp_stream(stdout_putchar, NULL, packets);
-		free_packet_list(packets);
-		packets = NULL;
-	} else {
-		rc = 1;
-		fprintf(stderr, "No changes.\n");
-	}
-
-	return rc;
-}
diff --git a/ll.c b/ll.c
index acad0bc..7110378 100644
--- a/ll.c
+++ b/ll.c
@@ -31,7 +31,8 @@ struct ll *lladd(struct ll *curll, void *object)
 struct ll *lldel(struct ll *curll, void *object,
 	int (*objectcmp) (const void *object1, const void *object2))
 {
-	struct ll *cur;
+	struct ll *cur = NULL;
+	struct ll *old = NULL;
 
 	assert(objectcmp != NULL);
 
@@ -39,11 +40,16 @@ struct ll *lldel(struct ll *curll, void *object,
 	if (cur == NULL) {
 		return NULL;
 	} else if (!(*objectcmp)(cur->object, object)) {
-		return cur->next;
+		old = cur;
+		cur = cur->next;
+		free(old);
+		return cur;
 	} 
 	while (cur->next != NULL) {
 		if (!(*objectcmp)(cur->next->object, object)) {
+			old = cur->next;
 			cur->next = cur->next->next;
+			free(old);
 			break;
 		}
 	}
@@ -55,6 +61,8 @@ struct ll *llfind(struct ll *curll, void *object,
 {
 	struct ll *cur;
 
+	assert(objectcmp != NULL);
+
 	cur = curll;
 	while (cur != NULL && (*objectcmp)(cur->object, object)) {
 		cur = cur->next;
diff --git a/lookup.c b/lookup.c
index 3c55a61..94f38c8 100644
--- a/lookup.c
+++ b/lookup.c
@@ -18,7 +18,7 @@
 #include "keydb.h"
 #include "keyindex.h"
 #include "mem.h"
-#include "onak_conf.h"
+#include "onak-conf.h"
 #include "parsekey.h"
 
 #define OP_UNKNOWN 0
@@ -26,9 +26,9 @@
 #define OP_INDEX   2
 #define OP_VINDEX  3
 
-int putnextchar(void *ctx, unsigned char c)
+int putnextchar(void *ctx, size_t count, unsigned char *c)
 {
-        return putchar(c);
+	return printf("%.*s", count, c);
 }
 
 void find_keys(char *search, uint64_t keyid, bool ishex,
@@ -38,7 +38,7 @@ void find_keys(char *search, uint64_t keyid, bool ishex,
 	int count = 0;
 
 	if (ishex) {
-		count = fetch_key(keyid, &publickey);
+		count = fetch_key(keyid, &publickey, false);
 	} else {
 		count = fetch_key_text(search, &publickey);
 	}
@@ -115,7 +115,7 @@ int main(int argc, char *argv[])
 		initdb();
 		switch (op) {
 		case OP_GET:
-			if (fetch_key(keyid, &publickey)) {
+			if (fetch_key(keyid, &publickey, false)) {
 				puts("<pre>");
 				flatten_publickey(publickey,
 							&packets,
diff --git a/maxpath.c b/maxpath.c
index 3714281..f46ea4c 100644
--- a/maxpath.c
+++ b/maxpath.c
@@ -24,7 +24,7 @@ void findmaxpath(unsigned long max)
 	printf("In findmaxpath\n");
 	distance = 0;
 	from = to = tmp = NULL;
-	hash_getkeysigs(0x5B430367);
+	hash_getkeysigs(0xF1BD4BE45B430367);
 
 	for (loop = 0; (loop < HASHSIZE) && (distance < max); loop++) {
 		curkey = gethashtableentry(loop);
diff --git a/merge.c b/merge.c
index 96e628b..8bfbd09 100644
--- a/merge.c
+++ b/merge.c
@@ -105,7 +105,9 @@ bool remove_signed_packet(struct openpgp_signedpacket_list **packet_list,
 			if (cur->next == NULL) {
 				*list_end = prev;
 			}
+			// TODO: Free the removed signed packet...
 		}
+		prev = cur;
 	}
 
 	return found;
@@ -192,25 +194,29 @@ int merge_signed_packets(struct openpgp_signedpacket_list **old,
 			if (newelem->sigs == NULL) {
 				remove_signed_packet(new,
 						new_end,
-						curelem->packet);
+						newelem->packet);
 			}
 		}
 	}
 
 	/*
-	 * If *new != NULL now then there are UIDs on the new key that weren't
-	 * on the old key. Add them.
+	 * If *new != NULL now then there might be UIDs on the new key that
+	 * weren't on the old key. Walk through them, checking if the UID is
+	 * on the old key and if not adding them to it.
 	 */
 	for (curelem = *new; curelem != NULL;
 			curelem = curelem->next) {
-		ADD_PACKET_TO_LIST((*old_end),
+
+		if (find_signed_packet(*old, curelem->packet) == NULL) {
+			ADD_PACKET_TO_LIST((*old_end),
 				packet_dup(curelem->packet));
-		if (*old == NULL) {
-			*old = *old_end;
-		}
-		packet_list_add(&(*old_end)->sigs,
+			if (*old == NULL) {
+				*old = *old_end;
+			}
+			packet_list_add(&(*old_end)->sigs,
 				&(*old_end)->last_sig,
 				curelem->sigs);
+		}
 	}
 
 	return 0;
@@ -290,7 +296,7 @@ int merge_keys(struct openpgp_publickey *a, struct openpgp_publickey *b)
 		 */
 		merge_signed_packets(&a->uids, &a->last_uid, 
 				&b->uids, &b->last_uid);
-		merge_signed_packets(&a->subkeys, &a->last_uid,
+		merge_signed_packets(&a->subkeys, &a->last_subkey,
 				&b->subkeys, &b->last_subkey);
 
 	}
@@ -301,6 +307,7 @@ int merge_keys(struct openpgp_publickey *a, struct openpgp_publickey *b)
 /**
  *	update_keys - Takes a list of public keys and updates them in the DB.
  *	@keys: The keys to update in the DB.
+ *	@verbose: Should we output more information as we add keys?
  *
  *	Takes a list of keys and adds them to the database, merging them with
  *	the key in the database if it's already present there. The key list is
@@ -308,15 +315,23 @@ int merge_keys(struct openpgp_publickey *a, struct openpgp_publickey *b)
  *	we had before to what we have now (ie the set of data that was added to
  *	the DB). Returns the number of entirely new keys added.
  */
-int update_keys(struct openpgp_publickey **keys)
+int update_keys(struct openpgp_publickey **keys, bool verbose)
 {
 	struct openpgp_publickey *curkey = NULL;
 	struct openpgp_publickey *oldkey = NULL;
-	struct	openpgp_publickey *prev = NULL;
+	struct openpgp_publickey *prev = NULL;
 	int newkeys = 0;
+	bool intrans;
 
 	for (curkey = *keys; curkey != NULL; curkey = curkey->next) {
-		fetch_key(get_keyid(curkey), &oldkey);
+		intrans = starttrans();
+		if (verbose) {
+			fprintf(stderr, "Fetching key 0x%llX, result: %d\n",
+				get_keyid(curkey),
+				fetch_key(get_keyid(curkey), &oldkey, intrans));
+		} else {
+			fetch_key(get_keyid(curkey), &oldkey, intrans);
+		}
 
 		/*
 		 * If we already have the key stored in the DB then merge it
@@ -337,14 +352,22 @@ int update_keys(struct openpgp_publickey **keys)
 				}
 			} else {
 				prev = curkey;
-				store_key(oldkey);
+				if (verbose) {
+					fprintf(stderr, "Merged key; storing updated key.\n");
+				}
+				store_key(oldkey, intrans, true);
 			}
 			free_publickey(oldkey);
 			oldkey = NULL;
 		} else {
-			store_key(curkey);
+			if (verbose) {
+				fprintf(stderr, "Storing completely new key.\n");
+			}
+			store_key(curkey, intrans, false);
 			newkeys++;
 		}
+		endtrans();
+		intrans = false;
 	}
 
 	return newkeys;
diff --git a/merge.h b/merge.h
index 208c39b..f298172 100644
--- a/merge.h
+++ b/merge.h
@@ -28,6 +28,7 @@ int merge_keys(struct openpgp_publickey *a, struct openpgp_publickey *b);
 /**
  *	update_keys - Takes a list of public keys and updates them in the DB.
  *	@keys: The keys to update in the DB.
+ *	@verbose: Should we output more information as we add keys?
  *
  *	Takes a list of keys and adds them to the database, merging them with
  *	the key in the database if it's already present there. The key list is
@@ -35,6 +36,6 @@ int merge_keys(struct openpgp_publickey *a, struct openpgp_publickey *b);
  *	we had before to what we have now (ie the set of data that was added to
  *	the DB). Returns the number of entirely new keys added.
  */
-int update_keys(struct openpgp_publickey **keys);
+int update_keys(struct openpgp_publickey **keys, bool verbose);
 
 #endif
diff --git a/onak_conf.c b/onak-conf.c
similarity index 74%
rename from onak_conf.c
rename to onak-conf.c
index a72d31d..a3ee64b 100644
--- a/onak_conf.c
+++ b/onak-conf.c
@@ -1,5 +1,5 @@
 /*
- * onak_conf.c - Routines related to runtime config.
+ * onak-conf.c - Routines related to runtime config.
  *
  * Jonathan McDowell <noodles@earth.li>
  *
@@ -8,7 +8,7 @@
 
 #include <stdlib.h>
 
-#include "onak_conf.h"
+#include "onak-conf.h"
 
 /*
  *	config - Runtime configuration for onak.
@@ -22,7 +22,7 @@ struct onak_config config = {
 	/*
 	 * Options for the db2 file backend.
 	 */
-	NULL,			/* db2_dbpath */
+	"/community/pgp-keyserver/db-copy",	/* db2_dbpath */
 
 	/*
 	 * Options for the file backend.
@@ -33,7 +33,7 @@ struct onak_config config = {
 	 * Options for the Postgres backend.
 	 */
 	NULL,			/* pg_dbhost */
-	NULL,			/* pg_dbname */
-	"noodles",		/* pg_dbuser */
+	"noodles",		/* pg_dbname */
+	NULL,			/* pg_dbuser */
 	NULL,			/* pg_dbpass */
 };
diff --git a/onak_conf.h b/onak-conf.h
similarity index 93%
rename from onak_conf.h
rename to onak-conf.h
index 9e11167..dde7320 100644
--- a/onak_conf.h
+++ b/onak-conf.h
@@ -1,5 +1,5 @@
 /*
- * onak_conf.h - Routines related to runtime config.
+ * onak-conf.h - Routines related to runtime config.
  *
  * Jonathan McDowell <noodles@earth.li>
  *
@@ -9,7 +9,7 @@
 #ifndef __ONAK_CONF_H_
 #define __ONAK_CONF_H_
 
-#define VERSION "0.0.2"
+#define VERSION "0.0.3"
 
 /*
  *	struct onak_config - Runtime configuration for onak.
diff --git a/onak-mail.pl b/onak-mail.pl
index 30e5b93..0f108f4 100755
--- a/onak-mail.pl
+++ b/onak-mail.pl
@@ -22,14 +22,16 @@ sub submitupdate {
 	my @data = @_;
 	my (@errors, @mergedata);
 
-	open3(\*MERGEIN, \*MERGEOUT, \*MERGEERR, "/home/noodles/onak-0.0.2/keymerge");
+	open3(\*MERGEIN, \*MERGEOUT, \*MERGEERR, "/home/noodles/onak-0.0.3/onak", "add");
 
 	print MERGEIN @data;
 	close MERGEIN;
 	@errors = <MERGEERR>;
 	@mergedata = <MERGEOUT>;
 
-	#print @errors;
+	open (LOG, ">>/home/noodles/onak-0.0.3/keyadd.log");
+	print LOG @errors;
+	close LOG;
 
 	return @mergedata;
 }
diff --git a/onak.c b/onak.c
index 7b0f2f3..e7c7486 100644
--- a/onak.c
+++ b/onak.c
@@ -10,13 +10,17 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
 #include "armor.h"
 #include "keydb.h"
 #include "keyid.h"
+#include "keyindex.h"
 #include "keystructs.h"
 #include "mem.h"
 #include "merge.h"
+#include "onak-conf.h"
 #include "parsekey.h"
 
 int stdin_getchar(void *ctx, size_t count, unsigned char *c)
@@ -31,11 +35,50 @@ int stdin_getchar(void *ctx, size_t count, unsigned char *c)
 	return (ic == EOF);
 }
 
-int stdout_putchar(void *ctx, unsigned char c)
+int stdout_putchar(void *ctx, size_t count, unsigned char *c)
 {
-	return (putchar(c));
+	int i;
+
+	for (i = 0; i < count; i++) {
+		putchar(c[i]);
+	}
+	return 0;
 }
 
+void find_keys(char *search, uint64_t keyid, bool ishex,
+		bool fingerprint, bool exact, bool verbose)
+{
+	struct openpgp_publickey *publickey = NULL;
+	int count = 0;
+
+	if (ishex) {
+		count = fetch_key(keyid, &publickey, false);
+	} else {
+		count = fetch_key_text(search, &publickey);
+	}
+	if (publickey != NULL) {
+		key_index(publickey, verbose, fingerprint, false);
+		free_publickey(publickey);
+	} else if (count == 0) {
+		puts("Key not found.");
+	} else {
+		printf("Found %d keys, but maximum number to return is %d.\n",
+				count,
+				config.maxkeys);
+		puts("Try again with a more specific search.");
+	}
+}
+
+void usage(void) {
+	puts("onak " VERSION " - an OpenPGP keyserver.\n");
+	puts("Usage:\n");
+	puts("\tonak [options] <command> <parameters>\n");
+	puts("\tCommands:\n");
+	puts("\tadd    - read armored OpenPGP keys from stdin and add to the keyserver");
+	puts("\tdelete - delete a given key from the keyserver");
+	puts("\tindex  - search for a key and list it");
+	puts("\tvindex - search for a key and list it and its signatures");
+}
 
 int main(int argc, char *argv[])
 {
@@ -43,28 +86,93 @@ int main(int argc, char *argv[])
 	struct openpgp_packet_list	*list_end = NULL;
 	struct openpgp_publickey	*keys = NULL;
 	int				 rc = EXIT_SUCCESS;
+	char				*search = NULL;
+	char				*end = NULL;
+	uint64_t			 keyid = 0;
+	bool				 ishex = false;
+	bool				 verbose = false;
+	bool				 binary = false;
+	int				 optchar;
 
-	read_openpgp_stream(stdin_getchar, NULL, &packets);
-	if (packets != NULL) {
-		parse_keys(packets, &keys);
-		free_packet_list(packets);
-		packets = NULL;
 
-		initdb();
-		fprintf(stderr, "Got %d new keys.\n",
-				update_keys(&keys));
-		cleanupdb();
-	} else {
-		rc = 1;
-		fprintf(stderr, "No keys read.\n");
+	while ((optchar = getopt(argc, argv, "bv")) != -1 ) {
+		switch (optchar) {
+		case 'b': 
+			binary = true;
+			break;
+		case 'v': 
+			verbose = true;
+			break;
+		}
 	}
 
-	if (keys != NULL) {
-		free_publickey(keys);
-		keys = NULL;
+	if ((argc - optind) < 1) {
+		usage();
+	} else if (!strcmp("add", argv[optind])) {
+		if (binary) {
+			read_openpgp_stream(stdin_getchar, NULL, &packets);
+		} else {
+			dearmor_openpgp_stream(stdin_getchar, NULL, &packets);
+		}
+		if (packets != NULL) {
+			parse_keys(packets, &keys);
+			free_packet_list(packets);
+			packets = NULL;
+			if (verbose) {
+				fprintf(stderr, "Finished reading keys.\n");
+			}
+	
+			initdb();
+			fprintf(stderr, "Got %d new keys.\n",
+					update_keys(&keys, verbose));
+			cleanupdb();
+		} else {
+			rc = 1;
+			fprintf(stderr, "No keys read.\n");
+		}
+
+		if (keys != NULL) {
+			free_publickey(keys);
+			keys = NULL;
+		} else {
+			rc = 1;
+			fprintf(stderr, "No changes.\n");
+		}
+	} else if ((argc - optind) == 2) {
+		search = argv[optind+1];
+		if (search != NULL) {
+			keyid = strtoul(search, &end, 16);
+			if (*search != 0 &&
+					end != NULL &&
+					*end == 0) {
+				ishex = true;
+			}
+		}
+		initdb();
+		if (!strcmp("index", argv[optind])) {
+			find_keys(search, keyid, ishex, false, false, false);
+		} else if (!strcmp("vindex", argv[optind])) {
+			find_keys(search, keyid, ishex, false, false, true);
+		} else if (!strcmp("delete", argv[optind])) {
+			delete_key(getfullkeyid(keyid), false);
+		} else if (!strcmp("get", argv[optind])) {
+			if (fetch_key(keyid, &keys, false)) {
+				if (verbose) {
+					fprintf(stderr, "Got key.\n");
+				}
+				flatten_publickey(keys,
+						&packets,
+						&list_end);
+				armor_openpgp_stream(stdout_putchar,
+						NULL,
+						packets);
+			} else {
+				puts("Key not found");
+			}
+		}
+		cleanupdb();
 	} else {
-		rc = 1;
-		fprintf(stderr, "No changes.\n");
+		usage();
 	}
 
 	return rc;
diff --git a/onak.gprof b/onak.gprof
new file mode 100644
index 0000000..0e8b40b
--- /dev/null
+++ b/onak.gprof
@@ -0,0 +1,295 @@
+Flat profile:
+
+Each sample counts as 0.01 seconds.
+  %   cumulative   self              self     total           
+ time   seconds   seconds    calls  us/call  us/call  name    
+ 44.57      0.41     0.41    44208     9.27    14.02  transform
+ 22.83      0.62     0.21  9902592     0.02     0.02  rol
+  9.78      0.71     0.09    31610     2.85    19.66  sha1_write
+  7.61      0.78     0.07    33623     2.08     2.08  stdin_getchar
+  2.17      0.80     0.02     6937     2.88   106.67  get_keyid
+  2.17      0.82     0.02     1690    11.83    17.75  write_openpgp_stream
+  2.17      0.84     0.02                             Letext
+  1.09      0.85     0.01    30179     0.33     0.33  keydb_putchar
+  1.09      0.86     0.01    17848     0.56     0.56  packet_dup
+  1.09      0.87     0.01     6322     1.58    35.26  sha1_final
+  1.09      0.88     0.01     1690     5.92     8.88  flatten_publickey
+  1.09      0.89     0.01     1690     5.92     5.92  keyuids
+  1.09      0.90     0.01     1690     5.92   262.98  store_key
+  1.09      0.91     0.01        1 10000.00 10000.00  free_publickey
+  1.09      0.92     0.01        1 10000.00 815000.00  update_keys
+  0.00      0.92     0.00    17848     0.00     0.00  free_packet
+  0.00      0.92     0.00     6322     0.00     0.00  sha1_init
+  0.00      0.92     0.00     6322     0.00     0.00  sha1_read
+  0.00      0.92     0.00     3438     0.00     0.00  free_packet_list
+  0.00      0.92     0.00     3227     0.00     0.00  free_signedpacket_list
+  0.00      0.92     0.00     1690     0.00     0.00  endtrans
+  0.00      0.92     0.00     1690     0.00     0.00  fetch_key
+  0.00      0.92     0.00     1690     0.00     0.00  spsize
+  0.00      0.92     0.00     1690     0.00     0.00  starttrans
+  0.00      0.92     0.00        1     0.00     0.00  cleanupdb
+  0.00      0.92     0.00        1     0.00     0.00  initdb
+  0.00      0.92     0.00        1     0.00  5000.00  parse_keys
+  0.00      0.92     0.00        1     0.00 70000.00  read_openpgp_stream
+
+ %         the percentage of the total running time of the
+time       program used by this function.
+
+cumulative a running sum of the number of seconds accounted
+ seconds   for by this function and those listed above it.
+
+ self      the number of seconds accounted for by this
+seconds    function alone.  This is the major sort for this
+           listing.
+
+calls      the number of times this function was invoked, if
+           this function is profiled, else blank.
+ 
+ self      the average number of milliseconds spent in this
+ms/call    function per call, if this function is profiled,
+	   else blank.
+
+ total     the average number of milliseconds spent in this
+ms/call    function and its descendents per call, if this 
+	   function is profiled, else blank.
+
+name       the name of the function.  This is the minor sort
+           for this listing. The index shows the location of
+	   the function in the gprof listing. If the index is
+	   in parenthesis it shows where it would appear in
+	   the gprof listing if it were to be printed.
+
+		     Call graph (explanation follows)
+
+
+granularity: each sample hit covers 4 byte(s) for 1.09% of 0.92 seconds
+
+index % time    self  children    called     name
+                                                 <spontaneous>
+[1]     97.8    0.00    0.90                 main [1]
+                0.01    0.81       1/1           update_keys [2]
+                0.00    0.07       1/1           read_openpgp_stream [10]
+                0.01    0.00       1/1           free_publickey [17]
+                0.00    0.01       1/1           parse_keys [18]
+                0.00    0.00       1/3438        free_packet_list [22]
+                0.00    0.00       1/1           initdb [29]
+                0.00    0.00       1/1           cleanupdb [28]
+-----------------------------------------------
+                0.01    0.81       1/1           main [1]
+[2]     88.6    0.01    0.81       1         update_keys [2]
+                0.01    0.43    1690/1690        store_key [6]
+                0.01    0.35    3380/6937        get_keyid [3]
+                0.00    0.00    1690/1690        starttrans [27]
+                0.00    0.00    1690/1690        fetch_key [25]
+                0.00    0.00    1690/1690        endtrans [24]
+-----------------------------------------------
+                0.01    0.35    3380/6937        update_keys [2]
+                0.01    0.37    3557/6937        store_key [6]
+[3]     80.4    0.02    0.72    6937         get_keyid [3]
+                0.07    0.43   25288/31610       sha1_write [4]
+                0.01    0.21    6322/6322        sha1_final [7]
+                0.00    0.00    6322/6322        sha1_init [20]
+                0.00    0.00    6322/6322        sha1_read [21]
+-----------------------------------------------
+                               18966             sha1_write [4]
+                0.02    0.11    6322/31610       sha1_final [7]
+                0.07    0.43   25288/31610       get_keyid [3]
+[4]     67.5    0.09    0.53   31610+18966   sha1_write [4]
+                0.35    0.18   37886/44208       transform [5]
+                               18966             sha1_write [4]
+-----------------------------------------------
+                0.06    0.03    6322/44208       sha1_final [7]
+                0.35    0.18   37886/44208       sha1_write [4]
+[5]     67.4    0.41    0.21   44208         transform [5]
+                0.21    0.00 9902592/9902592     rol [8]
+-----------------------------------------------
+                0.01    0.43    1690/1690        update_keys [2]
+[6]     48.3    0.01    0.43    1690         store_key [6]
+                0.01    0.37    3557/6937        get_keyid [3]
+                0.02    0.01    1690/1690        write_openpgp_stream [11]
+                0.01    0.01    1690/1690        flatten_publickey [13]
+                0.01    0.00    1690/1690        keyuids [16]
+-----------------------------------------------
+                0.01    0.21    6322/6322        get_keyid [3]
+[7]     24.2    0.01    0.21    6322         sha1_final [7]
+                0.02    0.11    6322/31610       sha1_write [4]
+                0.06    0.03    6322/44208       transform [5]
+-----------------------------------------------
+                0.21    0.00 9902592/9902592     transform [5]
+[8]     22.8    0.21    0.00 9902592         rol [8]
+-----------------------------------------------
+                0.07    0.00   33623/33623       read_openpgp_stream [10]
+[9]      7.6    0.07    0.00   33623         stdin_getchar [9]
+-----------------------------------------------
+                0.00    0.07       1/1           main [1]
+[10]     7.6    0.00    0.07       1         read_openpgp_stream [10]
+                0.07    0.00   33623/33623       stdin_getchar [9]
+-----------------------------------------------
+                0.02    0.01    1690/1690        store_key [6]
+[11]     3.3    0.02    0.01    1690         write_openpgp_stream [11]
+                0.01    0.00   30179/30179       keydb_putchar [14]
+-----------------------------------------------
+                                                 <spontaneous>
+[12]     2.2    0.02    0.00                 Letext [12]
+-----------------------------------------------
+                0.01    0.01    1690/1690        store_key [6]
+[13]     1.6    0.01    0.01    1690         flatten_publickey [13]
+                0.01    0.00    8924/17848       packet_dup [15]
+-----------------------------------------------
+                0.01    0.00   30179/30179       write_openpgp_stream [11]
+[14]     1.1    0.01    0.00   30179         keydb_putchar [14]
+-----------------------------------------------
+                0.01    0.00    8924/17848       parse_keys [18]
+                0.01    0.00    8924/17848       flatten_publickey [13]
+[15]     1.1    0.01    0.00   17848         packet_dup [15]
+-----------------------------------------------
+                0.01    0.00    1690/1690        store_key [6]
+[16]     1.1    0.01    0.00    1690         keyuids [16]
+                0.00    0.00    1690/1690        spsize [26]
+-----------------------------------------------
+                0.01    0.00       1/1           main [1]
+[17]     1.1    0.01    0.00       1         free_publickey [17]
+                0.00    0.00    3227/3227        free_signedpacket_list [23]
+                0.00    0.00    1690/17848       free_packet [19]
+                0.00    0.00      41/3438        free_packet_list [22]
+-----------------------------------------------
+                0.00    0.01       1/1           main [1]
+[18]     0.5    0.00    0.01       1         parse_keys [18]
+                0.01    0.00    8924/17848       packet_dup [15]
+-----------------------------------------------
+                0.00    0.00    1690/17848       free_publickey [17]
+                0.00    0.00    3410/17848       free_signedpacket_list [23]
+                0.00    0.00   12748/17848       free_packet_list [22]
+[19]     0.0    0.00    0.00   17848         free_packet [19]
+-----------------------------------------------
+                0.00    0.00    6322/6322        get_keyid [3]
+[20]     0.0    0.00    0.00    6322         sha1_init [20]
+-----------------------------------------------
+                0.00    0.00    6322/6322        get_keyid [3]
+[21]     0.0    0.00    0.00    6322         sha1_read [21]
+-----------------------------------------------
+                0.00    0.00       1/3438        main [1]
+                0.00    0.00      41/3438        free_publickey [17]
+                0.00    0.00    3396/3438        free_signedpacket_list [23]
+[22]     0.0    0.00    0.00    3438         free_packet_list [22]
+                0.00    0.00   12748/17848       free_packet [19]
+-----------------------------------------------
+                0.00    0.00    3227/3227        free_publickey [17]
+[23]     0.0    0.00    0.00    3227         free_signedpacket_list [23]
+                0.00    0.00    3410/17848       free_packet [19]
+                0.00    0.00    3396/3438        free_packet_list [22]
+-----------------------------------------------
+                0.00    0.00    1690/1690        update_keys [2]
+[24]     0.0    0.00    0.00    1690         endtrans [24]
+-----------------------------------------------
+                0.00    0.00    1690/1690        update_keys [2]
+[25]     0.0    0.00    0.00    1690         fetch_key [25]
+-----------------------------------------------
+                0.00    0.00    1690/1690        keyuids [16]
+[26]     0.0    0.00    0.00    1690         spsize [26]
+-----------------------------------------------
+                0.00    0.00    1690/1690        update_keys [2]
+[27]     0.0    0.00    0.00    1690         starttrans [27]
+-----------------------------------------------
+                0.00    0.00       1/1           main [1]
+[28]     0.0    0.00    0.00       1         cleanupdb [28]
+-----------------------------------------------
+                0.00    0.00       1/1           main [1]
+[29]     0.0    0.00    0.00       1         initdb [29]
+-----------------------------------------------
+
+ This table describes the call tree of the program, and was sorted by
+ the total amount of time spent in each function and its children.
+
+ Each entry in this table consists of several lines.  The line with the
+ index number at the left hand margin lists the current function.
+ The lines above it list the functions that called this function,
+ and the lines below it list the functions this one called.
+ This line lists:
+     index	A unique number given to each element of the table.
+		Index numbers are sorted numerically.
+		The index number is printed next to every function name so
+		it is easier to look up where the function in the table.
+
+     % time	This is the percentage of the `total' time that was spent
+		in this function and its children.  Note that due to
+		different viewpoints, functions excluded by options, etc,
+		these numbers will NOT add up to 100%.
+
+     self	This is the total amount of time spent in this function.
+
+     children	This is the total amount of time propagated into this
+		function by its children.
+
+     called	This is the number of times the function was called.
+		If the function called itself recursively, the number
+		only includes non-recursive calls, and is followed by
+		a `+' and the number of recursive calls.
+
+     name	The name of the current function.  The index number is
+		printed after it.  If the function is a member of a
+		cycle, the cycle number is printed between the
+		function's name and the index number.
+
+
+ For the function's parents, the fields have the following meanings:
+
+     self	This is the amount of time that was propagated directly
+		from the function into this parent.
+
+     children	This is the amount of time that was propagated from
+		the function's children into this parent.
+
+     called	This is the number of times this parent called the
+		function `/' the total number of times the function
+		was called.  Recursive calls to the function are not
+		included in the number after the `/'.
+
+     name	This is the name of the parent.  The parent's index
+		number is printed after it.  If the parent is a
+		member of a cycle, the cycle number is printed between
+		the name and the index number.
+
+ If the parents of the function cannot be determined, the word
+ `<spontaneous>' is printed in the `name' field, and all the other
+ fields are blank.
+
+ For the function's children, the fields have the following meanings:
+
+     self	This is the amount of time that was propagated directly
+		from the child into the function.
+
+     children	This is the amount of time that was propagated from the
+		child's children to the function.
+
+     called	This is the number of times the function called
+		this child `/' the total number of times the child
+		was called.  Recursive calls by the child are not
+		listed in the number after the `/'.
+
+     name	This is the name of the child.  The child's index
+		number is printed after it.  If the child is a
+		member of a cycle, the cycle number is printed
+		between the name and the index number.
+
+ If there are any cycles (circles) in the call graph, there is an
+ entry for the cycle-as-a-whole.  This entry shows who called the
+ cycle (as parents) and the members of the cycle (as children.)
+ The `+' recursive calls entry shows the number of function calls that
+ were internal to the cycle, and the calls entry for each member shows,
+ for that member, how many times it was called from other members of
+ the cycle.
+
+
+Index by function name
+
+  [12] Letext (bithelp.h)     [29] initdb                  [4] sha1_write
+  [28] cleanupdb              [14] keydb_putchar (keydb_pg.c) [26] spsize
+  [24] endtrans               [16] keyuids                [27] starttrans
+  [25] fetch_key              [15] packet_dup              [9] stdin_getchar
+  [13] flatten_publickey      [18] parse_keys              [6] store_key
+  [19] free_packet            [10] read_openpgp_stream     [5] transform (sha.c)
+  [22] free_packet_list        [8] rol (bithelp.h)         [2] update_keys
+  [17] free_publickey          [7] sha1_final             [11] write_openpgp_stream
+  [23] free_signedpacket_list [20] sha1_init
+   [3] get_keyid              [21] sha1_read
diff --git a/onak.sql b/onak.sql
index 6afa737..9fc033c 100644
--- a/onak.sql
+++ b/onak.sql
@@ -6,6 +6,7 @@ CREATE TABLE onak_keys (
 	keyid	char(16) NOT NULL,
 	keydata	oid NOT NULL
 );
+CREATE INDEX onak_keys_keyid_index ON onak_keys(keyid);
 
 CREATE TABLE onak_uids (
 	keyid	char(16) NOT NULL,
diff --git a/parsekey.c b/parsekey.c
index f83e805..fbdde15 100644
--- a/parsekey.c
+++ b/parsekey.c
@@ -281,64 +281,66 @@ int read_openpgp_stream(int (*getchar_func)(void *ctx, size_t count,
  *	This function uses putchar_func to write characters to an OpenPGP
  *	packet stream from a linked list of packets.
  */
-int write_openpgp_stream(int (*putchar_func)(void *ctx, unsigned char c),
+int write_openpgp_stream(int (*putchar_func)(void *ctx, size_t count,
+						unsigned char *c),
 				void *ctx,
 				struct openpgp_packet_list *packets)
 {
 	unsigned char	curchar = 0;
-	int		i;
 
 	while (packets != NULL) {
 		curchar = 0x80;
 		if (packets->packet->newformat) {
 			curchar |= 0x40;
 			curchar |= packets->packet->tag;
-			putchar_func(ctx, curchar);
+			putchar_func(ctx, 1, &curchar);
 
 			if (packets->packet->length < 192) {
-				putchar_func(ctx, packets->packet->length);
+				curchar = packets->packet->length;
+				putchar_func(ctx, 1, &curchar);
 			} else if (packets->packet->length > 191 &&
 				packets->packet->length < 8383) {
-//				fputs("Potentially dodgy code here.\n", stderr);
-				putchar_func(ctx, 
-					(((packets->packet->length - 192) &
-					 0xFF00) >> 8) + 192);
-
-				putchar_func(ctx, 
-					(packets->packet->length - 192) &
-					 0xFF);
+				curchar = (((packets->packet->length - 192) &
+					 0xFF00) >> 8) + 192;
+				putchar_func(ctx, 1, &curchar);
 
+				curchar = (packets->packet->length - 192) &
+					 0xFF;
+				putchar_func(ctx, 1, &curchar);
 			} else {
 				fputs("Unsupported new format length.\n", stderr);
 			}
 		} else {
 			curchar |= (packets->packet->tag << 2);
 			if (packets->packet->length < 256) {
-				putchar_func(ctx, curchar);
-				putchar_func(ctx, packets->packet->length);
+				putchar_func(ctx, 1, &curchar);
+				curchar = packets->packet->length;
+				putchar_func(ctx, 1, &curchar);
 			} else if (packets->packet->length < 0x10000) {
 				curchar |= 1;
-				putchar_func(ctx, curchar);
-				putchar_func(ctx, packets->packet->length >> 8);
-				putchar_func(ctx,
-					packets->packet->length & 0xFF);
+				putchar_func(ctx, 1, &curchar);
+				curchar = packets->packet->length >> 8;
+				putchar_func(ctx, 1, &curchar);
+				curchar = packets->packet->length & 0xFF;
+				putchar_func(ctx, 1, &curchar);
 			} else {
 				curchar |= 2;
-				putchar_func(ctx, curchar);
-				putchar_func(ctx,
-					packets->packet->length >> 24);
-				putchar_func(ctx,
-					(packets->packet->length >> 16) & 0xFF);
-				putchar_func(ctx,
-					(packets->packet->length >> 8) & 0xFF);
-				putchar_func(ctx,
-					packets->packet->length & 0xFF);
+				putchar_func(ctx, 1, &curchar);
+				curchar = packets->packet->length >> 24;
+				putchar_func(ctx, 1, &curchar);
+				curchar = (packets->packet->length >> 16) & 0xFF;
+				putchar_func(ctx, 1, &curchar);
+				curchar = (packets->packet->length >> 8) & 0xFF;
+				putchar_func(ctx, 1, &curchar);
+				curchar = packets->packet->length & 0xFF;
+				putchar_func(ctx, 1, &curchar);
 			}
 		}
 
-		for (i = 0; i < packets->packet->length; i++) {
-			putchar_func(ctx, packets->packet->data[i]);
-		}
+		putchar_func(ctx, packets->packet->length, packets->packet->data);
+//		for (i = 0; i < packets->packet->length; i++) {
+//			putchar_func(ctx, packets->packet->data[i]);
+//		}
 		packets = packets->next;
 	}
 	return 0;
diff --git a/parsekey.h b/parsekey.h
index 113f14a..2bfaf72 100644
--- a/parsekey.h
+++ b/parsekey.h
@@ -68,7 +68,8 @@ int read_openpgp_stream(int (*getchar_func)(void *ctx, size_t count,
  *	This function uses putchar_func to write characters to an OpenPGP
  *	packet stream from a linked list of packets.
  */
-int write_openpgp_stream(int (*putchar_func)(void *ctx, unsigned char c),
+int write_openpgp_stream(int (*putchar_func)(void *ctx, size_t count,
+						unsigned char *c),
 				void *ctx,
 				struct openpgp_packet_list *packets);
 
diff --git a/pathtest.c b/pathtest.c
deleted file mode 100644
index 50dcdcf..0000000
--- a/pathtest.c
+++ /dev/null
@@ -1,65 +0,0 @@
-//#include <stdint.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "hash.h"
-#include "keydb.h"
-#include "stats.h"
-
-void dofindpath(uint64_t have, uint64_t want, bool html)
-{
-	struct stats_key *keyinfoa, *keyinfob, *curkey;
-	int rec;
-
-	/*
-	 * Make sure the key we have and want are in the cache.
-	 */
-	hash_getkeysigs(have);
-	hash_getkeysigs(want);
-
-	if ((keyinfoa = findinhash(have)) == NULL) {
-		printf("550 Couldn't find key 0x%llX.\n", have);
-		return;
-	}
-	if ((keyinfob = findinhash(want)) == NULL) {
-		printf("550 Couldn't find key 0x%llX.\n", want);
-		return;
-	}
-	
-	/*
-	 * Fill the tree info up.
-	 */
-	initcolour(true);
-	rec = findpath(keyinfoa, keyinfob);
-	keyinfob->parent = 0;
-
-	printf("%d nodes examined. %ld elements in the hash\n", rec,
-			hashelements());
-	if (keyinfoa->colour == 0) {
-		printf("550 Can't find a link from 0x%llX to 0x%llX\n",
-				have,
-				want);
-	} else {
-		printf("250-%d steps from 0x%llX to 0x%llX\n",
-				keyinfoa->colour, have, want);
-		curkey = keyinfoa;
-		while (curkey != NULL) {
-			printf("250-0x%llX (%s)\n",
-					curkey->keyid,
-					keyid2uid(curkey->keyid));
-			curkey = findinhash(curkey->parent);
-		}
-	}
-}
-
-int main(int argc, char *argv[])
-{
-	initdb();
-	inithash();
-	dofindpath(0x5B430367, 0x3E1D0C1C, false);
-	dofindpath(0x3E1D0C1C, 0x5B430367, false);
-	cleanupdb();
-
-	return EXIT_SUCCESS;
-}
diff --git a/stats.c b/stats.c
index 0e2662b..b302f09 100644
--- a/stats.c
+++ b/stats.c
@@ -6,6 +6,7 @@
  * Copyright 2000-2002 Project Purple
  */
 
+#include <stdio.h>
 #include <stdlib.h>
 
 #include "hash.h"
@@ -87,6 +88,7 @@ unsigned long findpath(struct stats_key *have, struct stats_key *want)
 			nextkeys = NULL;
 			curdegree++;
 		}
+		fprintf(stderr, "Hash contains %ld keys.\n", hashelements());
 	}
 
 	return count;