cscvs to tla changeset 1
authorJonathan McDowell <noodles@earth.li>
Mon, 31 May 2004 23:46:40 +0000 (23:46 +0000)
committerJonathan McDowell <noodles@earth.li>
Mon, 31 May 2004 23:46:40 +0000 (23:46 +0000)
Author: noodles
Date: 2002/09/08 08:49:53
Initial revision

69 files changed:
BUGS [new file with mode: 0644]
CGI [new file with mode: 0644]
HISTORY [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
add.c [new file with mode: 0644]
armor.c [new file with mode: 0644]
armor.h [new file with mode: 0644]
bithelp.h [new file with mode: 0644]
getcgi.c [new file with mode: 0644]
getcgi.h [new file with mode: 0644]
gpgstats-0.0.2/Makefile [new file with mode: 0644]
gpgstats-0.0.2/dotrees.c [new file with mode: 0644]
gpgstats-0.0.2/filt.pl [new file with mode: 0755]
gpgstats-0.0.2/gpgpre [new file with mode: 0755]
gpgstats-0.0.2/gpgprecolons [new file with mode: 0755]
gpgstats-0.0.2/gpgstats.c [new file with mode: 0644]
gpgstats-0.0.2/gpgstats.h [new file with mode: 0644]
gpgstats-0.0.2/graphstuff.c [new file with mode: 0644]
gpgstats-0.0.2/graphstuff.h [new file with mode: 0644]
gpgstats-0.0.2/parse.c [new file with mode: 0644]
gpgstats-0.0.2/parse.h [new file with mode: 0644]
gpgstats-0.0.2/sig2dot.pl [new file with mode: 0755]
gpgwww.c [new file with mode: 0644]
hash.c [new file with mode: 0644]
hash.h [new file with mode: 0644]
index.html [new file with mode: 0644]
keydb.c [new file with mode: 0644]
keydb.h [new file with mode: 0644]
keydb_db2.c [new file with mode: 0644]
keydb_file.c [new file with mode: 0644]
keydb_pg.c [new file with mode: 0644]
keyid.c [new file with mode: 0644]
keyid.h [new file with mode: 0644]
keyindex.c [new file with mode: 0644]
keyindex.h [new file with mode: 0644]
keymerge.c [new file with mode: 0644]
keys/autodns.key [new file with mode: 0644]
keys/blackcat.key [new file with mode: 0644]
keys/iwj.key [new file with mode: 0644]
keys/manysubkeys.key [new file with mode: 0644]
keys/mjr.new [new file with mode: 0644]
keys/noodles.key [new file with mode: 0644]
keys/swhite.key [new file with mode: 0644]
keys/v4.key [new file with mode: 0644]
keystructs.h [new file with mode: 0644]
ll.c [new file with mode: 0644]
ll.h [new file with mode: 0644]
lookup.c [new file with mode: 0644]
main.c [new file with mode: 0644]
mathopd.conf [new file with mode: 0644]
maxpath.c [new file with mode: 0644]
md5.c [new file with mode: 0644]
md5.h [new file with mode: 0644]
mem.c [new file with mode: 0644]
mem.h [new file with mode: 0644]
merge.c [new file with mode: 0644]
merge.h [new file with mode: 0644]
onak.sql [new file with mode: 0644]
parsekey.c [new file with mode: 0644]
parsekey.h [new file with mode: 0644]
pathtest.c [new file with mode: 0644]
sha.c [new file with mode: 0644]
sha.h [new file with mode: 0644]
stats.c [new file with mode: 0644]
stats.h [new file with mode: 0644]
strace.out [new file with mode: 0644]

diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..f84368e
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,11 @@
+gpgwww:
+
+> Sometimes I just get:
+> Looking for path from 0xDF5CE2B4 to 0xB2713C8
+> Does that mean there is no path?
+
+Failing assert. Need to investigate.
+
+> And it takes forever (well it's not returned yet and I'm not really
+> expecting it to) on DF5CE2B4 to ED9547ED
+
diff --git a/CGI b/CGI
new file mode 100644 (file)
index 0000000..7e1980c
--- /dev/null
+++ b/CGI
@@ -0,0 +1,12 @@
+
+/pks/lookup:
+
+op             index | vindex | get    required
+search                                 required
+fingerprint    boolean (true == on)
+exact          boolean
+
+
+/pks/add:
+
+keytext                for ADD.                required
diff --git a/HISTORY b/HISTORY
new file mode 100644 (file)
index 0000000..bbcf69c
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,4 @@
+0.0.1 - 16th May 2002.
+
+* First release.
+* Merges gpgstats 0.0.2 (never released).
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..b2852a9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,280 @@
+GNU GENERAL PUBLIC LICENSE
+
+   Version 2, June 1991
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+Preamble
+
+   The licenses for most software are designed to take away your freedom
+   to share and change it. By contrast, the GNU General Public License is
+   intended to guarantee your freedom to share and change free
+   software--to make sure the software is free for all its users. This
+   General Public License applies to most of the Free Software
+   Foundation's software and to any other program whose authors commit to
+   using it. (Some other Free Software Foundation software is covered by
+   the GNU Library General Public License instead.) You can apply it to
+   your programs, too.
+   
+   When we speak of free software, we are referring to freedom, not
+   price. Our General Public Licenses are designed to make sure that you
+   have the freedom to distribute copies of free software (and charge for
+   this service if you wish), that you receive source code or can get it
+   if you want it, that you can change the software or use pieces of it
+   in new free programs; and that you know you can do these things.
+   
+   To protect your rights, we need to make restrictions that forbid
+   anyone to deny you these rights or to ask you to surrender the rights.
+   These restrictions translate to certain responsibilities for you if
+   you distribute copies of the software, or if you modify it.
+   
+   For example, if you distribute copies of such a program, whether
+   gratis or for a fee, you must give the recipients all the rights that
+   you have. You must make sure that they, too, receive or can get the
+   source code. And you must show them these terms so they know their
+   rights.
+   
+   We protect your rights with two steps: (1) copyright the software, and
+   (2) offer you this license which gives you legal permission to copy,
+   distribute and/or modify the software.
+   
+   Also, for each author's protection and ours, we want to make certain
+   that everyone understands that there is no warranty for this free
+   software. If the software is modified by someone else and passed on,
+   we want its recipients to know that what they have is not the
+   original, so that any problems introduced by others will not reflect
+   on the original authors' reputations.
+   
+   Finally, any free program is threatened constantly by software
+   patents. We wish to avoid the danger that redistributors of a free
+   program will individually obtain patent licenses, in effect making the
+   program proprietary. To prevent this, we have made it clear that any
+   patent must be licensed for everyone's free use or not licensed at
+   all.
+   
+   The precise terms and conditions for copying, distribution and
+   modification follow.
+   
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+   0. This License applies to any program or other work which contains a
+   notice placed by the copyright holder saying it may be distributed
+   under the terms of this General Public License. The "Program", below,
+   refers to any such program or work, and a "work based on the Program"
+   means either the Program or any derivative work under copyright law:
+   that is to say, a work containing the Program or a portion of it,
+   either verbatim or with modifications and/or translated into another
+   language. (Hereinafter, translation is included without limitation in
+   the term "modification".) Each licensee is addressed as "you".
+   
+   Activities other than copying, distribution and modification are not
+   covered by this License; they are outside its scope. The act of
+   running the Program is not restricted, and the output from the Program
+   is covered only if its contents constitute a work based on the Program
+   (independent of having been made by running the Program). Whether that
+   is true depends on what the Program does.
+   
+   1. You may copy and distribute verbatim copies of the Program's source
+   code as you receive it, in any medium, provided that you conspicuously
+   and appropriately publish on each copy an appropriate copyright notice
+   and disclaimer of warranty; keep intact all the notices that refer to
+   this License and to the absence of any warranty; and give any other
+   recipients of the Program a copy of this License along with the
+   Program.
+   
+   You may charge a fee for the physical act of transferring a copy, and
+   you may at your option offer warranty protection in exchange for a
+   fee.
+   
+   2. You may modify your copy or copies of the Program or any portion of
+   it, thus forming a work based on the Program, and copy and distribute
+   such modifications or work under the terms of Section 1 above,
+   provided that you also meet all of these conditions:
+   
+     * a) You must cause the modified files to carry prominent notices
+       stating that you changed the files and the date of any change.
+     * b) You must cause any work that you distribute or publish, that in
+       whole or in part contains or is derived from the Program or any
+       part thereof, to be licensed as a whole at no charge to all third
+       parties under the terms of this License.
+     * c) If the modified program normally reads commands interactively
+       when run, you must cause it, when started running for such
+       interactive use in the most ordinary way, to print or display an
+       announcement including an appropriate copyright notice and a
+       notice that there is no warranty (or else, saying that you provide
+       a warranty) and that users may redistribute the program under
+       these conditions, and telling the user how to view a copy of this
+       License. (Exception: if the Program itself is interactive but does
+       not normally print such an announcement, your work based on the
+       Program is not required to print an announcement.)
+       
+   These requirements apply to the modified work as a whole. If
+   identifiable sections of that work are not derived from the Program,
+   and can be reasonably considered independent and separate works in
+   themselves, then this License, and its terms, do not apply to those
+   sections when you distribute them as separate works. But when you
+   distribute the same sections as part of a whole which is a work based
+   on the Program, the distribution of the whole must be on the terms of
+   this License, whose permissions for other licensees extend to the
+   entire whole, and thus to each and every part regardless of who wrote
+   it.
+   
+   Thus, it is not the intent of this section to claim rights or contest
+   your rights to work written entirely by you; rather, the intent is to
+   exercise the right to control the distribution of derivative or
+   collective works based on the Program.
+   
+   In addition, mere aggregation of another work not based on the Program
+   with the Program (or with a work based on the Program) on a volume of
+   a storage or distribution medium does not bring the other work under
+   the scope of this License.
+   
+   3. You may copy and distribute the Program (or a work based on it,
+   under Section 2) in object code or executable form under the terms of
+   Sections 1 and 2 above provided that you also do one of the following:
+     * a) Accompany it with the complete corresponding machine-readable
+       source code, which must be distributed under the terms of Sections
+       1 and 2 above on a medium customarily used for software
+       interchange; or,
+     * b) Accompany it with a written offer, valid for at least three
+       years, to give any third party, for a charge no more than your
+       cost of physically performing source distribution, a complete
+       machine-readable copy of the corresponding source code, to be
+       distributed under the terms of Sections 1 and 2 above on a medium
+       customarily used for software interchange; or,
+     * c) Accompany it with the information you received as to the offer
+       to distribute corresponding source code. (This alternative is
+       allowed only for noncommercial distribution and only if you
+       received the program in object code or executable form with such
+       an offer, in accord with Subsection b above.)
+       
+   The source code for a work means the preferred form of the work for
+   making modifications to it. For an executable work, complete source
+   code means all the source code for all modules it contains, plus any
+   associated interface definition files, plus the scripts used to
+   control compilation and installation of the executable. However, as a
+   special exception, the source code distributed need not include
+   anything that is normally distributed (in either source or binary
+   form) with the major components (compiler, kernel, and so on) of the
+   operating system on which the executable runs, unless that component
+   itself accompanies the executable.
+   
+   If distribution of executable or object code is made by offering
+   access to copy from a designated place, then offering equivalent
+   access to copy the source code from the same place counts as
+   distribution of the source code, even though third parties are not
+   compelled to copy the source along with the object code.
+   
+   4. You may not copy, modify, sublicense, or distribute the Program
+   except as expressly provided under this License. Any attempt otherwise
+   to copy, modify, sublicense or distribute the Program is void, and
+   will automatically terminate your rights under this License. However,
+   parties who have received copies, or rights, from you under this
+   License will not have their licenses terminated so long as such
+   parties remain in full compliance.
+   
+   5. You are not required to accept this License, since you have not
+   signed it. However, nothing else grants you permission to modify or
+   distribute the Program or its derivative works. These actions are
+   prohibited by law if you do not accept this License. Therefore, by
+   modifying or distributing the Program (or any work based on the
+   Program), you indicate your acceptance of this License to do so, and
+   all its terms and conditions for copying, distributing or modifying
+   the Program or works based on it.
+   
+   6. Each time you redistribute the Program (or any work based on the
+   Program), the recipient automatically receives a license from the
+   original licensor to copy, distribute or modify the Program subject to
+   these terms and conditions. You may not impose any further
+   restrictions on the recipients' exercise of the rights granted herein.
+   You are not responsible for enforcing compliance by third parties to
+   this License.
+   
+   7. If, as a consequence of a court judgment or allegation of patent
+   infringement or for any other reason (not limited to patent issues),
+   conditions are imposed on you (whether by court order, agreement or
+   otherwise) that contradict the conditions of this License, they do not
+   excuse you from the conditions of this License. If you cannot
+   distribute so as to satisfy simultaneously your obligations under this
+   License and any other pertinent obligations, then as a consequence you
+   may not distribute the Program at all. For example, if a patent
+   license would not permit royalty-free redistribution of the Program by
+   all those who receive copies directly or indirectly through you, then
+   the only way you could satisfy both it and this License would be to
+   refrain entirely from distribution of the Program.
+   
+   If any portion of this section is held invalid or unenforceable under
+   any particular circumstance, the balance of the section is intended to
+   apply and the section as a whole is intended to apply in other
+   circumstances.
+   
+   It is not the purpose of this section to induce you to infringe any
+   patents or other property right claims or to contest validity of any
+   such claims; this section has the sole purpose of protecting the
+   integrity of the free software distribution system, which is
+   implemented by public license practices. Many people have made
+   generous contributions to the wide range of software distributed
+   through that system in reliance on consistent application of that
+   system; it is up to the author/donor to decide if he or she is willing
+   to distribute software through any other system and a licensee cannot
+   impose that choice.
+   
+   This section is intended to make thoroughly clear what is believed to
+   be a consequence of the rest of this License.
+   
+   8. If the distribution and/or use of the Program is restricted in
+   certain countries either by patents or by copyrighted interfaces, the
+   original copyright holder who places the Program under this License
+   may add an explicit geographical distribution limitation excluding
+   those countries, so that distribution is permitted only in or among
+   countries not thus excluded. In such case, this License incorporates
+   the limitation as if written in the body of this License.
+   
+   9. The Free Software Foundation may publish revised and/or new
+   versions of the General Public License from time to time. Such new
+   versions will be similar in spirit to the present version, but may
+   differ in detail to address new problems or concerns.
+   
+   Each version is given a distinguishing version number. If the Program
+   specifies a version number of this License which applies to it and
+   "any later version", you have the option of following the terms and
+   conditions either of that version or of any later version published by
+   the Free Software Foundation. If the Program does not specify a
+   version number of this License, you may choose any version ever
+   published by the Free Software Foundation.
+   
+   10. If you wish to incorporate parts of the Program into other free
+   programs whose distribution conditions are different, write to the
+   author to ask for permission. For software which is copyrighted by the
+   Free Software Foundation, write to the Free Software Foundation; we
+   sometimes make exceptions for this. Our decision will be guided by the
+   two goals of preserving the free status of all derivatives of our free
+   software and of promoting the sharing and reuse of software generally.
+   
+   NO WARRANTY
+   
+   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
+   WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+   EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+   OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
+   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+   PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+   PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
+   THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+   
+   12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+   WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+   AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
+   FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+   CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+   PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+   RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+   FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
+   SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+   DAMAGES.
+   
+END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..aacd7a8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,55 @@
+# Makefile for onak.
+
+CC = gcc
+CFLAGS += -Wall -pedantic -g -I/usr/local/include
+# 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
+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
+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
+
+all: $(PROGS) testparse pathtest 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)
+
+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
+       $(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 $(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
+       $(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 $(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
+       $(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 $(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
+
+depend:
+       makedepend $(SRCS)
+
+# DO NOT DELETE
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..dd3ced1
--- /dev/null
+++ b/README
@@ -0,0 +1,26 @@
+onak 0.0.1
+
+First release. You're mostly on your own folks.
+
+Developed under Linux & FreeBSD. Not currently autoconfed, so might need some
+tweaking to compile under anything else (or indeed either of those with header
+files in different places).
+
+The DB2 support for pksd databases is read only at present. It was knocked up
+in an hour to get the pathfinder working with the wwwkeys.uk.pgp.net DB.
+
+File database backend support is good for testing.
+
+It's running on hell.on.earth.li; currently with just under 400 keys in the
+database (Postgres backend). This isn't merging with anything at present
+though; that's the next step which is sort of there.
+
+You want to put add & lookup in a /pks directory under a web server running on
+port 11371 to make gpg happy. There's a mathopd.conf file that I used for
+testing, however I'm now running it under Apache for the public test rig as
+that host already runs it.
+
+Really needs a config file so all the hardcoded stuff isn't any more.
+
+Patches to do stuff welcome. Bug reports welcome. Don't expect quick responses
+though. ;)
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..cd1005d
--- /dev/null
+++ b/TODO
@@ -0,0 +1,28 @@
+* Search on key text.
+* Revoked keys on index.
+* Check keys on import?
+* Test library?
+* Better signature subpacket parsing (primary UID for example).
+* Better txt2html routine.
+* Remove bithelp.h (i386 only at present & inlined).
+* 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'")
+  Suggested by Jochen Voss <voss@mathematik.uni-kl.de>.
+* Other stats. sixdegrees? with piccy? most signed? signs most?
+* DB access modules as a library to allow runtime configuration of it?
+* Clean up gcc warnings (`ll' length modifier especially! Also inline & wrong
+  signedness for lo_read/write)
+* Webpages should bt UTF-8?
+* autoconf
+* config file
+
+Must do before initial release:
+
+* Key deletion (needed for updating keys).
+* Fix output to make GPG happy with --keyserver.
+* Check freeing.
+* More comments.
+* Sort out merging (use keymerge + some Perl to answer incoming email. Not
+  sure about keys via hkp yet though).
diff --git a/add.c b/add.c
new file mode 100644 (file)
index 0000000..323373b
--- /dev/null
+++ b/add.c
@@ -0,0 +1,85 @@
+/*
+ * add.c - CGI to add keys.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "armor.h"
+#include "getcgi.h"
+#include "keydb.h"
+#include "keystructs.h"
+#include "parsekey.h"
+
+struct cgi_get_ctx {
+       char *buffer;
+       int offset;
+};
+
+
+int cgi_getchar(void *ctx, unsigned char *c)
+{
+       struct cgi_get_ctx *buf = NULL;
+
+       buf = (struct cgi_get_ctx *) ctx;
+
+       *c = buf->buffer[buf->offset++];
+
+       return (*c == 0);
+}
+
+int main(int argc, char *argv[])
+{
+       struct openpgp_packet_list *packets = NULL;
+       struct openpgp_publickey *keys = NULL;
+       struct openpgp_publickey *curkey = NULL;
+       char **params = NULL;
+       struct cgi_get_ctx ctx;
+       int i;
+
+       memset(&ctx, 0, sizeof(ctx));
+
+       params = getcgivars(argc, argv);
+       for (i = 0; params != NULL && params[i] != NULL; i += 2) {
+               if (!strcmp(params[i], "keytext")) {
+                       ctx.buffer = params[i+1];
+               }
+       }
+
+//     puts("HTTP/1.0 200 OK");
+//     puts("Server: onak 0.0.1");
+       puts("Content-Type: text/html\n");
+       puts("<html><title>onak : Add</title><body>");
+       if (ctx.buffer == NULL) {
+               puts("Error: No keytext to add supplied.");
+       } else {
+               dearmor_openpgp_stream(cgi_getchar,
+                                       &ctx,
+                                       &packets);
+               if (packets != NULL) {
+                       parse_keys(packets, &keys);
+                       curkey = keys;
+                       initdb();
+                       while (curkey != NULL) {
+                               if (store_key(curkey)) {
+//                                     puts("Key added successfully.");
+                               } else {
+                                       printf("Problem adding key '%s'.\n", strerror(errno));
+                               }
+                               curkey = curkey->next;
+                       }
+                       cleanupdb();
+                       puts("Keys added.");
+               } else {
+                       puts("No OpenPGP packets found in input.");
+               }
+       }
+       puts("</body></html>");
+       return (EXIT_SUCCESS);
+}
diff --git a/armor.c b/armor.c
new file mode 100644 (file)
index 0000000..e41cbad
--- /dev/null
+++ b/armor.c
@@ -0,0 +1,386 @@
+/*
+ * armor.c - Routines to (de)armor OpenPGP packet streams.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "armor.h"
+#include "keystructs.h"
+#include "parsekey.h"
+
+#define ARMOR_WIDTH 64
+
+#define CRC24_INIT 0xb704ceL
+#define CRC24_POLY 0x1864cfbL
+
+/**
+ *
+ */
+static unsigned char encode64(unsigned char c) {
+       if (c <= 25) {
+               c += 'A';
+       } else if (c >= 26 && c <= 51) {
+               c += 'a' - 26;
+       } else if (c >= 52 && c <= 61) {
+               c += '0' - 52;
+       } else if (c == 62) {
+               c = '+';
+       } else if (c == 63) {
+               c = '/';
+       } else {
+               assert(c < 64);
+       }
+
+       return c;
+}
+
+/**
+ *
+ */
+static unsigned char decode64(unsigned char c) {
+       if (c >= 'A' && c <= 'Z') {
+               c -= 'A';
+       } else if (c >= 'a' && c <= 'z') {
+               c -= 'a' - 26;
+       } else if (c >= '0' && c <= '9') {
+               c -= '0' - 52;
+       } else if (c == '+') {
+               c = 62;
+       } else if (c == '/') {
+               c = 63;
+       } else if (c == '=' || c == '-') {
+               c = 64;
+       } else {
+               c = 65;
+       }
+
+       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).
+ *     @count: The number of octets we've seen.
+ *     @crc24: A running CRC24 of the data we've seen.
+ *     @putchar_func: The function to output a character.
+ *     @ctx: Context for putchar_func.
+ */
+struct armor_context {
+       unsigned char lastoctet;
+       int curoctet;
+       int count;
+       long crc24;
+       int (*putchar_func)(void *ctx, unsigned char c);
+       void *ctx;
+};
+
+static void armor_init(struct armor_context *ctx)
+{
+       ctx->curoctet = 0;
+       ctx->lastoctet = 0;
+       ctx->count = 0;
+       ctx->crc24 = CRC24_INIT;
+}
+
+static void armor_finish(struct armor_context *state)
+{
+       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, '=');
+               break;
+       case 2:
+               state->putchar_func(state->ctx,
+                       encode64((state->lastoctet & 0xF) << 2));
+               state->putchar_func(state->ctx, '=');
+               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');
+
+}
+
+static int armor_putchar(void *ctx, unsigned char c)
+{
+       struct armor_context *state;
+       int i;
+
+       assert(ctx != NULL);
+       state = (struct armor_context *) ctx;
+
+       switch (state->curoctet++) {
+       case 0:
+               state->putchar_func(state->ctx, encode64(c >> 2));
+               state->count++;
+               break;
+       case 1:
+               state->putchar_func(state->ctx,
+                       encode64(((state->lastoctet & 3) << 4) + (c >> 4)));
+               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));
+               state->count += 2;
+               break;
+       }
+       state->curoctet %= 3;
+       state->lastoctet = c;
+       
+       state->crc24 ^= c << 16;
+       for (i = 0; i < 8; i++) {
+               state->crc24 <<= 1;
+               if (state->crc24 & 0x1000000) {
+                       state->crc24 ^= CRC24_POLY;
+               }
+       }
+
+       if ((state->count % ARMOR_WIDTH) == 0) {
+               state->putchar_func(state->ctx, '\n');
+       }
+
+       return 0;
+}
+
+/**
+ *     @lastoctet: The last octet we got.
+ *     @curoctet: The current octet we're expecting (0, 1 or 2).
+ *     @count: The number of octets we've seen.
+ *     @crc24: A running CRC24 of the data we've seen.
+ *     @putchar_func: The function to output a character.
+ *     @ctx: Context for putchar_func.
+ */
+struct dearmor_context {
+       unsigned char lastoctet;
+       int curoctet;
+       int count;
+       long crc24;
+       int (*getchar_func)(void *ctx, unsigned char *c);
+       void *ctx;
+};
+
+static void dearmor_init(struct dearmor_context *ctx)
+{
+       ctx->curoctet = 0;
+       ctx->lastoctet = 0;
+       ctx->count = 0;
+       ctx->crc24 = CRC24_INIT;
+}
+
+static void dearmor_finish(struct dearmor_context *state)
+{
+       // Check the checksum,
+
+       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));
+
+}
+
+
+static int dearmor_getchar(void *ctx, unsigned char *c)
+{
+       struct dearmor_context *state;
+       unsigned char tmpc;
+       int i;
+
+       assert(ctx != NULL);
+       state = (struct dearmor_context *) ctx;
+       *c = 0;
+       
+       tmpc = 65;
+       while (tmpc == 65) {
+               state->getchar_func(state->ctx, &tmpc);
+               tmpc = decode64(tmpc);
+       }
+
+       if (tmpc != 64) {
+               switch (state->curoctet++) {
+               case 0:
+                       state->lastoctet = tmpc;
+                       tmpc = 65;
+                       while (tmpc == 65) {
+                               state->getchar_func(state->ctx, &tmpc);
+                               tmpc = decode64(tmpc);
+                       }
+                       *c = (state->lastoctet << 2) + (tmpc >> 4);
+                       break;
+               case 1:
+                       *c = ((state->lastoctet & 0xF) << 4) + (tmpc >> 2);
+                       break;
+               case 2:
+                       *c = ((state->lastoctet & 3) << 6) + tmpc;
+                       break;
+               }
+       
+               state->curoctet %= 3;
+               state->lastoctet = tmpc;
+               state->count++;
+               
+               state->crc24 ^= *c << 16;
+               for (i = 0; i < 8; i++) {
+                       state->crc24 <<= 1;
+                       if (state->crc24 & 0x1000000) {
+                               state->crc24 ^= CRC24_POLY;
+                       }
+               }
+       }
+
+       return (tmpc == 64);
+}
+
+static int dearmor_getchar_c(void *ctx, size_t count, unsigned char *c)
+{
+       int i, rc = 0;
+
+       for (i = 0; i < count && rc == 0; i++) {
+               rc = dearmor_getchar(ctx, &c[i]);
+       }
+
+       return rc;
+}
+
+/**
+ *     armor_openpgp_stream - Takes a list of OpenPGP packets and armors it.
+ *     @putchar_func: The function to output the next armor character.
+ *     @ctx: The context pointer for putchar_func.
+ *     @packets: The list of packets to output.
+ *
+ *     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),
+                               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");
+       
+       armor_init(&armor_ctx);
+       armor_ctx.putchar_func = putchar_func;
+       armor_ctx.ctx = ctx;
+       write_openpgp_stream(armor_putchar, &armor_ctx, packets);
+       armor_finish(&armor_ctx);
+
+       /*
+        * Print armor footer
+        */
+       putstring(putchar_func, ctx, "-----END PGP PUBLIC KEY BLOCK-----\n");
+
+       return 0;
+}
+
+/**
+ *     dearmor_openpgp_stream - Reads & decodes an ACSII armored OpenPGP msg.
+ *     @getchar_func: The function to get the next character from the stream.
+ *     @ctx: The context pointer for getchar_func.
+ *     @packets: The list of packets.
+ *
+ *     This function uses getchar_func to read characters from an ASCII
+ *     armored OpenPGP stream and outputs the data as a linked list of
+ *     packets.
+ */
+int dearmor_openpgp_stream(int (*getchar_func)(void *ctx, unsigned char *c),
+                               void *ctx,
+                               struct openpgp_packet_list **packets)
+{
+       struct dearmor_context dearmor_ctx;
+       unsigned char curchar;
+       int state = 0;
+       int count = 0;
+
+       /*
+        * Look for armor header. We want "-----BEGIN.*\n", then some headers
+        * with :s in them, then a blank line, then the data.
+        */
+       state = 1;
+       while (state != 4 && !getchar_func(ctx, &curchar)) {
+               switch (state) {
+                       case 0:
+                               if (curchar == '\n') {
+                                       count = 0;
+                                       state = 1;
+                               }
+                               break;
+                       case 1:
+                               if (curchar == '-') {
+                                       count++;
+                                       if (count == 5) {
+                                               state = 2;
+                                       }
+                               } else {
+                                       state = 0;
+                               }
+                               break;
+                       case 2:
+                               if (curchar == 'B') {
+                                       count = 0;
+                                       state = 3;
+                               } else {
+                                       state = 0;
+                               }
+                               break;
+                       case 3:
+                               if (curchar == '\n') {
+                                       count++;
+                                       if (count == 2) {
+                                               state = 4;
+                                       }
+                               } else {
+                                       count = 0;
+                               }
+                               break;
+               }
+       }
+
+       dearmor_init(&dearmor_ctx);
+       dearmor_ctx.getchar_func = getchar_func;
+       dearmor_ctx.ctx = ctx;
+       read_openpgp_stream(dearmor_getchar_c, &dearmor_ctx, packets);
+       dearmor_finish(&dearmor_ctx);
+       // TODO: Look for armor footer
+
+       return 0;
+}
diff --git a/armor.h b/armor.h
new file mode 100644 (file)
index 0000000..63f9198
--- /dev/null
+++ b/armor.h
@@ -0,0 +1,41 @@
+/*
+ * armor.h - Routines to (de)armor OpenPGP packet streams.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#ifndef __ARMOR_H__
+#define __ARMOR_H__
+
+#include "keystructs.h"
+
+/**
+ *     armor_openpgp_stream - Takes a list of OpenPGP packets and armors it.
+ *     @putchar_func: The function to output the next armor character.
+ *     @ctx: The context pointer for putchar_func.
+ *     @packets: The list of packets to output.
+ *
+ *     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),
+                               void *ctx,
+                               struct openpgp_packet_list *packets);
+
+/**
+ *     dearmor_openpgp_stream - Reads & decodes an ACSII armored OpenPGP msg.
+ *     @getchar_func: The function to get the next character from the stream.
+ *     @ctx: The context pointer for getchar_func.
+ *     @packets: The list of packets.
+ *
+ *     This function uses getchar_func to read characters from an ASCII
+ *     armored OpenPGP stream and outputs the data as a linked list of
+ *     packets.
+ */
+int dearmor_openpgp_stream(int (*getchar_func)(void *ctx, unsigned char *c),
+                               void *ctx,
+                               struct openpgp_packet_list **packets);
+
+#endif /* __ARMOR_H__ */
diff --git a/bithelp.h b/bithelp.h
new file mode 100644 (file)
index 0000000..ebb057f
--- /dev/null
+++ b/bithelp.h
@@ -0,0 +1,38 @@
+/* bithelp.h  -  Some bit manipulation helpers
+ *     Copyright (C) 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef __BITHELP_H__
+#define __BITHELP_H__
+
+/**
+ *     rol - Rotate a 32 bit integer by n bytes
+ *     @x: The integer to rotate.
+ *     @n: The number of bytes to rotate it by.
+ */
+static inline unsigned int
+rol(int x, int n)
+{
+       __asm__("roll %%cl,%0"
+               :"=r" (x)
+               :"0" (x),"c" (n));
+       return x;
+}
+
+#endif /* __BITHELP_H__ */
diff --git a/getcgi.c b/getcgi.c
new file mode 100644 (file)
index 0000000..47a94b2
--- /dev/null
+++ b/getcgi.c
@@ -0,0 +1,186 @@
+/*
+ * getcgivars.c - routine to read CGI input variables into an array.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * The x2c() and unescape_url() routines were lifted directly
+ * from NCSA's sample program util.c, packaged with their HTTPD.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "getcgi.h"
+
+/**
+ *     txt2html - Takes a string and converts it to HTML.
+ *     @string: The string to HTMLize.
+ *
+ *     Takes a string and escapes any HTML entities.
+ */
+char *txt2html(const char *string)
+{
+       static char buf[1024];
+       char *ptr = NULL;
+       char *nextptr = NULL;
+
+       buf[0] = 0;
+
+       ptr = strchr(string, '<');
+       if (ptr != NULL) {
+               nextptr = ptr + 1;
+               *ptr = 0;
+               strncpy(buf, string, 1023);
+               strncat(buf, "&lt;", 1023 - strlen(buf));
+               string = nextptr;
+       }
+
+       ptr = strchr(string, '>');
+       if (ptr != NULL) {
+               nextptr = ptr + 1;
+               *ptr = 0;
+               strncat(buf, string, 1023 - strlen(buf));
+               strncat(buf, "&gt;", 1023 - strlen(buf));
+               string = nextptr;
+       }
+       
+       /*
+        * TODO: We need to while() this really as each entity may appear more
+        * than once. We need to start with & and ; as we replace with those
+        * throughout. Fuck it for the moment though; it's Easter and < & > are
+        * the most common and tend to only appear once.
+        */
+
+       strncat(buf, string, 1023 - strlen(buf));
+
+       return buf;
+}
+
+/* Convert a two-char hex string into the char it represents */
+char x2c(char *what) 
+{
+       register char digit;
+
+       digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 :
+                                       (what[0] - '0'));
+       digit *= 16;
+       digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 :
+                                       (what[1] - '0'));
+       
+       return(digit);
+}
+
+/* Reduce any %xx escape sequences to the characters they represent */
+void unescape_url(char *url) 
+{
+       register int i,j;
+
+       for(i=0,j=0; url[j]; ++i,++j) {
+               if((url[i] = url[j]) == '%') {
+                       url[i]=x2c(&url[j+1]);
+                       j+=2;
+               }
+       }
+       
+       url[i] = '\0';
+}
+
+
+/* Read the CGI input and place all name/val pairs into list.        */
+/* Returns list containing name1, value1, name2, value2, ... , NULL  */
+char **getcgivars(int argc, char *argv[]) 
+{
+       int i;
+       char *request_method;
+       int content_length, paircount;
+       char *cgiinput;
+       char **cgivars;
+       char **pairlist;
+       char *nvpair,*eqpos;
+
+       /* Depending on the request method, read all CGI input into cgiinput */
+       /* (really should produce HTML error messages, instead of exit()ing) */
+
+       request_method=getenv("REQUEST_METHOD");
+       
+       if (request_method == NULL) {
+               if (argc > 1) {
+                       cgiinput = argv[1];
+               } else {
+                       return NULL;
+               }
+       } else if (strlen(request_method)==0) {
+               return NULL;
+       } else if (!strcmp(request_method, "GET") || !strcmp(request_method, "HEAD")) {
+               cgiinput=strdup(getenv("QUERY_STRING"));
+       } else if (!strcmp(request_method, "POST")) {
+               if (getenv("CONTENT_TYPE") != NULL &&
+                               strcasecmp(getenv("CONTENT_TYPE"),
+                                       "application/x-www-form-urlencoded")) {
+                       printf("getcgivars(): Unsupported Content-Type.\n");
+                       exit(1);
+               }
+               
+               if (!(content_length = atoi(getenv("CONTENT_LENGTH")))) {
+                       printf("getcgivars(): No Content-Length was sent with the POST request.\n");
+                       exit(1);
+               }
+               
+               if (!(cgiinput= (char *) malloc(content_length+1))) {
+                       printf("getcgivars(): Could not malloc for cgiinput.\n");
+                       exit(1);
+               }
+               
+               if (!fread(cgiinput, content_length, 1, stdin)) {
+                       printf("Couldn't read CGI input from STDIN.\n");
+                       exit(1);
+               }
+               
+               cgiinput[content_length]='\0';
+               
+       } else {
+               printf("getcgivars(): unsupported REQUEST_METHOD\n");
+               exit(1);
+       }
+
+       /* Change all plusses back to spaces */
+
+       for(i=0; cgiinput[i]; i++) if (cgiinput[i]=='+') cgiinput[i] = ' ';
+
+       /* First, split on "&" to extract the name-value pairs into pairlist */
+       pairlist=(char **) malloc(256*sizeof(char **));
+       paircount=0;
+       nvpair=strtok(cgiinput, "&");
+       while (nvpair) {
+               pairlist[paircount++]= strdup(nvpair) ;
+               if (!(paircount%256)) pairlist=(char **) realloc(pairlist,(paircount+256)*sizeof(char **));
+               nvpair=strtok(NULL, "&") ;
+       }
+
+       pairlist[paircount]=0;          /* terminate the list with NULL */
+
+       /* Then, from the list of pairs, extract the names and values */
+       
+       cgivars=(char **) malloc((paircount*2+1)*sizeof(char **));
+       
+       for (i=0; i<paircount; i++) {
+               if ((eqpos=strchr(pairlist[i], '='))!=NULL) {
+                       *eqpos='\0';
+                       unescape_url(cgivars[i*2+1]=strdup(eqpos+1));
+               } else {
+                       unescape_url(cgivars[i*2+1]=strdup(""));
+               }
+               unescape_url(cgivars[i*2]= strdup(pairlist[i])) ;
+       }
+
+       cgivars[paircount*2]=NULL;      /* terminate the list with NULL */
+    
+       /* Free anything that needs to be freed */
+       free(cgiinput);
+       for (i=0; pairlist[i]; i++) free(pairlist[i]);
+       free(pairlist);
+
+       /* Return the list of name-value strings */
+       return cgivars;
+}
diff --git a/getcgi.h b/getcgi.h
new file mode 100644 (file)
index 0000000..58d009b
--- /dev/null
+++ b/getcgi.h
@@ -0,0 +1,16 @@
+#ifndef __GETCGI_H_
+#define __GETCGI_H_
+
+/**
+ *     txt2html - Takes a string and converts it to HTML.
+ *     @string: The string to HTMLize.
+ *
+ *     Takes a string and escapes any HTML entities.
+ */
+char *txt2html(const char *string);
+
+char x2c(char *what); 
+void unescape_url(char *url); 
+char **getcgivars(int argc, char *argv[]);
+
+#endif /* __GETCGI_H_ */ 
diff --git a/gpgstats-0.0.2/Makefile b/gpgstats-0.0.2/Makefile
new file mode 100644 (file)
index 0000000..deed7ea
--- /dev/null
@@ -0,0 +1,18 @@
+# Makefile for gpgstats.
+
+CC = gcc
+OBJS = gpgstats.o hash.o ll.o parse.o graphstuff.o
+CFLAGS += -Wall -pedantic -g -I..
+# -DUSEREADLINE
+
+all:   gpgstats
+
+gpgstats: $(OBJS)
+       gcc -g -o gpgstats $(OBJS)
+# -lreadline -lhistory
+
+dotrees: $(OBJS) dotrees.o
+       gcc -g -o dotrees dotrees.o hash.o ll.o parse.o 
+       
+clean:
+       rm -f $(OBJS) gpgctl.o dotrees.o gpgstats *.core core
diff --git a/gpgstats-0.0.2/dotrees.c b/gpgstats-0.0.2/dotrees.c
new file mode 100644 (file)
index 0000000..0dec450
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+       gpgstats.c - Program to produce stats on a GPG keyring.
+       Written by Jonathan McDowell <noodles@earth.li>.
+
+       19/02/2000 - Started writing (sort of).
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gpgstats.h"
+#include "hash.h"
+#include "logging.h"
+#include "ll.h"
+#include "parse.h"
+
+struct ll *finished = NULL;
+struct ll *trees = NULL;
+
+unsigned long hex2long(char *string)
+{
+       size_t loop;
+       unsigned long value;
+
+       value = 0;
+       for (loop = 0; loop < 8 && loop < strlen(string) && string[loop] > ' ';
+                       loop++) {
+               value=((string[loop]>'9') ? toupper(string[loop])-'A'+10 :
+                                       string[loop]-'0')+(value << 4);
+       }
+       return value;
+}
+
+
+int keycmp(struct key *key1, struct key *key2)
+{
+       if (key1->keyid==key2->keyid) {
+               return 0;
+       }
+       return 1;
+}
+
+
+struct ll *addkey(struct ll *curkey, struct key newkey)
+{
+       return lladd(curkey, copyandaddtohash(newkey));
+}
+
+
+void initcolour(int pi) {
+       unsigned long loop;
+       struct ll *curkey;
+
+       /* Init the colour/pi hashes */
+       for (loop=0; loop<HASHSIZE; loop++) {
+               curkey = gethashtableentry(loop);
+               while (curkey!=NULL) {
+                       ((struct key *)curkey->object)->colour = 0;
+                       if (pi != NULL) {
+                               ((struct key *)curkey->object)->pi = NULL;
+                       }
+                       curkey = curkey->next;
+               }
+       }
+}
+
+
+void readkeys(const char *filename)
+{
+       char curline[1024];
+       unsigned long keys=0,sigs=0,pub=0, revoked=0;
+       struct key keyin;
+       struct key *curkey=NULL, *cursign=NULL;
+       struct key cursig;
+       FILE *keyfile;
+       int (*p)();
+
+       p=keycmp;
+       keyin.name=cursig.name=NULL;
+       keyin.sigs=keyin.signs=cursig.sigs=cursig.signs=NULL;
+       keyin.selfsigned=cursig.selfsigned=0;
+       keyin.revoked=cursig.revoked=0;
+
+       log(LOG_INFO, "Reading key info from '%s'.\n", filename);
+       if ((keyfile=fopen(filename, "r"))==NULL) {
+               perror("readkeys()");
+               return;
+       }
+       /* read a line */
+       fgets(curline, 1023, keyfile);
+       while (!feof(keyfile)) {        
+               if (curline[0]=='P') {
+                       ++pub;
+                       ++keys;
+                       printf("\rRead %ld keys so far.", keys);
+                       keyin.keyid=hex2long(&curline[1]);
+                       curkey=copyandaddtohash(keyin);
+                       if (curkey->keyid!=keyin.keyid) {
+                               printf("Erk! Didn't get back the key we asked for! %08lX != %08lX\n", curkey->keyid, keyin.keyid);
+                       }
+               } else if (curline[0]=='S') {
+                       cursig.keyid=hex2long(&curline[1]);
+                       if (curkey->keyid==cursig.keyid) {
+                               curkey->selfsigned=1;
+                       }
+
+                       if (!llfind(curkey->sigs, &cursig, p)) {
+                               curkey->sigs=addkey(curkey->sigs, cursig);
+                               ++sigs;
+                       }
+
+                       if ((cursign=findinhash(&cursig))==NULL) {
+                               cursign=copyandaddtohash(cursig);
+                       }
+                       if (cursign->keyid!=cursig.keyid) {
+                               printf("Erk! Didn't get back the key we asked for! %08lX != %08lX\n", cursign->keyid, cursig.keyid);
+                       }
+
+                       if (!llfind(cursign->signs, curkey, p))
+                               cursign->signs=addkey(cursign->signs, *curkey);
+               } else if (curline[0]=='N') {
+                       if (curkey->name==NULL) {
+                               curkey->name=strdup(&curline[1]);
+                               curkey->name[strlen(curkey->name)-1]=0;
+                               if (strcmp(curkey->name, "[revoked]")==0) {
+                                       curkey->revoked=1;
+                                       ++revoked;
+                               }
+                       }
+               }
+               fgets(curline, 1023, keyfile);
+       }
+       fclose(keyfile);
+       printf("\rRead %lu keys, %lu pub, %lu sigs, %lu revoked.\n",
+               keys, pub, sigs, revoked);
+}
+
+
+void DFSVisit(int type, struct key *key, unsigned long *time, unsigned long *depth)
+{
+       struct ll *curkey;
+       struct key *v;
+
+       key->colour=1;
+//     key->d=(*time)++;
+
+       if (type==0) curkey=key->signs; else curkey=key->sigs;
+       while (curkey!=NULL) {
+               v=(struct key *)findinhash(curkey->object);
+               if (v==NULL) {
+                       printf("Couldn't find key in hash. Most odd.\n");
+               }
+               if (v!=NULL && v->colour==0) {
+                       if (type==1 && key->pi==NULL) {
+                               printf("key->pi is NULL.\n");
+                       } else if (type==1) {
+                               key->pi->object=lladd(key->pi->object, v);
+                               v->pi=key->pi;
+                       }
+
+                       (*depth)++;
+                       DFSVisit(type, v, time, depth);
+               }
+               curkey=curkey->next;
+       }
+       key->colour=2;
+//     key->f=(*time)++;
+       if (type==0) finished=lladd(finished, key);
+}
+
+
+void DFS(void)
+{
+       unsigned long loop,time=0,depth,maxdepth=0;
+       struct ll *curkey;
+
+       initcolour(1);
+       for (loop=0; loop<HASHSIZE; loop++) {
+               curkey=gethashtableentry(loop);
+               while (curkey!=NULL) {
+                       if (((struct key *)curkey->object)->colour==0) {
+                               depth=0;
+                               DFSVisit(0, ((struct key *)curkey->object),
+                                       &time, &depth);
+                               if (depth>maxdepth) maxdepth=depth;
+                       }
+                       curkey=curkey->next;
+               }
+       }
+       printf("Max depth reached in DFS(): %ld\n", maxdepth);
+}
+
+
+void DFSsorted(void)
+{
+       unsigned long time=0,depth,maxdepth=0;
+       struct ll *curkey;
+
+       initcolour(1);
+       curkey=finished;
+       while (curkey!=NULL) {
+               if (((struct key *)curkey->object)->colour==0) {
+                       trees=lladd(trees, curkey->object);
+                       ((struct key *)curkey->object)->pi=trees;
+                       ((struct key *)curkey->object)->pi->object=lladd(NULL, curkey->object);
+
+                       depth=0;
+                       DFSVisit(1, ((struct key *)curkey->object),
+                               &time, &depth);
+                       if (depth>maxdepth) maxdepth=depth;
+               }
+               curkey=curkey->next;
+       }
+       printf("Max depth reached in DFSsorted(): %ld\n", maxdepth);
+}
+
+
+long checkselfsig()
+{
+       unsigned long loop;
+       struct ll *curkey;
+       unsigned long selfsig=0;
+
+       for (loop=0; loop<HASHSIZE; loop++) {
+               curkey=gethashtableentry(loop);
+               while (curkey!=NULL) {
+                       if (((struct key *)curkey->object)->selfsigned) ++selfsig;
+                       curkey=curkey->next;
+               }
+       }
+
+       return selfsig;
+}
+
+
+void printtrees(int minsize)
+{
+       struct ll *curtree,*curkey;
+       unsigned long count, total;
+
+       curtree=trees;
+       total=0;
+       while (curtree!=NULL) { 
+               curkey=curtree->object;
+               ++total;
+               count=0;
+               if (llsize(curkey)>=minsize) while (curkey!=NULL) {
+                       printf("0x%08lX (%s)\n", ((struct key *)curkey->object)->keyid,
+                               ((struct key *)curkey->object)->name);
+                       count++;
+                       curkey=curkey->next;
+               }
+               if (count>=minsize) printf("Tree size of %ld\n", count);
+               curtree=curtree->next;
+       }
+       printf("Total of %ld trees.\n", total);
+}
+
+
+unsigned long size2degree(struct ll *curll, struct key *prev, int sigs, int curdegree, int maxdegree, int *rec)
+{
+       unsigned long count=0;
+       struct ll *nextll;
+
+       ++curdegree;
+       ++(*rec);
+
+       nextll=NULL;
+       while (curll!=NULL) {
+               if (((struct key *) curll->object)->revoked==1) {
+                       /* It's revoked. Ignore it. */
+               } else if (((struct key *) curll->object)->colour==0) {
+                       /* We've never seen it. Count it, mark it and
+                               explore its subtree */
+                       count++;
+                       printf("0x%08lX (%s)\n", ((struct key *) curll->object)->keyid,
+                               ((struct key *) curll->object)->name);
+                       ((struct key *)curll->object)->colour=curdegree;
+                       ((struct key *)curll->object)->pi=(struct ll *) prev;
+                       nextll=lladd(nextll, curll->object);
+               } else if (((struct key *) curll->object)->colour>curdegree) {
+                       /* We've seen it, but it it's closer to us than we
+                               thought. Re-evaluate, but don't count it
+                               again */
+                       ((struct key *)curll->object)->colour=curdegree;
+                       ((struct key *)curll->object)->pi=(struct ll *) prev;
+                       nextll=lladd(nextll, curll->object);
+               }
+               curll=curll->next;
+       }
+       /* Now we've marked, let's recurse */
+       if (curdegree<maxdegree) curll=nextll; else curll=NULL;
+       while (curll!=NULL) {
+               if (sigs) {
+                       count += size2degree(((struct key *)curll->object)->sigs, curll->object, sigs, curdegree, maxdegree, rec);
+               } else {
+                       count += size2degree(((struct key *)curll->object)->signs, curll->object, sigs, curdegree, maxdegree, rec);
+               }
+               nextll=curll->next;
+               free(curll);
+               curll=nextll;
+       }
+
+       return count;
+}
+
+
+void sixdegrees(unsigned long keyid)
+{
+       struct key *keyinfo, key;
+       int loop;
+       int rec;
+
+       key.keyid=keyid;
+
+       if ((keyinfo=findinhash(&key))==NULL) {
+               printf("Couldn't find key 0x%08lX.\n", keyid);
+               return;
+       }
+
+       printf("Six degrees for 0x%08lX (%s):\n", keyinfo->keyid, keyinfo->name);
+
+       puts("\t\t   Signs       Signed by");
+       for (loop=1; loop<7; loop++) {
+               initcolour(0);
+               rec=0;
+               printf("Degree %d:\t%8ld", loop, size2degree(keyinfo->signs, NULL, 0, 0, loop, &rec));
+               printf(" (%d)", rec);
+               initcolour(0);
+               rec=0;
+               printf("\t%8ld", size2degree(keyinfo->sigs, NULL, 1, 0, loop, &rec));
+               printf(" (%d)\n", rec);
+       }
+}
+
+
+void showkeysigs(unsigned long keyid, int sigs)
+{
+       struct key *keyinfo, key;
+       struct ll *cursig;
+
+       key.keyid=keyid;
+
+       if ((keyinfo=findinhash(&key))==NULL) {
+               printf("Couldn't find key 0x%08lX.\n", keyid);
+               return;
+       }
+
+       printf("0x%08lX (%s) %s:\n", keyinfo->keyid, keyinfo->name,
+                       sigs ? "is signed by" : "signs");
+
+       if (sigs) cursig=keyinfo->sigs; else cursig=keyinfo->signs;
+       while (cursig!=NULL) {
+               printf("\t0x%08lX (%s)\n", ((struct key *)cursig->object)->keyid,
+                               ((struct key *)cursig->object)->name);
+               cursig=cursig->next;
+       }
+}
+
+
+void findpath(unsigned long keyida, unsigned long keyidb)
+{
+       struct key *keyinfoa, *keyinfob, *curkey, keya, keyb;
+       int rec;
+
+       keya.keyid=keyida;
+       keyb.keyid=keyidb;
+
+       if ((keyinfoa=findinhash(&keya))==NULL) {
+               printf("Couldn't find key 0x%08lX.\n", keyida);
+               return;
+       }
+       if ((keyinfob=findinhash(&keyb))==NULL) {
+               printf("Couldn't find key 0x%08lX.\n", keyidb);
+               return;
+       }
+
+       /* Fill the tree info up */
+       initcolour(1);
+       rec=0;
+       size2degree(keyinfoa->signs, keyinfoa, 0, 0, 1000, &rec);
+       keyinfoa->pi=NULL;
+
+       printf("%d recursions required.\n", rec);
+       if (keyinfob->colour==0) {
+               printf("Can't find a link from 0x%08lX to 0x%08lX\n", keyida, keyidb);
+       } else {
+               printf("%d steps from 0x%08lX to 0x%08lX\n", keyinfob->colour, keyida, keyidb);
+               curkey=keyinfob;
+               while (curkey!=NULL) {
+                       printf("0x%08lX (%s)\n", curkey->keyid, curkey->name);
+                       curkey=(struct key *)curkey->pi;
+               }
+       }
+}
+
+
+int main(int argc, char *argv[])
+{
+       struct key *keyinfo,foo;
+       int rec;
+
+       printf("gpgstats %s by Jonathan McDowell\n", VERSION);
+       puts("Copyright 2000 Project Purple. Released under the GPL.");
+       puts("A simple program to do stats on a GPG keyring.\n");
+       inithash();
+//     readkeys("keyfile");
+       readkeys("keyfile.debian");
+//     readkeys("../keyfile.big");
+       printf("%ld selfsigned.\n", checkselfsig());
+       printf("%ld distinct keys.\n", hashelements());
+
+       finished=trees=NULL;
+       printf("Starting first DFS.\n");
+       DFS();
+       printf("Starting second DFS.\n");
+       DFSsorted();
+       printtrees(2);
+
+//     foo.keyid=0xC7A966DD; /* Phil Zimmerman himself */
+//     if ((keyinfo=findinhash(&foo))==NULL) {
+//             printf("Couldn't find key 0x%08lX.\n", foo.keyid);
+//             return 1;
+//     }
+
+//     initcolour(0);
+//     rec=0;
+//     printf("%ld\n", size2degree(keyinfo->sigs, NULL, 0, 0, 1000, &rec));
+//     return 0;
+}
diff --git a/gpgstats-0.0.2/filt.pl b/gpgstats-0.0.2/filt.pl
new file mode 100755 (executable)
index 0000000..a7e4353
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/perl
+
+@keyfile=<>;
+
+$count=0;
+
+while ($count<scalar(@keyfile)) {
+       if ($keyfile[$count] =~ /^P/ &&
+               $keyfile[$count+1] =~ /^N/ &&
+               $keyfile[$count+2] =~ /^S/ &&
+               $keyfile[$count+3] =~ /^P/) {
+               $count = $count + 3;
+       } else {
+               print $keyfile[$count++];
+       }
+}
diff --git a/gpgstats-0.0.2/gpgpre b/gpgstats-0.0.2/gpgpre
new file mode 100755 (executable)
index 0000000..9f81af3
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -Tw
+# Written by Jonathan McDowell <noodles@earth.li>
+# Copyright 2000 Project Purple.
+# Dedicated to linguists called Simon everywhere.
+#
+# Processes the output of gpg -v --list-keys to a format gpgstats likes.
+#
+# Try:
+# gpg -v --list-keys | ./gpgpre | uniq > keyfile
+#
+# I should really include the uniq in the code.
+
+use strict;
+
+my ($curline);
+
+while ($curline = <>) {
+       chomp $curline;
+
+       if ($curline =~ /^pub.*\/([0-9a-fA-F]{8}) [0-9-\/]{10} (.*)/) {
+               print "P$1\n";
+               print "N$2\n";
+       } elsif ($curline =~ /^sig *([0-9a-fA-F]{8})/) {
+               print "S$1\n";
+       }
+}
diff --git a/gpgstats-0.0.2/gpgprecolons b/gpgstats-0.0.2/gpgprecolons
new file mode 100755 (executable)
index 0000000..94ba88a
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/perl -Tw
+# Written by Jonathan McDowell <noodles@earth.li>
+# Dedicated to linguists called Simon everywhere.
+#
+# An attempt at parsing the output of gpg -v --with-colons --list-keys
+# Not completed yet. Will replace gpgpre and allow info on keysize/type.
+
+use strict;
+
+my ($curline, $rsa, $dsa, $elg, $elgeo);
+
+$rsa=$dsa=$elg=$elgeo=0;
+
+while ($curline = <>) {
+       chomp $curline;
+
+       if ($curline =~ /^pub:.*:\d*:(\d*):([0-9a-fA-F]*):.*:.*:.*:.*:(.*):/) {
+               if ($1 == 1) {
+                       $rsa++;
+               } elsif ($1 == 16) {
+                       $elgeo++;
+               } elsif ($1 == 17) {
+                       $dsa++;
+               } elsif ($1 == 20) {
+                       $elg++;
+               }
+#              print "P$2\n";
+#              print "N$3\n";
+       } elsif ($curline =~ /^sig:.*:\d*:(\d*):([0-9a-fA-F]*):.*:.*:.*:.*:.*/) {
+#              print "S$2\n";
+       } elsif ($curline =~ /^uid:/) {
+               # Extra uid. Ignore.
+       } elsif ($curline =~ /^sub:/) {
+               # Subkey. Ignore.
+       } elsif ($curline =~ /^rev:/) {
+               # Unsure. Ignore.
+       } else {
+               print "$curline\n";
+       }
+}
+
+print "RSA keys: $rsa, DSA keys: $dsa, ELG encrypt-only: $elgeo, ELG: $elg\n";
diff --git a/gpgstats-0.0.2/gpgstats.c b/gpgstats-0.0.2/gpgstats.c
new file mode 100644 (file)
index 0000000..a728799
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+       gpgstats.c - Program to produce stats on a GPG keyring.
+       Written by Jonathan McDowell <noodles@earth.li>.
+
+       19/02/2000 - Started writing (sort of).
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef USEREADLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#include "gpgstats.h"
+#include "graphstuff.h"
+#include "hash.h"
+#include "keydb.h"
+#include "ll.h"
+#include "parse.h"
+#include "stats.h"
+
+struct keycount { unsigned long count; struct key *k; };
+
+extern struct ll *trees;
+extern struct ll *finished;
+
+void insertval(unsigned long val, struct keycount a[], struct key *curkey)
+{
+       int loop;
+
+       loop=9;
+       if (val<a[loop--].count) return;
+
+       while (val>a[loop].count && loop >= 0) {
+               a[loop+1]=a[loop];
+               loop--;
+       }
+       a[loop+1].count=val;
+       a[loop+1].k=curkey;
+}
+
+void printtrees(int minsize)
+{
+       struct ll *curtree,*curkey;
+       unsigned long count, total;
+
+       curtree=trees;
+       total=0;
+       while (curtree!=NULL) { 
+               curkey=curtree->object;
+               ++total;
+               count=0;
+               while (curkey!=NULL) {
+                       count++;
+                       curkey=curkey->next;
+               }
+               if (count>=minsize) {
+//                     log(LOG_INFO, "Tree size of %ld\n", count);
+               }
+               curtree=curtree->next;
+       }
+//     log(LOG_INFO, "Total of %ld trees.\n", total);
+}
+
+
+void sixdegrees(uint64_t keyid)
+{
+       struct stats_key *keyinfo;
+       int loop;
+       long degree;
+
+       if ((keyinfo = findinhash(keyid)) == NULL) {
+               printf("Couldn't find key 0x%llX.\n", keyid);
+               return;
+       }
+
+       printf("Six degrees for 0x%llX (%s):\n", keyinfo->keyid,
+                                                keyid2uid(keyinfo->keyid));
+
+       puts("\t\t   Signs       Signed by");
+       for (loop = 1; loop < 7; loop++) {
+               initcolour(false);
+               degree = countdegree(keyinfo, 0, loop);
+               printf("Degree %d:\t%8ld", loop, degree);
+               initcolour(false);
+               degree = countdegree(keyinfo, 1, loop);
+               printf("\t%8ld\n", degree);
+       }
+}
+
+
+void showkeysigs(uint64_t keyid, bool sigs)
+{
+       struct stats_key *keyinfo = NULL;
+       struct ll *cursig;
+       long count;
+
+       if ((keyinfo = findinhash(keyid)) == NULL) {
+               printf("Couldn't find key 0x%llX.\n", keyid);
+               return;
+       }
+
+       printf("0x%llX (%s) %s:\n", keyinfo->keyid, keyid2uid(keyinfo->keyid),
+                       sigs ? "is signed by" : "signs");
+
+       if (sigs) {
+               cursig = keyinfo->sigs;
+       } else {
+//             cursig = keyinfo->signs;
+       }
+       count=0;
+       while (cursig!=NULL) {
+               count++;
+               printf("\t0x%08lX (%s)\n",
+                               ((struct key *)cursig->object)->keyid,
+                       keyid2uid(((struct key *)cursig->object)->keyid));
+               cursig=cursig->next;
+       }
+
+       printf("\t%s a total of %ld keys.\n", sigs ? "Signed by" : "Signs",
+                                               count); 
+}
+
+void memstats()
+{
+       unsigned long loop, total, hash, hashmax, hashmin, cur, sigs, signs;
+       unsigned long names;
+       struct ll *curkey;
+
+       total=sigs=signs=hash=names=0;
+       hashmin=-1;
+       hashmax=0;
+
+       for (loop=0; loop<HASHSIZE; loop++) {
+               curkey=gethashtableentry(loop);
+               cur=llsize(curkey);
+               if (cur>hashmax) hashmax=cur;
+               if (cur<hashmin) hashmin=cur;
+               hash+=cur;
+               while (curkey!=NULL) {
+                       sigs+=llsize(((struct key *)curkey->object)->sigs);
+                       signs+=llsize(((struct key *)curkey->object)->signs);
+                       if (((struct key *)curkey->object)->name!=NULL)
+                       names+=strlen(((struct key *)curkey->object)->name);
+                       curkey=curkey->next;
+               }
+       }
+
+       printf("%10ld bytes in %ld keys\n", hash*sizeof(struct key), hash);
+       total += hash*sizeof(struct key);
+       printf("%10ld bytes in hash structure\n", hash*sizeof(struct ll));
+       total += hash*sizeof(struct ll);
+       printf("           (Max hash bucket %ld, min hash bucket %ld.)\n", hashmax, hashmin);
+       printf("%10ld bytes in %ld sigs.\n", sigs*sizeof(struct ll), sigs);
+       total += sigs*sizeof(struct ll);
+       printf("%10ld bytes in %ld signs.\n", signs*sizeof(struct ll), signs);
+       total += signs*sizeof(struct ll);
+       printf("%10ld bytes in names.\n", names);
+       total += names;
+       printf("%10ld bytes total.\n", total);
+}
+
+void showmostsigns(int sigs)
+{
+       unsigned long count,loop;
+       struct keycount signs[10];
+       struct ll *curkey;
+
+       memset(signs, 0, sizeof(signs));
+       // for (count=0; count<10; count++) { signs[count].count=0; };
+       count=0;
+       for (loop=0; loop<HASHSIZE; loop++) {
+               curkey=gethashtableentry(loop);
+               while (curkey!=NULL) {
+                       if (sigs) {
+                               count=llsize(((struct key *)curkey->object)->sigs);
+                       } else {
+                               count=llsize(((struct key *)curkey->object)->signs);
+                       }
+                       if (count != 0) {
+                               insertval(count, signs, (struct key *)curkey->object);
+                       }
+                       curkey=curkey->next;
+               }
+       }
+       
+       for (count=0; count<10; count++) {
+               if (signs[count].k != NULL) {
+                       printf("0x%08lX (%s) %s %ld keys.\n",
+                               signs[count].k->keyid, signs[count].k->name,
+                               sigs ? "is signed by" : "signs",
+                               signs[count].count);
+               }
+       }
+}
+
+void findmaxpath(unsigned long max)
+{
+       struct key *from, *to, *tmp;
+       struct ll *curkey;
+       unsigned long distance, loop;
+
+       printf("In findmaxpath\n");
+       distance=0;
+       from=to=NULL;
+       for (loop=0; loop<HASHSIZE && distance<max; loop++) {
+               curkey=gethashtableentry(loop);
+               while (curkey!=NULL && distance<max) {
+                       initcolour(false);
+                       tmp=furthestkey((struct key *)curkey->object);
+                       if (tmp->colour>distance) {
+                               from=(struct key *)curkey->object;
+                               to=tmp;
+                               distance=to->colour;
+                               printf("Current max path (#%ld) is from %08lX to %08lX (%ld steps)\n", loop, from->keyid, to->keyid, distance);
+                       }
+                       curkey=curkey->next;
+               }
+       }
+       printf("Max path is from %08lX to %08lX (%ld steps)\n",
+                       from->keyid,
+                       to->keyid,
+                       distance);
+}
+
+void showhelp(void)
+{
+       printf("gpgstats %s by Jonathan McDowell\n", VERSION);
+       puts("A simple program to do stats on a GPG keyring.\n");
+
+       puts("DFS <minsize>\t\tOutput details on the strongly connected");
+       puts("\t\t\tsubtrees, min size <minsize>");
+       puts("MAXPATH\t\t\tShow the two further apart keys.");
+       puts("MEMSTATS\t\tShow some stats about memory usage.");
+       puts("MOSTSIGNED\t\tShow the 10 keys signed by most others.");
+       puts("PATH <keyida> <keyidb>\tShows the path of trust (if any) from.");
+       puts("\t\t\tkeyida to keyidb (ie I have keyida, I want keyidb).");
+       puts("QUIT\t\t\tQuits the program.");
+       puts("READ <file>\t\tRead <file> in and add to the loaded keyring.");
+       puts("SIGNS <keyid>\t\tShows the keys that the given key signs.");
+       puts("SIGNSMOST\t\tShow the 10 keys that sign most other keys.");
+       puts("SIGS <keyid>\t\tShows the signatures on the given key.");
+       puts("SIXDEGREES <keyid>\tShows the 6 degrees from the given keyid.");
+       puts("STATS\t\t\tDisplay some stats about the loaded keyring.");
+}
+
+void commandloop(void)
+{
+       struct cfginf commands[]={{"QUIT", 0,  NULL},
+                               {"READ", 1, NULL},
+                               {"SIXDEGREES", 1, NULL},
+                               {"PATH", 1, NULL},
+                               {"SIGS", 1, NULL},
+                               {"SIGNS", 1, NULL},
+                               {"STATS", 0, NULL},
+                               {"HELP", 0, NULL},
+                               {"DFS", 1, NULL},
+                               {"SIGNSMOST", 0, NULL},
+                               {"MOSTSIGNED", 0, NULL},
+                               {"MEMSTATS", 0, NULL},
+                               {"MAXPATH", 1, NULL}};
+       char tmpstr[1024];
+       char *param;
+       int cmd;
+
+       commands[1].var=commands[2].var=commands[3].var=&param;
+       commands[4].var=commands[5].var=commands[8].var=&param;
+       commands[12].var=&param;
+
+       do {
+               memset(tmpstr, 0, 1023);
+               fgets(tmpstr, 1023, stdin);
+//             printf("Read: '%s'\n", tmpstr);
+               cmd=parseline(commands, tmpstr);
+//             printf("Got command: '%d'\n", cmd);
+//             printf("Got command: '%d'\n", cmd);
+
+               switch (cmd) {
+               case 2:
+                       readkeys(param);
+                       break;
+               case 3:
+                       sixdegrees(strtoul(param, NULL, 16));
+                       break;
+               case 4:
+                       //dofindpath(strtoul(param, NULL, 16),
+                       //      strtoul(strchr(param, ' ')+1, NULL, 16));
+                       break;
+               case 5:
+                       showkeysigs(strtoul(param, NULL, 16), true);
+                       break;
+               case 6:
+                       showkeysigs(strtoul(param, NULL, 16), false);
+                       break;
+               case 7:
+                       printf("%ld keys currently loaded, %ld self signed.\n",
+                                       hashelements(),
+                                       checkselfsig());
+                       break;
+               case 8:
+                       showhelp();
+                       break;
+               case 9:
+                       finished=trees=NULL;
+                       printf("Starting first DFS.\n");
+                       DFS();
+                       printf("Starting second DFS.\n");
+                       DFSsorted();
+                       printtrees(atoi(param));
+                       break;
+               case 10:
+                       showmostsigns(0);
+                       break;
+               case 11:
+                       showmostsigns(1);
+                       break;
+               case 12:
+                       memstats();
+                       break;
+               case 13:
+                       findmaxpath(atoi(param));
+                       break;
+               }
+       } while (cmd!=1);
+}
+
+
+int main(int argc, char *argv[])
+{
+       printf("gpgstats %s by Jonathan McDowell\n", VERSION);
+       puts("Copyright 2000 Project Purple. Released under the GPL.");
+       puts("A simple program to do stats on a GPG keyring.\n");
+
+       inithash();
+       readkeys("keyfile");
+       printf("%ld selfsigned.\n", checkselfsig());
+       printf("%ld distinct keys.\n", hashelements());
+
+       commandloop();
+       return 0;
+}
diff --git a/gpgstats-0.0.2/gpgstats.h b/gpgstats-0.0.2/gpgstats.h
new file mode 100644 (file)
index 0000000..7bd25d5
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+       gpgstats.h - Program to produce stats on a GPG keyring.
+       Written by Jonathan McDowell <noodles@earth.li>.
+
+       19/02/2000 - Started writing (sort of).
+*/
+
+#ifndef __GPGSTATS_H_
+#define __GPGSTATS_H_
+
+#define VERSION "0.0.2"
+
+#include "ll.h"
+
+/* Structure to hold a key's info */
+struct key {
+       unsigned long keyid;
+       char *name;
+       struct ll *sigs;
+       struct ll *signs;
+       struct ll *pi;
+       int colour;
+       int selfsigned;
+       int revoked;
+};
+
+void readkeys();
+long checkselfsig();
+int main(int argc, char *argv[]);
+
+#endif
diff --git a/gpgstats-0.0.2/graphstuff.c b/gpgstats-0.0.2/graphstuff.c
new file mode 100644 (file)
index 0000000..b74ee8b
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+       grahpstuff.c - Code to handle the various graph algorithms
+       Written by Jonathan McDowell <noodles@earth.li>.
+
+       19/02/2000 - Started writing (sort of).
+*/
+
+// #include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+//#include "gpgstats.h"
+#include "hash.h"
+/* #include "ll.h"
+#include "parse.h" */
+#include "graphstuff.h"
+
+struct keycount { unsigned long count; struct stats_key *k; };
+
+struct ll *finished=NULL;
+struct ll *trees=NULL;
+
+
+int keycmp(struct stats_key *key1, struct stats_key *key2)
+{
+       if (key1->keyid == key2->keyid) {
+               return 0;
+       }
+       return 1;
+}
+
+
+struct ll *addkey(struct ll *curkey, uint64_t keyid)
+{
+       return lladd(curkey, createandaddtohash(keyid));
+}
+
+void readkeys(const char *filename)
+{
+       char curline[1024];
+       unsigned long keys=0,sigs=0,pub=0, revoked=0;
+       uint64_t keyin = 0;
+       uint64_t cursig = 0;
+       struct stats_key *curkey=NULL, *cursign=NULL;
+       FILE *keyfile;
+       int (*p)();
+
+       p=keycmp;
+
+       printf("Reading key info from '%s'.\n", filename);
+       if ((keyfile=fopen(filename, "r"))==NULL) {
+               perror("readkeys()");
+               return;
+       }
+       /* read a line */
+       fgets(curline, 1023, keyfile);
+       while (!feof(keyfile)) {        
+               if (curline[0]=='P') {
+                       ++pub;
+                       ++keys;
+                       printf("\rRead %ld keys so far.", keys);
+                       keyin = strtoul(&curline[1], NULL, 16);
+                       curkey = createandaddtohash(keyin);
+               } else if (curline[0]=='S') {
+                       cursig = strtoul(&curline[1], NULL, 16);
+/*                     if (curkey->keyid==cursig) {
+                               curkey->selfsigned=1;
+                       } */
+
+                       if (!llfind(curkey->sigs, &cursig, p)) {
+                               curkey->sigs = addkey(curkey->sigs, cursig);
+                               ++sigs;
+                       }
+
+                       if ((cursign=findinhash(cursig))==NULL) {
+                               cursign = createandaddtohash(cursig);
+                       }
+
+//SIGNS                        if (!llfind(cursign->signs, curkey, p)) {
+//SIGNS                                cursign->signs = addkey(cursign->signs,
+//SIGNS                                                curkey->keyid);
+//SIGNS                        }
+               } else if (curline[0]=='N') {
+/*                     if (curkey->name==NULL) {
+                               curkey->name=strdup(&curline[1]);
+                               curkey->name[strlen(curkey->name)-1]=0;
+                               if (strcmp(curkey->name, "[revoked]")==0) {
+                                       curkey->revoked=1;
+                                       ++revoked;
+                               }
+                       } */
+               }
+               fgets(curline, 1023, keyfile);
+       }
+       fclose(keyfile);
+       printf("\rRead %ld keys, %ld pub, %ld sigs, %ld revoked.\n", keys, pub, sigs, revoked);
+       printf("\rRead %ld keys, %ld pub, %ld sigs, %ld revoked.\n", keys, pub, sigs, revoked);
+}
+
+
+void DFSVisit(int type, struct stats_key *key, unsigned long *time, unsigned long *depth)
+{
+       struct ll *curkey;
+       struct stats_key *v;
+
+       key->colour=1;
+//     key->d=(*time)++;
+
+       if (type == 0) {
+//SIGNS                curkey = key->signs;
+       } else {
+               curkey = key->sigs;
+       }
+       while (curkey != NULL) {
+               v = (struct stats_key *)findinhash(
+                               ((struct stats_key *) curkey->object)->keyid);
+               if (v == NULL) {
+                       printf("Couldn't find key in hash. Most odd.\n");
+               }
+               if (v != NULL && v->colour == 0) {
+                       if (type == 1 && key->parent == 0) {
+                               printf("key->parent is 0.\n");
+                       } else if (type == 1) {
+                               key->parent->object = lladd(key->parent->object,
+                                               v);
+                               v->parent = key->parent;
+                       }
+
+                       (*depth)++;
+                       DFSVisit(type, v, time, depth);
+               }
+               curkey=curkey->next;
+       }
+       key->colour = 2;
+//     key->f=(*time)++;
+       if (type == 0) {
+               finished=lladd(finished, key);
+       }
+}
+
+
+unsigned long DFS(void)
+{
+       unsigned long loop,time=0,depth,maxdepth=0;
+       struct ll *curkey;
+
+       initcolour(1);
+       for (loop=0; loop<HASHSIZE; loop++) {
+               curkey=gethashtableentry(loop);
+               while (curkey!=NULL) {
+                       if (((struct stats_key *)curkey->object)->colour==0) {
+                               depth=0;
+                               DFSVisit(0, ((struct stats_key *)curkey->object),
+                                       &time, &depth);
+                               if (depth>maxdepth) maxdepth=depth;
+                       }
+                       curkey=curkey->next;
+               }
+       }
+       return maxdepth;
+}
+
+
+unsigned long DFSsorted(void)
+{
+       unsigned long time=0,depth,maxdepth=0;
+       struct ll *curkey;
+
+       initcolour(1);
+       curkey=finished;
+       while (curkey != NULL) {
+               if (((struct stats_key *)curkey->object)->colour == 0) {
+                       trees = lladd(trees, curkey->object);
+                       ((struct stats_key *)curkey->object)->parent =
+                               trees;
+                       ((struct stats_key *)curkey->object)->parent->object =
+                               lladd(NULL, curkey->object);
+
+                       depth = 0;
+                       DFSVisit(1, ((struct stats_key *)curkey->object),
+                               &time, &depth);
+                       if (depth>maxdepth) {
+                               maxdepth = depth;
+                       }
+               }
+               curkey = curkey->next;
+       }
+       return maxdepth;
+}
+
+long checkselfsig()
+{
+       unsigned long loop;
+       struct ll *curkey;
+       unsigned long selfsig=0;
+
+       for (loop = 0; loop < HASHSIZE; loop++) {
+               curkey = gethashtableentry(loop);
+               while (curkey != NULL) {
+//SELFSIGNED           if (((struct stats_key *)curkey->object)->selfsigned) {
+//SELFSIGNED                   ++selfsig;
+//SELFSIGNED           }
+                       curkey = curkey->next;
+               }
+       }
+
+       return selfsig;
+}
+
+
+unsigned long countdegree(struct stats_key *have, int sigs, int maxdegree)
+{
+       unsigned long count = 0, curdegree = 0;
+       struct ll *curll, *nextll, *sigll, *tmp;
+
+       ++curdegree;
+
+       nextll = NULL;
+       curll = lladd(NULL, have);
+
+       while (curll != NULL && curdegree <= maxdegree) {
+               if (sigs) {
+                       sigll = ((struct stats_key *)curll->object)->sigs;
+               } else {
+//SIGNS                        sigll = ((struct stats_key *)curll->object)->signs;
+               }
+               while (sigll!=NULL) {
+                       if (((struct stats_key *) sigll->object)->colour==0) {
+                               /* We've never seen it. Count it, mark it and
+                                       explore its subtree */
+                               count++;
+                               ((struct stats_key *)sigll->object)->colour=curdegree;
+                               ((struct stats_key *)sigll->object)->parent = 
+                                       ((struct stats_key *)
+                                        curll->object)->keyid;
+                               nextll=lladd(nextll, sigll->object);
+                       }
+                       sigll = sigll->next;
+               }
+               tmp = curll->next;
+               free(curll);
+               curll = tmp;
+               if (curll == NULL) {
+                       curll = nextll;
+                       nextll = NULL;
+                       ++curdegree;
+               };
+       }
+
+       return count;
+}
+
diff --git a/gpgstats-0.0.2/graphstuff.h b/gpgstats-0.0.2/graphstuff.h
new file mode 100644 (file)
index 0000000..87463fd
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+       grahpstuff.h - Code to handle the various graph algorithms
+       Written by Jonathan McDowell <noodles@earth.li>.
+
+       19/02/2000 - Started writing (sort of).
+*/
+
+#ifndef __GRAPHSTUFF_H__
+#define __GRAPHSTUFF_H__
+
+#include <stdint.h>
+
+#include "stats.h"
+
+int keycmp(struct stats_key *key1, struct stats_key *key2);
+struct ll *addkey(struct ll *curkey, uint64_t keyid);
+void readkeys(const char *filename);
+void DFSVisit(int type, struct stats_key *key,
+               unsigned long *time, unsigned long *depth);
+unsigned long DFS(void);
+unsigned long DFSsorted(void);
+long checkselfsig();
+unsigned long countdegree(struct stats_key *have, int sigs, int maxdegree);
+
+#endif /*__GRAPHSTUFF_H__ */
diff --git a/gpgstats-0.0.2/parse.c b/gpgstats-0.0.2/parse.c
new file mode 100644 (file)
index 0000000..bad334a
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+       parse.c - General string parsing routines.
+       Copyright 1999 Jonathan McDowell for Project Purple
+
+       19/09/1999 - Started writing.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parse.h"
+
+struct strll *addtoend(struct strll *current, char *newstr)
+{
+       struct strll *new, *tmp;
+
+       if ((new=malloc(sizeof(struct strll)))==NULL) {
+               perror("addtoend()");
+               exit(1);
+       }
+
+       new->str=newstr;
+       new->next=NULL;
+
+       if (current==NULL) {
+               return new;
+       } else {
+               tmp=current;
+               while (tmp->next!=NULL) tmp=tmp->next;
+               tmp->next=new;
+       }
+
+       return current;
+}
+
+int parseline(struct cfginf commands[], const char *commandline)
+{
+       int loop=0;
+       char *params;
+       char command[CMDLEN], *pos=NULL;
+
+       params=NULL;
+       if (commands==NULL || commandline==NULL || strlen(commandline)==0) return 0;
+
+       if ((params=strdup(commandline))==NULL) {
+               return 0;
+       }
+
+       while (params[strlen(params)-1]<' ') params[strlen(params)-1]=0;
+
+       if ((pos=strchr(params, ' '))!=NULL) {
+               *pos=0;
+               if (strlen(params)>=CMDLEN) {
+                       /* Hah. No buffer overflow here. (Egg on face approaching....) */
+                       free(params);
+                       return 0;
+               }
+               strncpy(command, params, CMDLEN);
+               command[CMDLEN-1]=0;
+               memmove(params, pos+1, strlen(commandline)-strlen(params));
+       } else {
+               if (strlen(params)>=CMDLEN) {
+                       /* Hah. No buffer overflow here. (Egg on face approaching....) */
+                       free(params);
+                       return 0;
+               }
+               strncpy(command, params, CMDLEN);
+               command[CMDLEN-1]=0;
+       }
+
+       while (strlen(commands[loop].command)>0 && strcasecmp(command, commands[loop].command)!=0) {
+               ++loop;
+       }
+
+       if (strlen(commands[loop].command)==0) {
+               return -1;
+       } else {
+               if (commands[loop].type==0 && params==NULL) {
+                       return loop+1;
+               } else {
+                       switch (commands[loop].type) {
+                       case 1: *((char **) commands[loop].var) = params;
+                               break;
+                       case 2: *((int *) commands[loop].var) = str2bool(params);
+                               free(params);
+                               break;
+                       case 3: *((int *) commands[loop].var) = atoi(params);
+                               free(params);
+                               break;
+                       case 4: *((struct strll **) commands[loop].var) = addtoend(*((struct strll **) commands[loop].var), params);
+                               break;
+                       default:
+                               break;
+                       }
+                       return loop+1;
+               }
+       }
+}
+
+int str2bool(const char *buf)
+{
+       if (strcasecmp("TRUE", buf) == 0 || strcmp("1", buf) == 0 ||
+               strcasecmp("Y", buf) == 0 || strcasecmp("YES", buf) == 0 ||
+               strcasecmp("T", buf) == 0) return 1;
+
+       if (strcasecmp("FALSE", buf) == 0 || strcmp("0", buf) == 0 ||
+               strcasecmp("N", buf) == 0 || strcasecmp("NO", buf) == 0 ||
+               strcasecmp("F", buf) == 0) return 0;
+
+       return -1;
+}
diff --git a/gpgstats-0.0.2/parse.h b/gpgstats-0.0.2/parse.h
new file mode 100644 (file)
index 0000000..8c6efd3
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+       parse.h - General string parsing routines.
+       Copyright 1999 Jonathan McDowell for Project Purple
+
+       19/09/1999 - Started writing.
+*/
+
+#ifndef __PARSE_H_
+#define __PARSE_H_
+
+#define CMDLEN 16
+
+struct cfginf {
+       char command[CMDLEN];
+       int type;       /* 0 : No option.
+                          1 : String.
+                          2 : Bool (in an int).
+                          3 : Int.
+                          4 : strll (see below) */
+       void *var;      /* Variable to store option in */
+};
+
+/* Linked list class for strings to allow returning a set of strings */
+struct strll {
+       char *str;
+       struct strll *next;
+};
+
+int parseline(struct cfginf commands[], const char *commandline);
+int str2bool(const char *buf);
+
+#endif
diff --git a/gpgstats-0.0.2/sig2dot.pl b/gpgstats-0.0.2/sig2dot.pl
new file mode 100755 (executable)
index 0000000..7b97646
--- /dev/null
@@ -0,0 +1,92 @@
+#!/usr/bin/perl
+
+# sig2dot v0.8 (c) Darxus@ChaosReigns.com, released under the GPL
+# Download from: http://www.chaosreigns.com/debian-keyring
+#
+# Parses the (gpg) debian-keyring 
+# (http://www.debian.org/Packages/unstable/misc/debian-keyring.html) to a format
+# suitable for use by dot or neato (package name graphviz,
+# http://www.research.att.com/sw/tools/graphviz/) like so:
+#
+# gpg --list-sigs --keyring /usr/share/keyrings/debian-keyring.gpg | ./sig2dot.pl > debian-keyring.dot
+# neato -Tps debian-keyring.dot > debian-keyring.neato.dot.ps
+# dot -Tps debian-keyring.dot > debian-keyring.dot.dot.ps
+
+while ($line = <STDIN>)
+{
+  chomp $line;
+  if ($line =~ m#([^ ]+) +[^ ]+ +[^ ]+ +([^<]+)#)
+  {
+    $type = $1;
+    $name = $2;
+    chop $name;
+    #print "type:$type:name:$name:\n";
+
+    if ($type eq "pub")
+    {
+      $owner = $name; 
+    }
+
+    if ($type eq "sig" and $name ne $owner and $name ne '[User id not found')
+    {
+      push (@{$sigs{$owner}},$name);
+      push (@names,$name,$owner);
+    }
+  } else {
+    print STDERR "Couldn't parse: $line\n";
+  }
+}
+
+print "digraph \"debian-keyring\" {\n";
+
+undef %saw;
+@saw{@names} = ();
+@names = keys %saw;
+undef %saw;
+
+for $owner (sort {$sigs{$a} <=> $sigs{$b}} keys %sigs)
+{
+  undef %saw;
+  @saw{@{$sigs{$owner}}} = ();
+  @{$sigs{$owner}} = keys %saw;
+  undef %saw;
+
+  #print STDERR scalar(@{$sigs{$owner}})," $owner\n";
+  $count{$owner} = scalar(@{$sigs{$owner}});
+}
+
+open (STATS,">stats.html");
+print STATS "<html><body><table border=1>\n";
+
+for $owner (sort {$count{$b} <=> $count{$a}} keys %sigs)
+{
+  print STATS "<tr><td>$owner<td>$count{$owner}<td><img src=\"/images/pipe0.jpg\" height=15 width=",$count{$owner} * 20,">\n";
+}
+
+print STATS "</table></body></html>\n";
+close STATS;
+
+print "node [style=filled]\n";
+for $name (@names)
+{
+  if ($count{$name} > 20)
+  {
+    print "\"$name\" [color=red]\n";
+  } elsif ($count{$name} > 8)
+  {
+    print "\"$name\" [color=blue]\n";
+  }
+}
+print "node [style=solid]\n";
+
+for $owner (sort keys %sigs)
+{
+  for $name (@{$sigs{$owner}})
+  {
+    print "\"$name\" -> \"$owner\" [len=5]\n";
+  }
+}
+
+print "}\n";
+
+
diff --git a/gpgwww.c b/gpgwww.c
new file mode 100644 (file)
index 0000000..265422f
--- /dev/null
+++ b/gpgwww.c
@@ -0,0 +1,144 @@
+/*
+ * gpgwww.c - www interface to path finder.
+ * 
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2001-2002 Project Purple.
+ */
+
+// #include <stdint.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "getcgi.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;
+       char *uid;
+
+       /*
+        * Make sure the keys we have and want are in the cache.
+        */
+       hash_getkeysigs(have);
+       hash_getkeysigs(want);
+
+       if ((keyinfoa = findinhash(have)) == NULL) {
+               printf("Couldn't find key 0x%llX.\n", have);
+               return;
+       }
+       if ((keyinfob = findinhash(want)) == NULL) {
+               printf("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("Can't find a link from 0x%llX to 0x%llX\n",
+                               have,
+                               want);
+       } else {
+               printf("%d steps from 0x%llX to 0x%llX\n",
+                               keyinfoa->colour, have, want);
+               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 == 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,
+                                       txt2html(keyid2uid(curkey->keyid)),
+                                       (curkey->keyid == want) ? "" :
+                                        " signs");
+                       } else {
+                               printf("0x%llX (%s)%s\n",
+                                       curkey->keyid,
+                                       (uid == NULL) ? "[User id not found]" :
+                                               uid,
+                                       (curkey->keyid == want) ? "" :
+                                        " signs");
+                       }
+                       curkey = findinhash(curkey->parent);
+               }
+       }
+}
+
+void parsecgistuff(char **cgiparams, uint64_t *from, uint64_t *to)
+{
+       int i = 0;
+
+       if (cgiparams != NULL) {
+               i = 0;
+               while (cgiparams[i] != NULL) {
+                       if (!strcmp(cgiparams[i], "to")) {
+                               *to = strtoul(cgiparams[i+1], NULL, 16);
+                       } else if (!strcmp(cgiparams[i], "from")) {
+                               *from = strtoul(cgiparams[i+1], NULL, 16);
+                       }
+                       i += 2;
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[])
+{
+       char **cgiparams = NULL;        /* Our CGI parameter block */
+       uint64_t from = 0, to = 0;
+
+       cgiparams = getcgivars(argc, argv);
+
+       puts("Content-Type: text/html\n");
+       puts("<HTML>");
+       puts("<HEAD>");
+       puts("<TITLE>Experimental PGP key path finder results</TITLE>");
+       puts("</HEAD>");
+       puts("<BODY>");
+       puts("</BODY>");
+
+       parsecgistuff(cgiparams, &from, &to);
+
+       if (from == 0 || to == 0) {
+               printf("Must pass from & to\n");
+               puts("</HTML>");
+               exit(1);
+       }
+
+       printf("<P>Looking for path from 0x%llX to 0x%llX</P>\n", from, to);
+       puts("<PRE>");
+       initdb();
+       inithash();
+       dofindpath(from, to, true);
+       cleanupdb();
+       puts("</PRE>");
+
+       puts("<HR>");
+       puts("Produced by gpgwww 0.0.1, part of onak. <A HREF=\"mailto:noodles-onak@earth.li\">Jonathan McDowell</A>");
+       puts("</HTML>");
+
+       return EXIT_SUCCESS;
+}
diff --git a/hash.c b/hash.c
new file mode 100644 (file)
index 0000000..82dfcc7
--- /dev/null
+++ b/hash.c
@@ -0,0 +1,132 @@
+/*
+ * hash.c - hashing routines mainly used for caching key details.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2000-2002 Project Purple
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "hash.h"
+#include "keydb.h"
+#include "ll.h"
+#include "stats.h"
+
+/**
+ *     hashtable - the hash table array.
+ */
+static struct ll *hashtable[HASHSIZE];
+
+/**
+ *     elements - the number of elements in the hash table.
+ */
+static unsigned long elements;
+
+/**
+ *     inithash - Initialize the hash ready for use.
+ */
+void inithash(void)
+{
+       unsigned int i;
+
+       for (i = 0; i < HASHSIZE; i++) {
+               hashtable[i] = NULL;
+       }
+       elements = 0;
+}
+
+void addtohash(struct stats_key *key)
+{
+       ++elements;
+       hashtable[key->keyid & HASHMASK]=
+               lladd(hashtable[key->keyid & HASHMASK], key);
+}
+
+/**
+ *     createandaddtohash - Creates a key and adds it to the hash.
+ *     @keyid: The key to create and add.
+ *
+ *     Takes a key, checks if it exists in the hash and if not creates it
+ *     and adds it to the hash. Returns the key from the hash whether it
+ *     already existed or we just created it.
+ */
+struct stats_key *createandaddtohash(uint64_t keyid)
+{
+       struct stats_key *tmpkey;
+
+       /*
+        * Check if the key already exists and if not create and add it.
+        */
+       tmpkey = findinhash(keyid);
+       if (tmpkey == NULL) {
+               tmpkey = malloc(sizeof(*tmpkey));
+               memset(tmpkey, 0, sizeof(*tmpkey));
+               tmpkey->keyid = keyid;
+               addtohash(tmpkey);
+       }
+       return tmpkey;
+}
+
+int stats_key_cmp(struct stats_key *key, uint64_t *keyid)
+{
+       return !(key != NULL && key->keyid == *keyid);
+}
+
+struct stats_key *findinhash(uint64_t keyid)
+{
+       int (*p)();
+       struct ll *found;
+
+       p = stats_key_cmp;
+       if ((found = llfind(hashtable[keyid & HASHMASK], &keyid, p))==NULL) {
+               return NULL;
+       }
+       return found->object;
+}
+
+unsigned long hashelements(void)
+{
+       return elements;
+}
+
+struct ll *gethashtableentry(int entry)
+{
+       return hashtable[entry];
+}
+
+/**
+ *     hash_getkeysigs - Gets the signatures on a key.
+ *     @keyid: The key we want the signatures for.
+ *     
+ *     This function gets the signatures on a key. It's the same as the
+ *     getkeysigs function from the keydb module except we also cache the data
+ *     so that if we need it again we already have it available.
+ */
+struct ll *hash_getkeysigs(uint64_t keyid)
+{
+       struct stats_key *key = NULL;
+
+       key = findinhash(keyid);
+       if (key == NULL) {
+               key = malloc(sizeof(*key));
+               if (key != NULL) {
+                       key->keyid = keyid;
+                       key->colour = 0;
+                       key->parent = 0;
+                       key->sigs = NULL;
+                       key->gotsigs = false;
+                       addtohash(key);
+               } else {
+                       perror("hash_getkeysigs()");
+                       return NULL;
+               }
+       }
+       if (key->gotsigs == false) {
+               key->sigs = getkeysigs(key->keyid);
+               key->gotsigs = true;
+       }
+
+       return key->sigs;
+}
diff --git a/hash.h b/hash.h
new file mode 100644 (file)
index 0000000..b7caa1a
--- /dev/null
+++ b/hash.h
@@ -0,0 +1,78 @@
+/*
+ * hash.h - hashing routines mainly used for caching key details.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2000-2002 Project Purple
+ */
+
+#ifndef __HASH_H__
+#define __HASH_H__
+
+#include "ll.h"
+#include "stats.h"
+
+#define HASHSIZE 1024
+#define HASHMASK 0x3FF
+
+/**
+ *     inithash - Initialize the hash ready for use.
+ *
+ *     This function prepares the hash ready for use. It should be called
+ *     before any of the functions below are used.
+ */
+void inithash(void);
+
+/**
+ *     addtohash - Adds a key to the hash.
+ *     @key: The key to add.
+ *
+ *     Takes a key and stores it in the hash.
+ */
+void addtohash(struct stats_key *key);
+
+/**
+ *     createandaddtohash - Creates a key and adds it to the hash.
+ *     @keyid: The key to create and add.
+ *
+ *     Takes a key, checks if it exists in the hash and if not creates it
+ *     and adds it to the hash. Returns the key from the hash whether it
+ *     already existed or we just created it.
+ */
+struct stats_key *createandaddtohash(uint64_t keyid);
+
+/**
+ *     findinhash - Finds a key in the hash.
+ *     @keyid: The key we want.
+ *
+ *     Finds a key in the hash and returns it.
+ */
+struct stats_key *findinhash(uint64_t keyid);
+
+/**
+ *     hashelements - Returns the size of the hash
+ *
+ *     Returns the number of elements that have been loaded into the hash.
+ */
+unsigned long hashelements(void);
+
+/**
+ *     gethashtableentry - Returns an entry from the hash table.
+ *     @entry: The entry to return. 0 <= entry < HASHSIZE must hold.
+ *
+ *     Gets a particular entry from the hash. Useful for doing something over
+ *     all entries in the hash.
+ */
+struct ll *gethashtableentry(int entry);
+
+/**
+ *     hash_getkeysigs - Gets the signatures on a key.
+ *     @keyid: The key we want the signatures for.
+ *     
+ *     This function gets the signatures on a key. It's the same as the
+ *     getkeysigs function from the keydb module except we also cache the data
+ *     so that if we need it again we already have it available.
+ */
+struct ll *hash_getkeysigs(uint64_t keyid);
+
+#endif /* __HASH_H__ */
diff --git a/index.html b/index.html
new file mode 100644 (file)
index 0000000..3da573e
--- /dev/null
@@ -0,0 +1,10 @@
+<html>
+<head><title>onak - Oh No, Another Keyserver</title></head>
+<body>
+<p>This is onak - Oh No, Another Keyserver. It is written by Jonathan McDowell
+<a href="mailto:noodles-onak@earth.li">&lt;noodles-onak@earth.li&gt;</a> and is
+currently far from finished.</p>
+<a href="pks/lookup?search=0x5B430367&op=vindex">My DSA key</a>
+<a href="pks/lookup?search=0x4DC4E7FD&op=vindex">My RSA key</a>
+</body>
+</html>
diff --git a/keydb.c b/keydb.c
new file mode 100644 (file)
index 0000000..2547b6b
--- /dev/null
+++ b/keydb.c
@@ -0,0 +1,80 @@
+/*
+ * keydb.c - Routines for DB access that just use store/fetch.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+/**
+ *     The routines in this file are meant to be used as an initial step when
+ *     adding a new db access module. They provide various functions required
+ *     of the db access module using only the store and fetch functions. As
+ *     they need to parse the actual OpenPGP data to work they are a lot
+ *     slower than custom functions however.
+ */
+
+#include <stdio.h>
+
+#include "keydb.h"
+#include "keyid.h"
+#include "keyindex.h"
+#include "keystructs.h"
+#include "mem.h"
+#include "parsekey.h"
+
+/**
+ *     keyid2uid - Takes a keyid and returns the primary UID for it.
+ *     @keyid: The keyid to lookup.
+ */
+char *keyid2uid(uint64_t keyid)
+{
+       struct openpgp_publickey *publickey = NULL;
+       struct openpgp_signedpacket_list *curuid = NULL;
+       static char buf[1024];
+
+       buf[0]=0;
+       if (fetch_key(keyid, &publickey) && publickey != NULL) {
+               curuid = publickey->uids;
+               while (curuid != NULL && buf[0] == 0) {
+                       if (curuid->packet->tag == 13) {
+                               snprintf(buf, 1023, "%.*s",
+                                               (int) curuid->packet->length,
+                                               curuid->packet->data);
+                       }
+                       curuid = curuid -> next;
+               }
+               free_publickey(publickey);
+       }
+
+       if (buf[0] == 0) {
+               return NULL;
+       } else {
+               return buf;
+       }
+}
+
+/**
+ *     getkeysigs - Gets a linked list of the signatures on a key.
+ *     @keyid: The keyid to get the sigs for.
+ *
+ *     This function gets the list of signatures on a key. Used for key 
+ *     indexing and doing stats bits.
+ */
+struct ll *getkeysigs(uint64_t keyid)
+{
+       struct ll *sigs = NULL;
+       struct openpgp_signedpacket_list *uids = NULL;
+       struct openpgp_publickey *publickey = NULL;
+
+       fetch_key(keyid, &publickey);
+       
+       if (publickey != NULL) {
+               for (uids = publickey->uids; uids != NULL; uids = uids->next) {
+                       sigs = keysigs(sigs, uids->sigs);
+               }
+               free_publickey(publickey);
+       }
+       
+       return sigs;
+}
diff --git a/keydb.h b/keydb.h
new file mode 100644 (file)
index 0000000..ca8fdf7
--- /dev/null
+++ b/keydb.h
@@ -0,0 +1,86 @@
+/*
+ * keydb.h - Routines to store and fetch keys.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#ifndef __KEYDB_H__
+#define __KEYDB_H__
+
+// #include <stdint.h>
+#include <inttypes.h>
+
+#include "keystructs.h"
+#include "ll.h"
+
+/**
+ *     initdb - Initialize the key database.
+ *
+ *     This function should be called before any of the other functions in
+ *     this file are called in order to allow the DB to be initialized ready
+ *     for access.
+ */
+void initdb(void);
+
+/**
+ *     cleanupdb - De-initialize the key database.
+ *
+ *     This function should be called upon program exit to allow the DB to
+ *     cleanup after itself.
+ */
+void cleanupdb(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.
+ *
+ *     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);
+
+/**
+ *     store_key - Takes a key and stores it.
+ *     @publickey: A pointer to the public key to store.
+ *
+ *     This function stores a public key in whatever storage mechanism we are
+ *     using.
+ *
+ *     TODO: Do we store multiple keys of the same id? Or only one and replace
+ *     it?
+ */
+int store_key(struct openpgp_publickey *publickey);
+
+/**
+ *     delete_key - Given a keyid delete the key from storage.
+ *     @keyid: The keyid to delete.
+ *
+ *     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);
+
+/**
+ *     keyid2uid - Takes a keyid and returns the primary UID for it.
+ *     @keyid: The keyid to lookup.
+ *
+ *     This function returns a UID for the given key. Returns NULL if the key
+ *     isn't found.
+ */
+char *keyid2uid(uint64_t keyid);
+
+/**
+ *     getkeysigs - Gets a linked list of the signatures on a key.
+ *     @keyid: The keyid to get the sigs for.
+ *
+ *     This function gets the list of signatures on a key. Used for key 
+ *     indexing and doing stats bits.
+ */
+struct ll *getkeysigs(uint64_t keyid);
+
+#endif /* __KEYDB_H__ */
diff --git a/keydb_db2.c b/keydb_db2.c
new file mode 100644 (file)
index 0000000..6aa47c0
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * keydb_db2.c - Routines to store and fetch keys in a DB2 file (a la pksd)
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <db2/db.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "keydb.h"
+#include "keyid.h"
+#include "keyindex.h"
+#include "keystructs.h"
+#include "mem.h"
+#include "parsekey.h"
+
+#define DBDIR "/community/pgp-keyserver/db-copy"
+#define KEYDB_KEYID_BYTES 4
+
+/**
+ *     db2_numdb - The number of database files we have.
+ */
+static int db2_numdb = 16;
+
+/**
+ *     db2_keydbfiles - An array of DB structs for our key database files.
+ */
+static DB **db2_keydbfiles = NULL;
+
+/**
+ *     db2_env - Database environment variable.
+ */
+static DB_ENV db2_env;
+
+/*
+ * Shared with CGI buffer stuff...
+ */
+struct db2_get_ctx {
+       char *buffer;
+       int offset;
+       int size;
+};
+
+/**
+ *     keydb_fetchchar - Fetches a char from a buffer.
+ */
+int keydb_fetchchar(void *ctx, int count, unsigned char *c)
+{
+       struct db2_get_ctx *buf = NULL;
+       int i;
+       
+       buf = (struct db2_get_ctx *) ctx;
+       for (i = 0; i < count; i++) {
+               c[i] = buf->buffer[buf->offset++];
+       }
+
+       return (((buf->offset) == (buf->size)) ? 1 : 0);
+}
+
+/**
+ *     keydb_putchar - Puts a char to a file.
+ */
+static int keydb_putchar(void *fd, unsigned char c)
+{
+//     return !(lo_write(dbconn, *(int *) fd, &c, sizeof(c)));
+       return 1;
+}
+
+DB *keydb(DBT *key)
+{
+       /*
+        * keyid's are 8 bytes, msb first.  so start from the end.  use 16
+        * bits, since that's enough to divide by any small number of db files.
+        */
+       unsigned char *keydata = (unsigned char *) key->data;
+       unsigned long keyidnum;
+
+       keyidnum = (keydata[KEYDB_KEYID_BYTES-2]<<8)|keydata[KEYDB_KEYID_BYTES-1];
+       return(db2_keydbfiles[keyidnum % db2_numdb]);
+}
+
+/**
+ *     initdb - Initialize the key database.
+ *
+ *     This function should be called before any of the other functions in
+ *     this file are called in order to allow the DB to be initialized ready
+ *     for access.
+ */
+void initdb(void)
+{
+       DB_INFO keydbinfo;
+       int i;
+       int ret;
+       char keydbname[20];
+
+       memset(&db2_env, 0, sizeof(db2_env));
+
+       /*
+        * Tunable param. Just using what pksd does for the moment. Bigger uses
+        * more memory but improves performance. Bigger than physical memory
+        * makes no sense.
+        */
+       db2_env.mp_size = 20 * 1024 * 1024;
+
+       ret = db_appinit(DBDIR, NULL, &db2_env, DB_INIT_MPOOL|DB_INIT_LOCK);
+       if (!ret) {
+               db2_keydbfiles = (DB **) malloc(sizeof (DB *) * db2_numdb);
+               memset(&keydbinfo, 0, sizeof(keydbinfo));
+               keydbinfo.db_pagesize = 8192;
+               for (i = 0; i < db2_numdb; i++) {
+                       db2_keydbfiles[i] = NULL;
+                       snprintf(keydbname, 19, "keydb%03d", i);
+                       ret = db_open(keydbname, DB_HASH, DB_RDONLY, 0644,
+                                       &db2_env, &keydbinfo,
+                                       &db2_keydbfiles[i]);
+                       if (ret) {
+                               fprintf(stderr, "Error opening db file %d (errno %d)\n",
+                                       i, ret);
+                               exit(1);
+                       }
+               }
+       } else {
+               fprintf(stderr, "Error initializing db (%d).\n", ret);
+               exit(1);
+       }
+}
+
+/**
+ *     cleanupdb - De-initialize the key database.
+ *
+ *     This function should be called upon program exit to allow the DB to
+ *     cleanup after itself.
+ */
+void cleanupdb(void)
+{
+       int i;
+
+       for (i = 0; i < db2_numdb; i++) {
+               if (db2_keydbfiles[i] != NULL) {
+                       (*(db2_keydbfiles[i]->close))(db2_keydbfiles[i], 0);
+                       db2_keydbfiles[i] = NULL;
+               }
+       }
+
+       db_appexit(&db2_env);
+}
+
+/**
+ *     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.
+ *
+ *     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
+ *     packets, so we can just use read_openpgp_stream() to read the packets
+ *     in and then parse_keys() to parse the packets into a publickey
+ *     structure.
+ */
+int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey)
+{
+       struct openpgp_packet_list *packets = NULL;
+       int ret;
+       DBT key, data;
+       char id[KEYDB_KEYID_BYTES];
+       struct db2_get_ctx fetchbuf;
+
+       memset(&key, 0, sizeof(key));
+       memset(&data, 0, sizeof(data));
+
+       id[0] = (keyid >> 24) & 0xFF;
+       id[1] = (keyid >> 16) & 0xFF;
+       id[2] = (keyid >> 8) & 0xFF;
+       id[3] = keyid & 0xFF;
+
+       key.data = id;
+       key.size = KEYDB_KEYID_BYTES;
+
+       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;
+               read_openpgp_stream(keydb_fetchchar, &fetchbuf, &packets);
+               parse_keys(packets, publickey);
+       }
+
+       return (!ret);
+}
+
+/**
+ *     store_key - Takes a key and stores it.
+ *     @publickey: A pointer to the public key to store.
+ *
+ *     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)
+{
+       return 0;
+}
+
+/**
+ *     delete_key - Given a keyid delete the key from storage.
+ *     @keyid: The keyid to delete.
+ *
+ *     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)
+{
+       return (1);
+}
+
+/*
+ * Include the basic keydb routines.
+ */
+#include "keydb.c"
diff --git a/keydb_file.c b/keydb_file.c
new file mode 100644 (file)
index 0000000..e5cf087
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * keydb.c - Routines to store and fetch keys.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "keydb.h"
+#include "keyid.h"
+#include "keystructs.h"
+#include "ll.h"
+#include "mem.h"
+#include "parsekey.h"
+
+#define DBDIR "/home/noodles/onak-0.0.1/db"
+
+/**
+ *     keydb_fetchchar - Fetches a char from a file.
+ */
+static int keydb_fetchchar(void *fd, size_t count, unsigned char *c)
+{
+       return !(read( *(int *) fd, c, count));
+}
+
+/**
+ *     keydb_putchar - Puts a char to a file.
+ */
+static int keydb_putchar(void *fd, unsigned char c)
+{
+       return !(write( *(int *) fd, &c, sizeof(c)));
+}
+
+/**
+ *     initdb - Initialize the key database.
+ *
+ *     This is just a no-op for flat file access.
+ */
+void initdb(void)
+{
+}
+
+/**
+ *     cleanupdb - De-initialize the key database.
+ *
+ *     This is just a no-op for flat file access.
+ */
+void cleanupdb(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.
+ *
+ *     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
+ *     packets, so we can just use read_openpgp_stream() to read the packets
+ *     in and then parse_keys() to parse the packets into a publickey
+ *     structure.
+ */
+int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey)
+{
+       struct openpgp_packet_list *packets = NULL;
+       char keyfile[1024];
+       int fd = -1;
+
+       snprintf(keyfile, 1023, "%s/0x%llX", DBDIR, keyid & 0xFFFFFFFF);
+       fd = open(keyfile, O_RDONLY); // | O_SHLOCK);
+
+       if (fd > -1) {
+               read_openpgp_stream(keydb_fetchchar, &fd, &packets);
+               parse_keys(packets, publickey);
+               close(fd);
+       }
+
+       return (fd > -1);
+}
+
+/**
+ *     store_key - Takes a key and stores it.
+ *     @publickey: A pointer to the public key to store.
+ *
+ *     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)
+{
+       struct openpgp_packet_list *packets = NULL;
+       struct openpgp_packet_list *list_end = NULL;
+       struct openpgp_publickey *next = NULL;
+       char keyfile[1024];
+       int fd = -1;
+
+       snprintf(keyfile, 1023, "%s/0x%llX", DBDIR,
+                       get_keyid(publickey) & 0xFFFFFFFF);
+       fd = open(keyfile, O_WRONLY | O_CREAT, 0664); // | O_EXLOCK);
+
+       if (fd > -1) {
+               next = publickey -> next;
+               publickey -> next = NULL;
+               flatten_publickey(publickey, &packets, &list_end);
+               publickey -> next = next;
+               
+               write_openpgp_stream(keydb_putchar, &fd, packets);
+               close(fd);
+       }
+
+       return (fd > -1);
+}
+
+/**
+ *     delete_key - Given a keyid delete the key from storage.
+ *     @keyid: The keyid to delete.
+ *
+ *     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)
+{
+       char keyfile[1024];
+
+       snprintf(keyfile, 1023, "%s/0x%llX", DBDIR,
+                       keyid & 0xFFFFFFFF);
+
+       return unlink(keyfile);
+}
+
+/*
+ * Include the basic keydb routines.
+ */
+#include "keydb.c"
diff --git a/keydb_pg.c b/keydb_pg.c
new file mode 100644 (file)
index 0000000..845ec84
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * keydb_pg.c - Routines to store and fetch keys in a PostGres database.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#include <postgresql/libpq-fe.h>
+#include <postgresql/libpq/libpq-fs.h>
+
+//#include <libpq-fe.h>
+//#include <libpq/libpq-fs.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "keydb.h"
+#include "keyid.h"
+#include "keyindex.h"
+#include "keystructs.h"
+#include "mem.h"
+#include "parsekey.h"
+
+/**
+ *     dbconn - our connection to the database.
+ */
+static PGconn *dbconn = NULL;
+
+/**
+ *     keydb_fetchchar - Fetches a char from a file.
+ */
+static int keydb_fetchchar(void *fd, size_t count, unsigned char *c)
+{
+       return (!lo_read(dbconn, *(int *) fd, c, count));
+}
+
+/**
+ *     keydb_putchar - Puts a char to a file.
+ */
+static int keydb_putchar(void *fd, unsigned char c)
+{
+       return !(lo_write(dbconn, *(int *) fd, &c, sizeof(c)));
+}
+
+/**
+ *     initdb - Initialize the key database.
+ *
+ *     This function should be called before any of the other functions in
+ *     this file are called in order to allow the DB to be initialized ready
+ *     for access.
+ */
+void initdb(void)
+{
+       dbconn = PQsetdbLogin(NULL, // host
+                       NULL, // port
+                       NULL, // options
+                       NULL, // tty
+                       "noodles", // database
+                       NULL,  //login
+                       NULL); // password
+
+       if (PQstatus(dbconn) == CONNECTION_BAD) {
+               fprintf(stderr, "Connection to database failed.\n");
+               fprintf(stderr, "%s\n", PQerrorMessage(dbconn));
+               PQfinish(dbconn);
+               dbconn = NULL;
+               exit(1);
+       }
+}
+
+/**
+ *     cleanupdb - De-initialize the key database.
+ *
+ *     This function should be called upon program exit to allow the DB to
+ *     cleanup after itself.
+ */
+void cleanupdb(void)
+{
+       PQfinish(dbconn);
+       dbconn = NULL;
+}
+
+/**
+ *     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.
+ *
+ *     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
+ *     packets, so we can just use read_openpgp_stream() to read the packets
+ *     in and then parse_keys() to parse the packets into a publickey
+ *     structure.
+ */
+int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey)
+{
+       struct openpgp_packet_list *packets = NULL;
+       PGresult *result = NULL;
+       char *oids = NULL;
+       char statement[1024];
+       int fd = -1;
+       Oid key_oid;
+
+       result = PQexec(dbconn, "BEGIN");
+       PQclear(result);
+       
+       snprintf(statement, 1023,
+                       "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
+                       keyid & 0xFFFFFFFF);
+       result = PQexec(dbconn, statement);
+
+       if (PQresultStatus(result) == PGRES_TUPLES_OK &&
+                       PQntuples(result) == 1) {
+               oids = PQgetvalue(result, 0, 0);
+               key_oid = (Oid) atoi(oids);
+
+               fd = lo_open(dbconn, key_oid, INV_READ);
+               if (fd < 0) {
+                       fprintf(stderr, "Can't open large object.\n");
+               } else {
+                       read_openpgp_stream(keydb_fetchchar, &fd, &packets);
+                       parse_keys(packets, publickey);
+                       lo_close(dbconn, fd);
+               }
+       } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+               fprintf(stderr, "Problem retrieving key (%llX) from DB.\n",
+                               keyid);
+       }
+
+       PQclear(result);
+
+       result = PQexec(dbconn, "COMMIT");
+       PQclear(result);
+       return (fd > -1);
+}
+
+/**
+ *     store_key - Takes a key and stores it.
+ *     @publickey: A pointer to the public key to store.
+ *
+ *     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)
+{
+       struct openpgp_packet_list *packets = NULL;
+       struct openpgp_packet_list *list_end = NULL;
+       struct openpgp_publickey *next = NULL;
+       PGresult *result = NULL;
+       char statement[1024];
+       Oid key_oid;
+       int fd;
+
+
+       /*
+        * Delete the key if we already have it.
+        *
+        * TODO: Can we optimize this perhaps? Possibly when other data is
+        * involved as well? I suspect this is easiest and doesn't make a lot
+        * 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);
+
+       next = publickey->next;
+       publickey->next = NULL;
+       flatten_publickey(publickey, &packets, &list_end);
+       publickey->next = next;
+               
+       key_oid = lo_creat(dbconn, INV_READ | INV_WRITE);
+       if (key_oid == 0) {
+               fprintf(stderr, "Can't create key OID\n");
+       } else {
+               fd = lo_open(dbconn, key_oid, INV_WRITE);
+               write_openpgp_stream(keydb_putchar, &fd, packets);
+               lo_close(dbconn, fd);
+       }
+
+       snprintf(statement, 1023, 
+                       "INSERT INTO onak_keys (keyid, keydata) VALUES "
+                       "('%llX', '%d')", 
+                       get_keyid(publickey) & 0xFFFFFFFF,
+                       key_oid);
+       result = PQexec(dbconn, statement);
+
+       if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+               fprintf(stderr, "Problem storing key in DB.\n");
+               fprintf(stderr, "%s\n", PQresultErrorMessage(result));
+       }
+       PQclear(result);
+
+       result = PQexec(dbconn, "COMMIT");
+       PQclear(result);
+       
+       return 0;
+}
+
+/**
+ *     delete_key - Given a keyid delete the key from storage.
+ *     @keyid: The keyid to delete.
+ *
+ *     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)
+{
+       PGresult *result = NULL;
+       char *oids = NULL;
+       char statement[1024];
+       int found = 1;
+       Oid key_oid;
+
+       result = PQexec(dbconn, "BEGIN");
+       PQclear(result);
+       
+       snprintf(statement, 1023,
+                       "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
+                       keyid & 0xFFFFFFFF);
+       result = PQexec(dbconn, statement);
+
+       if (PQresultStatus(result) == PGRES_TUPLES_OK &&
+                       PQntuples(result) == 1) {
+               found = 0;
+               oids = PQgetvalue(result, 0, 0);
+               key_oid = (Oid) atoi(oids);
+               lo_unlink(dbconn, key_oid);
+               PQclear(result);
+               snprintf(statement, 1023,
+                       "DELETE * FROM onak_keys WHERE keyid = '%llX'",
+                       keyid & 0xFFFFFFFF);
+               result = PQexec(dbconn, statement);
+       } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+               fprintf(stderr, "Problem retrieving key (%llX) from DB.\n",
+                               keyid);
+       }
+
+       PQclear(result);
+
+       result = PQexec(dbconn, "COMMIT");
+       PQclear(result);
+       return (found);
+}
+
+/*
+ * Include the basic keydb routines.
+ */
+#include "keydb.c"
diff --git a/keyid.c b/keyid.c
new file mode 100644 (file)
index 0000000..5252e81
--- /dev/null
+++ b/keyid.c
@@ -0,0 +1,88 @@
+/*
+ * keyid.c - Routines to calculate key IDs.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#include <sys/types.h>
+
+#include "keyid.h"
+#include "keystructs.h"
+#include "md5.h"
+#include "sha.h"
+
+/**
+ *     get_keyid - Given a public key returns the keyid.
+ *     @publickey: The key to calculate the fingerprint for.
+ */
+uint64_t get_keyid(struct openpgp_publickey *publickey)
+{
+       SHA1_CONTEXT sha_ctx;
+       uint64_t keyid = 0;
+       int offset = 0;
+       int i = 0;
+       unsigned char c;
+       unsigned char *buff = NULL;
+
+       switch (publickey->publickey->data[0]) {
+       case 2:
+       case 3:
+               /*
+                * For a type 2 or 3 key the keyid is the last 64 bits of the
+                * public modulus n, which is stored as an MPI from offset 8
+                * onwards.
+                *
+                * We need to ensure it's an RSA key.
+                */
+               if (publickey->publickey->data[7] == 1) {
+                       offset = (publickey->publickey->data[8] << 8) +
+                               publickey->publickey->data[9];
+                       offset = ((offset + 7) / 8) + 2;
+
+                       for (keyid = 0, i = 0; i < 8; i++) {
+                               keyid <<= 8;
+                               keyid += publickey->publickey->data[offset++];
+                       }
+               } else {
+                       fputs("Type 2 or 3 key, but not RSA.\n", stderr);
+               }
+               break;
+       case 4:
+               /*
+                * For a type 4 key the keyid is the last 64 bits of the
+                * fingerprint, which is the 160 bit SHA-1 hash of the packet
+                * tag, 2 octet packet length and the public key packet
+                * including version field.
+                */
+               sha1_init(&sha_ctx);
+               /*
+                * TODO: Can this be 0x99? Are all public key packets old
+                * format with 2 bytes of length data?
+                */
+               c = 0x99;
+               sha1_write(&sha_ctx, &c, sizeof(c));
+               c = publickey->publickey->length >> 8;
+               sha1_write(&sha_ctx, &c, sizeof(c));
+               c = publickey->publickey->length & 0xFF;
+               sha1_write(&sha_ctx, &c, sizeof(c));
+               sha1_write(&sha_ctx, publickey->publickey->data,
+                       publickey->publickey->length);
+               sha1_final(&sha_ctx);
+               buff = sha1_read(&sha_ctx);
+
+               assert(buff != NULL);
+               
+               for (keyid = 0, i = 12; i < 20; i++) {
+                       keyid <<= 8;
+                       keyid += buff[i];
+               }
+
+               break;
+       default:
+               printf("Unknown key type: %d\n", publickey->publickey->data[0]);
+       }
+
+       return keyid;
+}
diff --git a/keyid.h b/keyid.h
new file mode 100644 (file)
index 0000000..1959a0d
--- /dev/null
+++ b/keyid.h
@@ -0,0 +1,25 @@
+/*
+ * keyid.h - Routines to calculate key IDs.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#ifndef __KEYID_H__
+#define __KEYID_H__
+
+// #include <stdint.h>
+#include <inttypes.h>
+
+#include "keystructs.h"
+
+/**
+ *     get_keyid - Given a public key returns the keyid.
+ *     @publickey: The key to calculate the fingerprint for.
+ *
+ *     This function returns the key id for a given public key.
+ */
+uint64_t get_keyid(struct openpgp_publickey *publickey);
+
+#endif /* __KEYID_H__ */
diff --git a/keyindex.c b/keyindex.c
new file mode 100644 (file)
index 0000000..2c4e24c
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * keyindex.c - Routines to list an OpenPGP key.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "getcgi.h"
+#include "hash.h"
+#include "keydb.h"
+#include "keyid.h"
+#include "keyindex.h"
+#include "keystructs.h"
+#include "ll.h"
+#include "stats.h"
+
+int parse_subpackets(unsigned char *data, bool html)
+{
+       int offset = 0;
+       int length = 0;
+       int packetlen = 0;
+       char *uid;
+
+       assert(data != NULL);
+
+       length = (data[0] << 8) + data[1] + 2;
+
+       offset = 2;
+       while (offset < length) {
+               packetlen = data[offset++];
+               if (packetlen > 191 && packetlen < 255) {
+                       packetlen = ((packetlen - 192) << 8) +
+                                       data[offset++] + 192;
+               } else if (packetlen == 255) {
+                       packetlen = data[offset++];
+                       packetlen <<= 8;
+                       packetlen = data[offset++];
+                       packetlen <<= 8;
+                       packetlen = data[offset++];
+                       packetlen <<= 8;
+                       packetlen = data[offset++];
+               }
+               switch (data[offset]) {
+               case 2:
+                       /*
+                        * Signature creation time. Might want to output this?
+                        */
+                       break;
+               case 16:
+                       uid = keyid2uid((data[offset+packetlen - 4] << 24) +
+                                       (data[offset+packetlen - 3] << 16) +
+                                       (data[offset+packetlen - 2] << 8) +
+                                       data[offset+packetlen - 1]);
+                       if (html && uid != NULL) {
+                               printf("sig        <a href=\"lookup?op=get&"
+                                       "search=%02X%02X%02X%02X\">"
+                                       "%02X%02X%02X%02X</a>             "
+                                       "<a href=\"lookup?op=vindex&"
+                                       "search=0x%02X%02X%02X%02X\">"
+                                       "%s</a>\n",
+                                       data[offset+packetlen - 4],
+                                       data[offset+packetlen - 3],
+                                       data[offset+packetlen - 2],
+                                       data[offset+packetlen - 1],
+                                       data[offset+packetlen - 4],
+                                       data[offset+packetlen - 3],
+                                       data[offset+packetlen - 2],
+                                       data[offset+packetlen - 1],
+
+                                       data[offset+packetlen - 4],
+                                       data[offset+packetlen - 3],
+                                       data[offset+packetlen - 2],
+                                       data[offset+packetlen - 1],
+                                       txt2html(uid));
+                       } else if (html && uid == NULL) {
+                               printf("sig        "
+                                       "%02X%02X%02X%02X             "
+                                       "[User id not found]\n",
+                                       data[offset+packetlen - 4],
+                                       data[offset+packetlen - 3],
+                                       data[offset+packetlen - 2],
+                                       data[offset+packetlen - 1]);
+                       } else {
+                               printf("sig        %02X%02X%02X%02X"
+                                               "             %s\n",
+                                       data[offset+packetlen - 4],
+                                       data[offset+packetlen - 3],
+                                       data[offset+packetlen - 2],
+                                       data[offset+packetlen - 1],
+                                       (uid != NULL) ? uid :
+                                       "[User id not found]");
+                       }
+                       break;
+               default:
+                       /*
+                        * We don't care about unrecognized packets unless bit
+                        * 7 is set in which case we prefer an error than
+                        * ignoring it.
+                        */
+                       assert(!(data[offset] & 0x80));
+               }
+               offset += packetlen;
+       }
+
+       return length;
+}
+
+int list_sigs(struct openpgp_packet_list *sigs, bool html)
+{
+       int length = 0;
+       char *uid;
+
+       while (sigs != NULL) {
+               switch (sigs->packet->data[0]) {
+               case 2:
+               case 3:
+                       uid = keyid2uid((sigs->packet->data[11] << 24) +
+                                       (sigs->packet->data[12] << 16) +
+                                       (sigs->packet->data[13] << 8) +
+                                       sigs->packet->data[14]);
+                       if (html && uid != NULL) {
+                               printf("sig        <a href=\"lookup?op=get&"
+                                       "search=%02X%02X%02X%02X\">"
+                                       "%02X%02X%02X%02X</a>             "
+                                       "<a href=\"lookup?op=vindex&"
+                                       "search=0x%02X%02X%02X%02X\">"
+                                       "%s</a>\n",
+                                       sigs->packet->data[11],
+                                       sigs->packet->data[12],
+                                       sigs->packet->data[13],
+                                       sigs->packet->data[14],
+                                       sigs->packet->data[11],
+                                       sigs->packet->data[12],
+                                       sigs->packet->data[13],
+                                       sigs->packet->data[14],
+
+                                       sigs->packet->data[11],
+                                       sigs->packet->data[12],
+                                       sigs->packet->data[13],
+                                       sigs->packet->data[14],
+                                       txt2html(uid));
+                       } else if (html && uid == NULL) {
+                               printf("sig        %02X%02X%02X%02X"
+                                       "             "
+                                       "[User id not found]\n",
+                                       sigs->packet->data[11],
+                                       sigs->packet->data[12],
+                                       sigs->packet->data[13],
+                                       sigs->packet->data[14]);
+                       } else {
+                               printf("sig        %02X%02X%02X%02X"
+                                               "             %s\n",
+                                       sigs->packet->data[11],
+                                       sigs->packet->data[12],
+                                       sigs->packet->data[13],
+                                       sigs->packet->data[14],
+                                       (uid != NULL) ? uid :
+                                       "[User id not found]");
+                       }
+                       break;
+               case 4:
+                       length = parse_subpackets(&sigs->packet->data[4], html);
+                       parse_subpackets(&sigs->packet->data[length + 4], html);
+                       break;
+               default:
+                       printf("sig        [Unknown packet version %d]",
+                                       sigs->packet->data[0]);
+               }
+               sigs = sigs->next;
+       }
+
+       return 0;
+}
+
+int list_uids(struct openpgp_signedpacket_list *uids, bool verbose, bool html)
+{
+       char buf[1024];
+
+       while (uids != NULL) {
+               if (uids->packet->tag == 13) {
+                       snprintf(buf, 1023, "%.*s",
+                               (int) uids->packet->length,
+                               uids->packet->data);
+                       printf("uid                            %s\n",
+                               (html) ? txt2html(buf) : buf);
+               } else if (uids->packet->tag == 17) {
+                       printf("uid                            "
+                               "[photo id]\n");
+               }
+               if (verbose) {
+                       list_sigs(uids->sigs, html);
+               }
+               uids = uids->next;
+       }
+
+       return 0;
+}
+
+/**
+ *     key_index - List a set of OpenPGP keys.
+ *     @keys: The keys to display.
+ *      @verbose: Should we list sigs as well?
+ *     @fingerprint: List the fingerprint?
+ *     @html: Should the output be tailored for HTML?
+ *
+ *     This function takes a list of OpenPGP public keys and displays an index
+ *     of them. Useful for debugging or the keyserver Index function.
+ */
+int key_index(struct openpgp_publickey *keys, bool verbose, bool fingerprint,
+                       bool html)
+{
+       struct openpgp_signedpacket_list        *curuid = NULL;
+       struct tm                               *created = NULL;
+       time_t                                   created_time = 0;
+       int                                      type = 0;
+       int                                      length = 0;
+       char                                     buf[1024];
+
+       if (html) {
+               puts("<pre>");
+       }
+       puts("Type  bits/keyID    Date       User ID");
+       while (keys != NULL) {
+               created_time = (keys->publickey->data[1] << 24) +
+                                       (keys->publickey->data[2] << 16) +
+                                       (keys->publickey->data[3] << 8) +
+                                       keys->publickey->data[4];
+               created = gmtime(&created_time);
+
+               switch (keys->publickey->data[0]) {
+               case 2:
+               case 3:
+                       type = keys->publickey->data[7];
+                       length = (keys->publickey->data[8] << 8) +
+                                       keys->publickey->data[9];
+                       break;
+               case 4:
+                       type = keys->publickey->data[5];
+                       length = (keys->publickey->data[6] << 8) +
+                                       keys->publickey->data[7];
+                       break;
+               default:
+                       fprintf(stderr, "Unknown key type: %d\n",
+                               keys->publickey->data[0]);
+               }
+               
+               printf("pub  %4d%c/%08X %04d/%02d/%02d ",
+                       length,
+                       (type == 1) ? 'R' : ((type == 17) ? 'D' : '?'),
+                       (uint32_t) (get_keyid(keys) & 0xFFFFFFFF),
+                       created->tm_year + 1900,
+                       created->tm_mon + 1,
+                       created->tm_mday);
+
+               curuid = keys->uids;
+               if (curuid != NULL && curuid->packet->tag == 13) {
+                       snprintf(buf, 1023, "%.*s",
+                               (int) curuid->packet->length,
+                               curuid->packet->data);
+                       printf("%s\n", (html) ? txt2html(buf) : buf);
+                       if (verbose) {
+
+                               list_sigs(curuid->sigs, html);
+                       }
+                       curuid = curuid->next;
+               } else {
+                       putchar('\n');
+               }
+
+               list_uids(curuid, verbose, html);
+
+               //TODO: List subkeys.
+
+               keys = keys->next;
+       }
+
+       if (html) {
+               puts("</pre>");
+       }
+
+       return 0;
+}
+
+
+int get_subpackets_keyid(unsigned char *data, uint64_t *keyid)
+{
+       int offset = 0;
+       int length = 0;
+       int packetlen = 0;
+
+       assert(data != NULL);
+
+       length = (data[0] << 8) + data[1] + 2;
+
+       offset = 2;
+       while (offset < length) {
+               packetlen = data[offset++];
+               if (packetlen > 191 && packetlen < 255) {
+                       packetlen = ((packetlen - 192) << 8) +
+                                       data[offset++] + 192;
+               } else if (packetlen == 255) {
+                       packetlen = data[offset++];
+                       packetlen <<= 8;
+                       packetlen = data[offset++];
+                       packetlen <<= 8;
+                       packetlen = data[offset++];
+                       packetlen <<= 8;
+                       packetlen = data[offset++];
+               }
+               switch (data[offset]) {
+               case 2:
+                       /*
+                        * Signature creation time. Might want to output this?
+                        */
+                       break;
+               case 16:
+                       *keyid = (data[offset+packetlen - 4] << 24) +
+                               (data[offset+packetlen - 3] << 16) +
+                               (data[offset+packetlen - 2] << 8) +
+                               data[offset+packetlen - 1];
+                       *keyid &= 0xFFFFFFFF;
+                       break;
+               default:
+                       /*
+                        * We don't care about unrecognized packets unless bit
+                        * 7 is set in which case we prefer an error than
+                        * ignoring it.
+                        */
+                       assert(!(data[offset] & 0x80));
+               }
+               offset += packetlen;
+       }
+
+       return length;
+}
+
+
+/**
+ *     keysigs - Return the sigs on a given OpenPGP signature list.
+ *     @curll: The current linked list. Can be NULL to create a new list.
+ *     @sigs: The signature list we want the sigs on.
+ *
+ *     Returns a linked list of stats_key elements containing the sigs on the
+ *     supplied OpenPGP packet list.
+ */
+struct ll *keysigs(struct ll *curll,
+               struct openpgp_packet_list *sigs)
+{
+       int length = 0;
+       uint64_t keyid = 0;
+       
+       while (sigs != NULL) {
+               keyid = 0;
+               switch (sigs->packet->data[0]) {
+               case 2:
+               case 3:
+                       keyid = sigs->packet->data[11] << 24;
+                       keyid += (sigs->packet->data[12] << 16);
+                       keyid += (sigs->packet->data[13] << 8);
+                       keyid += sigs->packet->data[14];
+                       keyid &= 0xFFFFFFFF;
+                       break;
+               case 4:
+                       length = get_subpackets_keyid(&sigs->packet->data[4],
+                                       &keyid);
+                       get_subpackets_keyid(&sigs->packet->data[length + 4],
+                                       &keyid);
+                       /*
+                        * Don't bother to look at the unsigned packets.
+                        */
+                       break;
+               default:
+                       break;
+               }
+               sigs = sigs->next;
+               curll = lladd(curll, createandaddtohash(keyid));
+       }
+
+       return curll;
+}
diff --git a/keyindex.h b/keyindex.h
new file mode 100644 (file)
index 0000000..81b0287
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * keyindex.h - Routines to list an OpenPGP key.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#ifndef __KEYINDEX_H__
+#define __KEYINDEX_H__
+
+#include <stdbool.h>
+
+#include "keystructs.h"
+
+/**
+ *     key_index - List a set of OpenPGP keys.
+ *     @keys: The keys to display.
+ *      @verbose: Should we list sigs as well?
+ *     @fingerprint: List the fingerprint?
+ *     @html: Should we tailor the output for HTML?
+ *
+ *     This function takes a list of OpenPGP public keys and displays an index
+ *     of them. Useful for debugging or the keyserver Index function.
+ */
+int key_index(struct openpgp_publickey *keys, bool verbose,
+               bool fingerprint, bool html);
+
+/**
+ *     keysigs - Return the sigs on a given OpenPGP signature packet list.
+ *     @curll: The current linked list. Can be NULL to create a new list.
+ *     @sigs: The signature list we want the sigs on.
+ *
+ *     Returns a linked list of stats_key elements containing the sigs for the
+ *     supplied OpenPGP signature packet list.
+ */
+struct ll *keysigs(struct ll *curll,
+               struct openpgp_packet_list *sigs);
+
+#endif
diff --git a/keymerge.c b/keymerge.c
new file mode 100644 (file)
index 0000000..09d71fc
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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, unsigned char *c)
+{
+       int ic;
+       ic = getchar();
+       *c = ic;
+       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;
+       struct openpgp_publickey        *prev = NULL;
+       struct openpgp_publickey        *curkey = NULL;
+       struct openpgp_publickey        *oldkey = NULL;
+       int                              newkeys = 0;
+       int                              rc = EXIT_SUCCESS;
+
+       dearmor_openpgp_stream(stdin_getchar, NULL, &packets);
+       parse_keys(packets, &keys);
+       free_packet_list(packets);
+       packets = NULL;
+
+       initdb();
+       for (curkey = keys; curkey != NULL; curkey = curkey->next) {
+               fprintf(stderr, "Dealing with key.\n");
+               fprintf(stderr, "fetch_key: %d\n",
+                               fetch_key(get_keyid(curkey), &oldkey));
+
+               /*
+                * If we already have the key stored in the DB then merge it
+                * with the new one that's been supplied. Otherwise the key
+                * we've just got is the one that goes in the DB and also the
+                * one that we send out.
+                */
+               if (oldkey != NULL) {
+                       fprintf(stderr, "merge_keys: %d\n",
+                                       merge_keys(oldkey, curkey));
+                       if (curkey->revocations == NULL &&
+                                       curkey->uids == NULL &&
+                                       curkey->subkeys == NULL) {
+                               fprintf(stderr, "No new info.\n");
+                               if (prev == NULL) {
+                                       keys = curkey->next;
+                               } else {
+                                       prev->next = curkey->next;
+                                       prev = curkey->next;
+                               }
+                       } else {
+                               prev = curkey;
+                       }
+                       /* TODO: store_key(oldkey); */
+                       free_publickey(oldkey);
+                       oldkey = NULL;
+               } else {
+                       store_key(curkey);
+                       newkeys++;
+               }
+       }
+       cleanupdb();
+
+       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;
+       }
+
+       return rc;
+}
diff --git a/keys/autodns.key b/keys/autodns.key
new file mode 100644 (file)
index 0000000..a431a17
Binary files /dev/null and b/keys/autodns.key differ
diff --git a/keys/blackcat.key b/keys/blackcat.key
new file mode 100644 (file)
index 0000000..1cd33d6
Binary files /dev/null and b/keys/blackcat.key differ
diff --git a/keys/iwj.key b/keys/iwj.key
new file mode 100644 (file)
index 0000000..2e1eb43
Binary files /dev/null and b/keys/iwj.key differ
diff --git a/keys/manysubkeys.key b/keys/manysubkeys.key
new file mode 100644 (file)
index 0000000..cdf5888
Binary files /dev/null and b/keys/manysubkeys.key differ
diff --git a/keys/mjr.new b/keys/mjr.new
new file mode 100644 (file)
index 0000000..d180902
Binary files /dev/null and b/keys/mjr.new differ
diff --git a/keys/noodles.key b/keys/noodles.key
new file mode 100644 (file)
index 0000000..bd5dd36
Binary files /dev/null and b/keys/noodles.key differ
diff --git a/keys/swhite.key b/keys/swhite.key
new file mode 100644 (file)
index 0000000..24c767d
Binary files /dev/null and b/keys/swhite.key differ
diff --git a/keys/v4.key b/keys/v4.key
new file mode 100644 (file)
index 0000000..1d57320
Binary files /dev/null and b/keys/v4.key differ
diff --git a/keystructs.h b/keystructs.h
new file mode 100644 (file)
index 0000000..33dba65
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * keystructs.h - Structures for OpenPGP keys
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#ifndef __KEYSTRUCTS_H__
+#define __KEYSTRUCTS_H__
+
+#include <stdbool.h>
+
+/**
+ *     struct openpgp_packet - Stores an OpenPGP packet.
+ *     @tag: The packet tag (ie type).
+ *     @newformat: Indicates if this is a new format packet.
+ *     @length: The length of the packet.
+ *     @data: The actual packet
+ *
+ *     This structure holds any form of OpenPGP packet with minimum common
+ *     details decoded out.
+ */
+struct openpgp_packet {
+       unsigned int tag;
+       bool newformat;
+       size_t length;
+       unsigned char *data;
+};
+
+/**
+ *     struct openpgp_packet_list - A linked list of OpenPGP packets.
+ *     @packet: The actual packet structure.
+ *     @next: A pointer to the next packet in the list.
+ *
+ *     This structure is used to hold a linked list of packets, for example
+ *     all the signatures of a public key's UID.
+ */
+struct openpgp_packet_list {
+       struct openpgp_packet *packet;
+       struct openpgp_packet_list *next;
+};
+
+/**
+ *     struct openpgp_signedpacket_list - A packet with signatures.
+ *     @uid: The OpenPGP packet that's signed.
+ *     @sigs: A list of sigs for the packet.
+ *     @next: A pointer to the next packet with signatures.
+ *
+ *     This structure holds an OpenPGP packet along with signatures that are
+ *     over this packet. It also links to the next signed packet. It's usually
+ *     used to hold a UID or subkey with their associated signatures.
+ */
+struct openpgp_signedpacket_list {
+       struct openpgp_packet *packet;
+       struct openpgp_packet_list *sigs;
+       struct openpgp_packet_list *last_sig;
+       struct openpgp_signedpacket_list *next;
+};
+
+/**
+ *     struct openpgp_publickey - An OpenPGP public key complete with sigs.
+ *     @publickey: The OpenPGP packet for the public key.
+ *     @revocation: The OpenPGP packet for the revocation [optional]
+ *     @uids: The list of UIDs with signatures for this key.
+ *     @subkeys: The list of subkeys with signatures for this key.
+ *     @next: The next public key.
+ */
+struct openpgp_publickey {
+       struct openpgp_packet                   *publickey;
+       struct openpgp_packet_list              *revocations;
+       struct openpgp_packet_list              *last_revocation;
+       struct openpgp_signedpacket_list        *uids;
+       struct openpgp_signedpacket_list        *last_uid;
+       struct openpgp_signedpacket_list        *subkeys;
+       struct openpgp_signedpacket_list        *last_subkey;
+       struct openpgp_publickey                *next;
+};
+
+#endif /* __KEYSTRUCTS_H__ */
diff --git a/ll.c b/ll.c
new file mode 100644 (file)
index 0000000..acad0bc
--- /dev/null
+++ b/ll.c
@@ -0,0 +1,75 @@
+/*
+ * ll.c - various things of used for dealing with linked lists.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2000-2002 Project Purple
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ll.h"
+
+struct ll *lladd(struct ll *curll, void *object)
+{
+       struct ll *new;
+
+       if ((new = malloc(sizeof(struct ll))) == NULL) {
+               perror("lladd()");
+               printf("Got NULL in lladd()\n");
+               return NULL;
+       }
+
+       new->next = curll;
+       new->object = object;
+
+       return new;
+}
+
+struct ll *lldel(struct ll *curll, void *object,
+       int (*objectcmp) (const void *object1, const void *object2))
+{
+       struct ll *cur;
+
+       assert(objectcmp != NULL);
+
+       cur = curll;
+       if (cur == NULL) {
+               return NULL;
+       } else if (!(*objectcmp)(cur->object, object)) {
+               return cur->next;
+       } 
+       while (cur->next != NULL) {
+               if (!(*objectcmp)(cur->next->object, object)) {
+                       cur->next = cur->next->next;
+                       break;
+               }
+       }
+       return curll;
+}
+
+struct ll *llfind(struct ll *curll, void *object,
+       int (*objectcmp) (const void *object1, const void *object2))
+{
+       struct ll *cur;
+
+       cur = curll;
+       while (cur != NULL && (*objectcmp)(cur->object, object)) {
+               cur = cur->next;
+       }
+       return cur;
+}
+
+unsigned long llsize(struct ll *curll)
+{
+       unsigned long count = 0;
+
+       while (curll != NULL) {
+               count++;
+               curll = curll->next;
+       }
+
+       return count;
+}
diff --git a/ll.h b/ll.h
new file mode 100644 (file)
index 0000000..3890ca9
--- /dev/null
+++ b/ll.h
@@ -0,0 +1,72 @@
+/*
+ * ll.h - various things of used for dealing with linked lists.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#ifndef __LL_H__
+#define __LL_H__
+
+#define ADD_PACKET_TO_LIST_END(list, name, item)                              \
+       if (list->name##s != NULL) {                                          \
+               list->last_##name->next = malloc(sizeof (*list->last_##name));\
+               list->last_##name = list->last_##name->next;                  \
+       } else {                                                              \
+               list->name##s = list->last_##name =                           \
+                       malloc(sizeof (*list->last_##name));                  \
+       }                                                                     \
+       memset(list->last_##name, 0, sizeof(*list->last_##name));             \
+       list->last_##name->packet = item;
+
+#define ADD_PACKET_TO_LIST(list, item)                                        \
+       if (list != NULL) {                                                   \
+               list->next = malloc(sizeof (*list));                          \
+               list = list->next;                                            \
+       } else {                                                              \
+               list = malloc(sizeof (*list));                                \
+       }                                                                     \
+       memset(list, 0, sizeof(*list));                                       \
+       list->packet = item;
+
+/**
+ *     struct ll - A generic linked list structure.
+ *     @object: The object.
+ *     @next: A pointer to the next object.
+ */
+struct ll {
+       void *object;
+       struct ll *next;
+};
+
+/**
+ *     lladd - Add an item to a linked list.
+ *     @curll: The list to add to. Can be NULL to create a new list.
+ *     @object: The object to add.
+ *
+ *     Returns a pointer to the head of the new list.
+ */
+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 *llfind(struct ll *curll, void *object,
+       int (*objectcmp) (const void *object1, const void *object2));
+
+/**
+ *     llsize - Returns the number of elements in a linked list.
+ *     @curll: The linked list to count.
+ *
+ *     Counts the number of elements in a linked list.
+ */
+unsigned long llsize(struct ll *curll);
+
+#endif /* __LL_H__ */
diff --git a/lookup.c b/lookup.c
new file mode 100644 (file)
index 0000000..8f37fd2
--- /dev/null
+++ b/lookup.c
@@ -0,0 +1,144 @@
+/*
+ * lookup.c - CGI to lookup keys.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+//#include <stdint.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "armor.h"
+#include "getcgi.h"
+#include "keydb.h"
+#include "keyindex.h"
+#include "mem.h"
+#include "parsekey.h"
+
+#define OP_UNKNOWN 0
+#define OP_GET     1
+#define OP_INDEX   2
+#define OP_VINDEX  3
+
+int putnextchar(void *ctx, unsigned char c)
+{
+        return putchar(c);
+}
+
+void find_keys(char *search, uint64_t keyid, bool ishex,
+               bool fingerprint, bool exact, bool verbose)
+{
+       struct openpgp_publickey *publickey = NULL;
+       bool found = false;
+
+       if (ishex) {
+               if (fetch_key(keyid, &publickey)) {
+                       if (publickey != NULL) {
+                               key_index(publickey, verbose, fingerprint,
+                                               true);
+                               free_publickey(publickey);
+                               found = true;
+                       }
+               }
+       }
+       if (!found) {
+               puts("Key not found.");
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       char **params = NULL;
+       int op = OP_UNKNOWN;
+       int i;
+       bool fingerprint = false;
+       bool exact = false;
+       bool ishex = false;
+       uint64_t keyid = 0;
+       char *search = NULL;
+       char *end = NULL;
+       struct openpgp_publickey *publickey = NULL;
+       struct openpgp_packet_list *packets = NULL;
+       struct openpgp_packet_list *list_end = NULL;
+
+       params = getcgivars(argc, argv);
+       for (i = 0; params != NULL && params[i] != NULL; i += 2) {
+               if (!strcmp(params[i], "op")) {
+                       if (!strcmp(params[i+1], "get")) {
+                               op = OP_GET;
+                       } else if (!strcmp(params[i+1], "index")) {
+                               op = OP_INDEX;
+                       } else if (!strcmp(params[i+1], "vindex")) {
+                               op = OP_VINDEX;
+                       }
+               } else if (!strcmp(params[i], "search")) {
+                       search = params[i+1];
+                       if (search != NULL) {
+                               keyid = strtoul(search, &end, 16);
+                               if (*search != 0 &&
+                                               end != NULL &&
+                                               *end == 0) {
+                                       ishex = true;
+                               }
+                       }
+               } else if (!strcmp(params[i], "fingerprint")) {
+                       if (!strcmp(params[i+1], "on")) {
+                               fingerprint = true;
+                       }
+               } else if (!strcmp(params[i], "exact")) {
+                       if (!strcmp(params[i+1], "on")) {
+                               exact = true;
+                       }
+               }
+       }
+
+//     puts("HTTP/1.0 200 OK");
+//     puts("Server: onak 0.0.1");
+       puts("Content-Type: text/html\n");
+       puts("<html>\n<title>Lookup of key</title>");
+       puts("<body>");
+
+       if (op == OP_UNKNOWN) {
+               puts("Error: No operation supplied.");
+       } else if (search == NULL) {
+               puts("Error: No key to search for supplied.");
+       } else {
+               initdb();
+               switch (op) {
+               case OP_GET:
+                       if (fetch_key(keyid, &publickey)) {
+                               puts("<pre>");
+                               flatten_publickey(publickey,
+                                                       &packets,
+                                                       &list_end);
+                               armor_openpgp_stream(putnextchar,
+                                               NULL,
+                                               packets);
+                               puts("</pre>");
+                       } else {
+                               puts("Key not found");
+                       }
+                       break;
+               case OP_INDEX:
+                       find_keys(search, keyid, ishex, fingerprint, exact,
+                                       false);
+                       break;
+               case OP_VINDEX:
+                       find_keys(search, keyid, ishex, fingerprint, exact,
+                                       true);
+                       break;
+               default:
+                       puts("Unknown operation!");
+               }
+               cleanupdb();
+       }
+       puts("<hr>");
+       puts("Produced by onak 0.0.1 by Jonathan McDowell");
+       puts("</body>\n</html>");
+       return (EXIT_SUCCESS);
+}
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..1f86873
--- /dev/null
+++ b/main.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "armor.h"
+#include "keydb.h"
+#include "keyid.h"
+#include "keyindex.h"
+#include "keystructs.h"
+#include "parsekey.h"
+
+int getnextchar(void *ctx, size_t count, unsigned char *c)
+{
+        return (!read(0, c, count));
+}
+
+int putnextchar(void *ctx, unsigned char c)
+{
+        return (!write(1, &c, 1));
+}
+
+
+int main(int argc, char *argv[])
+{
+       struct openpgp_packet_list *packets = NULL, *newpackets = NULL;
+       struct openpgp_packet_list *list_end = NULL;
+       struct openpgp_publickey *keys = NULL;
+       struct openpgp_publickey *newkeys = NULL;
+       void *ctx = NULL;
+
+       fputs("Doing read_openpgp_stream():\n", stderr);
+       read_openpgp_stream(getnextchar, ctx, &packets);
+
+//     fputs("Doing dearmor_openpgp_stream():\n", stderr);
+//     dearmor_openpgp_stream(getnextchar, NULL, &packets);
+//     fputs("Doing armor_openpgp_stream():\n", stderr);
+//     armor_openpgp_stream(putnextchar, NULL, packets);
+
+       fputs("Doing parse_keys():\n", stderr);
+       parse_keys(packets, &keys);
+
+       printf("Key id is 0x%llX\n", get_keyid(keys));
+
+//     key_index(keys, true, false, false);
+
+       initdb();
+       fetch_key(get_keyid(keys), &newkeys);
+       cleanupdb();
+
+       printf("New key id is 0x%llX\n", get_keyid(newkeys));
+
+//     fputs("Doing flatten_publickey():\n", stderr);
+//     flatten_publickey(keys, &newpackets, &list_end);
+
+//     fputs("Doing write_openpgp_stream():\n", stderr);
+//     write_openpgp_stream(putnextchar, ctx, newpackets);
+
+       return 0;
+}
diff --git a/mathopd.conf b/mathopd.conf
new file mode 100644 (file)
index 0000000..841c77e
--- /dev/null
@@ -0,0 +1,89 @@
+Umask 026
+
+Tuning {
+       NumConnections 120
+       BufSize 12288
+}
+
+StayRoot On
+
+PIDFile /home/noodles/tmp/mathopd/pid
+Log /home/noodles/tmp/mathopd/log.%Y%m%d
+ErrorLog /home/noodles/tmp/mathopd/errors.%Y%m
+
+Control {
+       Symlinks On
+       Types {
+               text/plain { * }
+               text/css { css }
+               application/octet-stream { zip gz tgz exe class }
+               application/futuresplash { spl }
+               model/vrml { wrl }
+               application/pdf { pdf }
+               text/html { html htm }
+               image/gif { gif }
+               image/jpeg { jpg }
+       }
+       Specials {
+               Imagemap { map }
+               CGI { cgi }
+               Redirect { url }
+       }
+       External {
+               /usr/bin/perl { pl }
+       }
+       IndexNames { home.html index.htm index.html redirect.url }
+}
+
+DefaultName localhost
+
+Server {
+       Port 11371
+
+       Virtual {
+       Control {
+               Alias /
+               Location /home/noodles/onak-0.0.1
+       }
+
+       Control {
+               Alias /pks
+               Location /home/noodles/onak-0.0.1/pks
+               Specials {
+                       CGI { * }
+               }
+       }
+       }
+
+       Virtual {
+               Host localhost:11371
+               Control {
+                       Alias /
+                       Location /home/noodles/onak-0.0.1
+               }
+
+               Control {
+                       Alias /pks
+                       Location /home/noodles/onak-0.0.1/pks
+                       Specials {
+                               CGI { * }
+                       }
+               }
+       }
+       Virtual {
+               Host maeve.orange:11371
+               Control {
+                       Alias /
+                       Location /home/noodles/onak-0.0.1
+               }
+
+               Control {
+                       Alias /pks
+                       Location /home/noodles/onak-0.0.1/pks
+                       Specials {
+                               CGI { * }
+                       }
+               }
+       }
+
+}
diff --git a/maxpath.c b/maxpath.c
new file mode 100644 (file)
index 0000000..3714281
--- /dev/null
+++ b/maxpath.c
@@ -0,0 +1,66 @@
+/*
+       gpgstats.c - Program to produce stats on a GPG keyring.
+       Written by Jonathan McDowell <noodles@earth.li>.
+
+       19/02/2000 - Started writing (sort of).
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "stats.h"
+#include "hash.h"
+#include "keydb.h"
+#include "ll.h"
+#include "stats.h"
+
+void findmaxpath(unsigned long max)
+{
+       struct stats_key *from, *to, *tmp;
+       struct ll *curkey;
+       unsigned long distance, loop;
+
+       printf("In findmaxpath\n");
+       distance = 0;
+       from = to = tmp = NULL;
+       hash_getkeysigs(0x5B430367);
+
+       for (loop = 0; (loop < HASHSIZE) && (distance < max); loop++) {
+               curkey = gethashtableentry(loop);
+               while (curkey != NULL && distance < max) {
+                       hash_getkeysigs(((struct stats_key *)
+                                       curkey->object)->keyid);
+                       initcolour(false);
+                       tmp = furthestkey((struct stats_key *)
+                                               curkey->object);
+                       if (tmp->colour > distance) {
+                               from = (struct stats_key *)curkey->object;
+                               to = tmp;
+                               distance = to->colour;
+                               printf("Current max path (#%ld) is from %llX to %llX (%ld steps)\n", 
+                                               loop,
+                                               from->keyid,
+                                               to->keyid,
+                                               distance);
+                       }
+                       curkey=curkey->next;
+               }
+       }
+       printf("Max path is from %llX to %llX (%ld steps)\n",
+                       from->keyid,
+                       to->keyid,
+                       distance);
+}
+
+int main(int argc, char *argv[])
+{
+       initdb();
+       inithash();
+       findmaxpath(30);
+       printf("--------\n");
+       findmaxpath(30);
+       cleanupdb();
+       
+       return EXIT_SUCCESS;
+}
diff --git a/md5.c b/md5.c
new file mode 100644 (file)
index 0000000..4707d63
--- /dev/null
+++ b/md5.c
@@ -0,0 +1,277 @@
+/* md5.c - MD5 Message-Digest Algorithm
+ *     Copyright (C) 1995, 1996, 1998, 1999 Free Software Foundation, Inc.
+ *
+ * according to the definition of MD5 in RFC 1321 from April 1992.
+ * NOTE: This is *not* the same file as the one from glibc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.  */
+/* heavily modified for GnuPG by <werner.koch@guug.de> */
+/* Heavily modified for CryptNET KeyServer by V. Alex Brennen <vab@cryptnet.net> */
+
+/* Test values:
+ * ""                  D4 1D 8C D9 8F 00 B2 04  E9 80 09 98 EC F8 42 7E
+ * "a"                 0C C1 75 B9 C0 F1 B6 A8  31 C3 99 E2 69 77 26 61
+ * "abc                90 01 50 98 3C D2 4F B0  D6 96 3F 7D 28 E1 7F 72
+ * "message digest"    F9 6B 69 7D 7C B7 93 8D  52 5A 2F 31 AA F1 61 D0
+ */
+#include "md5.h"
+
+
+void md5_init( MD5_CONTEXT *ctx )
+{
+    ctx->A = 0x67452301;
+    ctx->B = 0xefcdab89;
+    ctx->C = 0x98badcfe;
+    ctx->D = 0x10325476;
+
+    ctx->nblocks = 0;
+    ctx->count = 0;
+}
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+   and defined in the RFC 1321.  The first function is a little bit optimized
+   (as found in Colin Plumbs public domain implementation).  */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+#define DIM(v) (sizeof(v)/sizeof((v)[0]))
+
+/****************
+ * transform n*64 bytes
+ */
+static void transform( MD5_CONTEXT *ctx, unsigned char *data )
+{
+    unsigned int correct_words[16];
+    unsigned int A = ctx->A;
+    unsigned int B = ctx->B;
+    unsigned int C = ctx->C;
+    unsigned int D = ctx->D;
+    unsigned int *cwp = correct_words;
+
+    memcpy( correct_words, data, 64 );
+
+
+#define OP(a, b, c, d, s, T)                                       \
+  do                                                               \
+    {                                                              \
+      a += FF (b, c, d) + (*cwp++) + T;            \
+      a = rol(a, s);                                               \
+      a += b;                                                      \
+    }                                                              \
+  while (0)
+
+    /* Before we start, one word about the strange constants.
+       They are defined in RFC 1321 as
+
+       T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+     */
+
+    /* Round 1.  */
+    OP (A, B, C, D,  7, 0xd76aa478);
+    OP (D, A, B, C, 12, 0xe8c7b756);
+    OP (C, D, A, B, 17, 0x242070db);
+    OP (B, C, D, A, 22, 0xc1bdceee);
+    OP (A, B, C, D,  7, 0xf57c0faf);
+    OP (D, A, B, C, 12, 0x4787c62a);
+    OP (C, D, A, B, 17, 0xa8304613);
+    OP (B, C, D, A, 22, 0xfd469501);
+    OP (A, B, C, D,  7, 0x698098d8);
+    OP (D, A, B, C, 12, 0x8b44f7af);
+    OP (C, D, A, B, 17, 0xffff5bb1);
+    OP (B, C, D, A, 22, 0x895cd7be);
+    OP (A, B, C, D,  7, 0x6b901122);
+    OP (D, A, B, C, 12, 0xfd987193);
+    OP (C, D, A, B, 17, 0xa679438e);
+    OP (B, C, D, A, 22, 0x49b40821);
+
+#undef OP
+#define OP(f, a, b, c, d, k, s, T)  \
+    do                                                               \
+      {                                                              \
+       a += f (b, c, d) + correct_words[k] + T;                      \
+       a = rol(a, s);                                                \
+       a += b;                                                       \
+      }                                                              \
+    while (0)
+
+    /* Round 2.  */
+    OP (FG, A, B, C, D,  1,  5, 0xf61e2562);
+    OP (FG, D, A, B, C,  6,  9, 0xc040b340);
+    OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
+    OP (FG, B, C, D, A,  0, 20, 0xe9b6c7aa);
+    OP (FG, A, B, C, D,  5,  5, 0xd62f105d);
+    OP (FG, D, A, B, C, 10,  9, 0x02441453);
+    OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
+    OP (FG, B, C, D, A,  4, 20, 0xe7d3fbc8);
+    OP (FG, A, B, C, D,  9,  5, 0x21e1cde6);
+    OP (FG, D, A, B, C, 14,  9, 0xc33707d6);
+    OP (FG, C, D, A, B,  3, 14, 0xf4d50d87);
+    OP (FG, B, C, D, A,  8, 20, 0x455a14ed);
+    OP (FG, A, B, C, D, 13,  5, 0xa9e3e905);
+    OP (FG, D, A, B, C,  2,  9, 0xfcefa3f8);
+    OP (FG, C, D, A, B,  7, 14, 0x676f02d9);
+    OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+    /* Round 3.  */
+    OP (FH, A, B, C, D,  5,  4, 0xfffa3942);
+    OP (FH, D, A, B, C,  8, 11, 0x8771f681);
+    OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
+    OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
+    OP (FH, A, B, C, D,  1,  4, 0xa4beea44);
+    OP (FH, D, A, B, C,  4, 11, 0x4bdecfa9);
+    OP (FH, C, D, A, B,  7, 16, 0xf6bb4b60);
+    OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
+    OP (FH, A, B, C, D, 13,  4, 0x289b7ec6);
+    OP (FH, D, A, B, C,  0, 11, 0xeaa127fa);
+    OP (FH, C, D, A, B,  3, 16, 0xd4ef3085);
+    OP (FH, B, C, D, A,  6, 23, 0x04881d05);
+    OP (FH, A, B, C, D,  9,  4, 0xd9d4d039);
+    OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
+    OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+    OP (FH, B, C, D, A,  2, 23, 0xc4ac5665);
+
+    /* Round 4.  */
+    OP (FI, A, B, C, D,  0,  6, 0xf4292244);
+    OP (FI, D, A, B, C,  7, 10, 0x432aff97);
+    OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
+    OP (FI, B, C, D, A,  5, 21, 0xfc93a039);
+    OP (FI, A, B, C, D, 12,  6, 0x655b59c3);
+    OP (FI, D, A, B, C,  3, 10, 0x8f0ccc92);
+    OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
+    OP (FI, B, C, D, A,  1, 21, 0x85845dd1);
+    OP (FI, A, B, C, D,  8,  6, 0x6fa87e4f);
+    OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+    OP (FI, C, D, A, B,  6, 15, 0xa3014314);
+    OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
+    OP (FI, A, B, C, D,  4,  6, 0xf7537e82);
+    OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
+    OP (FI, C, D, A, B,  2, 15, 0x2ad7d2bb);
+    OP (FI, B, C, D, A,  9, 21, 0xeb86d391);
+
+    /* Put checksum in context given as argument.  */
+    ctx->A += A;
+    ctx->B += B;
+    ctx->C += C;
+    ctx->D += D;
+}
+
+
+
+/* The routine updates the message-digest context to
+ * account for the presence of each of the characters inBuf[0..inLen-1]
+ * in the message whose digest is being computed.
+ */
+void md5_write( MD5_CONTEXT *hd, unsigned char *inbuf, size_t inlen)
+{
+    if( hd->count == 64 ) { /* flush the buffer */
+       transform( hd, hd->buf );
+       hd->count = 0;
+       hd->nblocks++;
+    }
+    if( !inbuf )
+       return;
+    if( hd->count ) {
+       for( ; inlen && hd->count < 64; inlen-- )
+           hd->buf[hd->count++] = *inbuf++;
+       md5_write( hd, NULL, 0 );
+       if( !inlen )
+           return;
+    }
+
+    while( inlen >= 64 ) {
+       transform( hd, inbuf );
+       hd->count = 0;
+       hd->nblocks++;
+       inlen -= 64;
+       inbuf += 64;
+    }
+    for( ; inlen && hd->count < 64; inlen-- )
+       hd->buf[hd->count++] = *inbuf++;
+
+}
+
+
+
+/* The routine final terminates the message-digest computation and
+ * ends with the desired message digest in mdContext->digest[0...15].
+ * The handle is prepared for a new MD5 cycle.
+ * Returns 16 bytes representing the digest.
+ */
+
+void md5_final( MD5_CONTEXT *hd )
+{
+    unsigned int t, msb, lsb;
+    unsigned char *p;
+
+    md5_write(hd, NULL, 0); /* flush */;
+
+    msb = 0;
+    t = hd->nblocks;
+    if( (lsb = t << 6) < t ) /* multiply by 64 to make a byte count */
+       msb++;
+    msb += t >> 26;
+    t = lsb;
+    if( (lsb = t + hd->count) < t ) /* add the count */
+       msb++;
+    t = lsb;
+    if( (lsb = t << 3) < t ) /* multiply by 8 to make a bit count */
+       msb++;
+    msb += t >> 29;
+
+    if( hd->count < 56 ) { /* enough room */
+       hd->buf[hd->count++] = 0x80; /* pad */
+       while( hd->count < 56 )
+           hd->buf[hd->count++] = 0;  /* pad */
+    }
+    else { /* need one extra block */
+       hd->buf[hd->count++] = 0x80; /* pad character */
+       while( hd->count < 64 )
+           hd->buf[hd->count++] = 0;
+       md5_write(hd, NULL, 0);  /* flush */;
+       memset(hd->buf, 0, 56 ); /* fill next block with zeroes */
+    }
+    /* append the 64 bit count */
+    hd->buf[56] = lsb     ;
+    hd->buf[57] = lsb >>  8;
+    hd->buf[58] = lsb >> 16;
+    hd->buf[59] = lsb >> 24;
+    hd->buf[60] = msb     ;
+    hd->buf[61] = msb >>  8;
+    hd->buf[62] = msb >> 16;
+    hd->buf[63] = msb >> 24;
+    transform( hd, hd->buf );
+
+    p = hd->buf;
+    /* little endian */
+    /*#define X(a) do { *(u32*)p = hd->##a ; p += 4; } while(0)*/
+    /* Unixware's cpp doesn't like the above construct so we do it his way:
+     * (reported by Allan Clark) */
+    #define X(a) do { *(unsigned int *)p = (*hd).a ; p += 4; } while(0)
+    X(A);
+    X(B);
+    X(C);
+    X(D);
+  #undef X
+
+}
+
+unsigned char *md5_read(MD5_CONTEXT *hd)
+{
+    return hd->buf;
+}
diff --git a/md5.h b/md5.h
new file mode 100644 (file)
index 0000000..65a2159
--- /dev/null
+++ b/md5.h
@@ -0,0 +1,23 @@
+#ifndef __MD5_H__
+#define __MD5_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "bithelp.h"
+
+typedef struct {
+    unsigned int  A,B,C,D;       /* chaining variables */
+    unsigned int  nblocks;
+    unsigned char buf[64];
+    int  count;
+} MD5_CONTEXT;
+
+void md5_init(MD5_CONTEXT *);
+void md5_write(MD5_CONTEXT *, unsigned char *, size_t);
+void md5_final(MD5_CONTEXT *);
+unsigned char *md5_read(MD5_CONTEXT *);
+
+#endif /* __MD5_H__ */
diff --git a/mem.c b/mem.c
new file mode 100644 (file)
index 0000000..1b9627c
--- /dev/null
+++ b/mem.c
@@ -0,0 +1,173 @@
+/*
+ * mem.c - Routines to cleanup memory after use.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "keystructs.h"
+#include "ll.h"
+#include "mem.h"
+
+/**
+ *     packet_dup - duplicate an OpenPGP packet.
+ *     @packet: The packet to duplicate.
+ *
+ *     This function takes an OpenPGP packet structure and duplicates it,
+ *     including the data part. It returns NULL if there is a problem
+ *     allocating memory for the duplicate.
+ */
+struct openpgp_packet *packet_dup(struct openpgp_packet *packet)
+{
+       struct openpgp_packet *newpacket = NULL;
+
+       assert(packet != NULL);
+
+       newpacket = malloc(sizeof (struct openpgp_packet));
+       if (newpacket != NULL) {
+               newpacket->tag = packet->tag;
+               newpacket->newformat = packet->newformat;
+               newpacket->length = packet->length;
+               newpacket->data = malloc(newpacket->length);
+               if (newpacket->data != NULL) {
+                       memcpy(newpacket->data, packet->data,
+                                       newpacket->length);
+               }
+       }
+
+       return newpacket;
+}
+
+/**
+ *     packet_list_add - Adds an OpenPGP packet list to another.
+ *     @list: The packet list to add to.
+ *     @list_end: The end of the packet list to add to.
+ *     @packet_list: The packet list to add.
+ *
+ *     This function takes an OpenPGP packet list and adds it to another list,
+ *     duplicating it in the process. The list to add to need not exists to
+ *     begin with, in which case the function simply duplicates the supplied
+ *     list.
+ */
+void packet_list_add(struct openpgp_packet_list **list,
+               struct openpgp_packet_list **list_end,
+               struct openpgp_packet_list *packet_list)
+{
+       assert(list != NULL);
+       assert(list_end != NULL);
+
+       for (; packet_list != NULL; packet_list = packet_list->next) {
+               ADD_PACKET_TO_LIST((*list_end),
+                               packet_dup(packet_list->packet));
+               if (*list == NULL) {
+                       *list = *list_end;
+               }
+       }
+
+       return;
+}
+
+/**
+ *     free_packet - free the memory used by an OpenPGP packet.
+ *     @packet: The packet to free.
+ *
+ *     Takes an OpenPGP packet structure and frees the memory used by it,
+ *     including the data part.
+ */
+void free_packet(struct openpgp_packet *packet) {
+       assert(packet != NULL);
+
+       if (packet->data != NULL) {
+               free(packet->data);
+               packet->data = NULL;
+       }
+       free(packet);
+}
+
+/**
+ *     free_packet_list - free the memory used by an OpenPGP packet list.
+ *     @packet_list: The packet list to free.
+ *
+ *     Takes an OpenPGP packet list structure and frees the memory used by the
+ *     packets in it and the linked list overhead.
+ */
+void free_packet_list(struct openpgp_packet_list *packet_list) {
+       struct openpgp_packet_list *nextpacket = NULL;
+
+       assert(packet_list != NULL);
+
+       while (packet_list != NULL) {
+               nextpacket = packet_list->next;
+               if (packet_list->packet != NULL) {
+                       free_packet(packet_list->packet);
+               }
+               free(packet_list);
+               packet_list = nextpacket;
+       }
+}
+
+/**
+ *     free_signedpacket_list - free an OpenPGP signed packet list.
+ *     @signedpacket_list: The packet list to free.
+ *
+ *     Takes an OpenPGP signed packet list structure and frees the memory used
+ *      by the packets and signatures it and the linked list overhead.
+ */
+void free_signedpacket_list(
+               struct openpgp_signedpacket_list *signedpacket_list) {
+       struct openpgp_signedpacket_list *nextpacket = NULL;
+
+       assert(signedpacket_list != NULL);
+
+       while (signedpacket_list != NULL) {
+               nextpacket = signedpacket_list->next;
+               if (signedpacket_list->packet != NULL) {
+                       free_packet(signedpacket_list->packet);
+               }
+               if (signedpacket_list->sigs != NULL) {
+                       free_packet_list(signedpacket_list->sigs);
+               }
+               free(signedpacket_list);
+               signedpacket_list = nextpacket;
+       }
+}
+
+/**
+ *     free_publickey - free an OpenPGP public key structure.
+ *     @key: The key to free.
+ *
+ *     Takes an OpenPGP key and frees the memory used by all the structures it
+ *     contains.
+ */
+void free_publickey(struct openpgp_publickey *key) {
+       struct openpgp_publickey *nextkey = NULL;
+
+       assert(key != NULL);
+
+       while (key != NULL) {
+               nextkey = key->next;
+               if (key->publickey != NULL) {
+                       free_packet(key->publickey);
+                       key->publickey = NULL;
+               }
+               if (key->revocations != NULL) {
+                       free_packet_list(key->revocations);
+                       key->revocations = NULL;
+               }
+               if (key->uids != NULL) {
+                       free_signedpacket_list(key->uids);
+                       key->uids = NULL;
+               }
+               if (key->subkeys != NULL) {
+                       free_signedpacket_list(key->subkeys);
+                       key->subkeys = NULL;
+               }
+               free(key);
+               key = nextkey;
+       }
+}
diff --git a/mem.h b/mem.h
new file mode 100644 (file)
index 0000000..3358cd1
--- /dev/null
+++ b/mem.h
@@ -0,0 +1,76 @@
+/*
+ * mem.h - Routines to cleanup memory after use.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#ifndef __MEM_H_
+#define __MEM_H_
+
+#include "keystructs.h"
+
+/**
+ *     packet_dup - duplicate an OpenPGP packet.
+ *     @packet: The packet to duplicate.
+ *
+ *     This function takes an OpenPGP packet structure and duplicates it,
+ *     including the data part. It returns NULL if there is a problem
+ *     allocating memory for the duplicate.
+ */
+struct openpgp_packet *packet_dup(struct openpgp_packet *packet);
+
+/**
+ *     packet_list_add - Adds an OpenPGP packet list to another.
+ *     @list: The packet list to add to.
+ *     @list_end: The end of the packet list to add to.
+ *     @packet_list: The packet list to add.
+ *
+ *     This function takes an OpenPGP packet list and adds it to another list,
+ *     duplicating it in the process. The list to add to need not exists to
+ *     begin with, in which case the function simply duplicates the supplied
+ *     list.
+ */
+void packet_list_add(struct openpgp_packet_list **list,
+               struct openpgp_packet_list **list_end,
+               struct openpgp_packet_list *packet_list);
+
+/**
+ *     free_packet - free the memory used by an OpenPGP packet.
+ *     @packet: The packet to free.
+ *
+ *     Takes an OpenPGP packet structure and frees the memory used by it,
+ *     including the data part.
+ */
+void free_packet(struct openpgp_packet *packet);
+
+/**
+ *     free_packet_list - free the memory used by an OpenPGP packet list.
+ *     @packet_list: The packet list to free.
+ *
+ *     Takes an OpenPGP packet list structure and frees the memory used by the
+ *     packets in it and the linked list overhead.
+ */
+void free_packet_list(struct openpgp_packet_list *packet_list);
+
+/**
+ *     free_signedpacket_list - free an OpenPGP signed packet list.
+ *     @signedpacket_list: The packet list to free.
+ *
+ *     Takes an OpenPGP signed packet list structure and frees the memory used
+ *      by the packets and signatures it and the linked list overhead.
+ */
+void free_signedpacket_list(
+               struct openpgp_signedpacket_list *signedpacket_list);
+
+/**
+ *     free_publickey - free an OpenPGP public key structure.
+ *     @key: The key to free.
+ *
+ *     Takes an OpenPGP key and frees the memory used by all the structures it
+ *     contains.
+ */
+void free_publickey(struct openpgp_publickey *key);
+
+#endif /* __MEM_H_ */
diff --git a/merge.c b/merge.c
new file mode 100644 (file)
index 0000000..c1410e1
--- /dev/null
+++ b/merge.c
@@ -0,0 +1,298 @@
+/*
+ * merge.c - Routines to merge OpenPGP public keys.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "keyid.h"
+#include "keystructs.h"
+#include "ll.h"
+#include "mem.h"
+#include "merge.h"
+
+/**
+ *     compare_packets - Check to see if 2 OpenPGP packets are the same.
+ *     @a: The first key to compare.
+ *     @b: The second key to compare.
+ *
+ *     Takes 2 keys and returns true if they are the same and false otherwise.
+ */
+bool compare_packets(struct openpgp_packet *a, struct openpgp_packet *b)
+{
+       return (a->tag == b->tag && a->length == b->length &&
+               !memcmp(a->data, b->data, b->length));
+}
+
+/**
+ *     find_packet - Checks to see if an OpenPGP packet exists in a list.
+ *     @packet_list: The list of packets to look in.
+ *     @packet: The packet to look for.
+ *
+ *     Walks through the packet_list checking to see if the packet given is
+ *     present in it. Returns true if it is.
+ */
+bool find_packet(struct openpgp_packet_list *packet_list,
+                       struct openpgp_packet *packet)
+{
+       bool found = false;
+
+       while (!found && packet_list != NULL) {
+               if (compare_packets(packet_list->packet, packet)) {
+                       found = true;
+               }
+               packet_list = packet_list -> next;
+       }
+
+       return found;
+}
+
+/**
+ *     get_signed_packet - Gets a signed packet from a list.
+ *     @packet_list: The list of packets to look in.
+ *     @packet: The packet to look for.
+ *
+ *     Walks through the signedpacket_list looking for the supplied packet and
+ *     returns it if found. Otherwise returns NULL.
+ */
+struct openpgp_signedpacket_list *find_signed_packet(
+               struct openpgp_signedpacket_list *packet_list,
+               struct openpgp_packet *packet)
+{
+       struct openpgp_signedpacket_list *found = NULL;
+
+       while (found == NULL && packet_list != NULL) {
+               if (compare_packets(packet_list->packet, packet)) {
+                       found = packet_list;
+               }
+               packet_list = packet_list -> next;
+       }
+
+       return found;
+}
+
+/**
+ *     remove_signed_packet - Removes a signed packet from a list.
+ *     @packet_list: The list of packets to look in.
+ *     @packet: The packet to remove.
+ *
+ *     Walks through the signedpacket_list looking for the supplied packet and
+ *     removes it if found. Assumes the packet can only exist a maximum of
+ *     once in the list.
+ */
+bool remove_signed_packet(struct openpgp_signedpacket_list **packet_list,
+               struct openpgp_signedpacket_list **list_end,
+               struct openpgp_packet *packet)
+{
+       struct openpgp_signedpacket_list *cur = NULL;
+       struct openpgp_signedpacket_list *prev = NULL;
+       bool found = false;
+
+       for (cur = *packet_list; !found && (cur != NULL); cur = cur->next) {
+               if (compare_packets(cur->packet, packet)) {
+                       found = true;
+                       if (prev == NULL) {
+                               *packet_list = cur->next;
+                       } else {
+                               prev->next = cur->next;
+                       }
+                       if (cur->next == NULL) {
+                               *list_end = prev;
+                       }
+               }
+       }
+
+       return found;
+}
+
+/**
+ *     merge_packet_sigs - Takes 2 signed packets and merges their sigs.
+ *     @old: The old signed packet.
+ *     @new: The new signed packet.
+ *
+ *     Takes 2 signed packet list structures and the sigs of the packets on
+ *     the head of these structures. These packets must both be the same and
+ *     the fully merged structure is returned in old and the minimal
+ *     difference to get from old to new in new.
+ */
+int merge_packet_sigs(struct openpgp_signedpacket_list *old,
+                       struct openpgp_signedpacket_list *new)
+{
+       struct openpgp_packet_list      *lastpacket = NULL;
+       struct openpgp_packet_list      *curpacket = NULL;
+       struct openpgp_packet_list      *nextpacket = NULL;
+
+       assert(compare_packets(old->packet, new->packet));
+
+       curpacket = new->sigs;
+       while (curpacket != NULL) {
+               nextpacket = curpacket->next;
+               if (find_packet(old->sigs, curpacket->packet)) {
+                       /*
+                        * We already have this sig, remove it from the
+                        * difference list and free the memory allocated for
+                        * it.
+                        */
+                       if (lastpacket != NULL) {
+                               lastpacket->next = curpacket->next;
+                       } else {
+                               assert(curpacket == new->sigs);
+                               new->sigs = curpacket->next;
+                       }
+                       curpacket->next = NULL;
+                       free_packet_list(curpacket);
+               } else {
+                       lastpacket = curpacket;
+               }
+               curpacket = nextpacket;
+       }
+       new->last_sig = lastpacket;
+
+       /*
+        * What's left on new->sigs now are the new signatures, so add them to
+        * old->sigs.
+        */
+       packet_list_add(&old->sigs, &old->last_sig, new->sigs);
+
+       return 0;
+}
+
+/**
+ *     merge_signed_packets - Takes 2 lists of signed packets and merges them.
+ *     @old: The old signed packet list.
+ *     @new: The new signed packet list.
+ *
+ *     Takes 2 lists of signed packets and merges them. The complete list of
+ *     signed packets & sigs is returned in old and the minimal set of
+ *     differences required to get from old to new in new.
+ */
+int merge_signed_packets(struct openpgp_signedpacket_list **old,
+                       struct openpgp_signedpacket_list **old_end,
+                       struct openpgp_signedpacket_list **new,
+                       struct openpgp_signedpacket_list **new_end)
+{
+       struct openpgp_signedpacket_list *curelem = NULL;
+       struct openpgp_signedpacket_list *newelem = NULL;
+
+       for (curelem = *old; curelem != NULL; curelem = curelem->next) {
+               newelem = find_signed_packet(*new, curelem->packet);
+               if (newelem != NULL) {
+                       merge_packet_sigs(curelem, newelem);
+                       
+                       /*
+                        * If there are no sigs left on the new signed packet
+                        * then remove it from the list.
+                        */
+                       if (newelem->sigs == NULL) {
+                               remove_signed_packet(new,
+                                               new_end,
+                                               curelem->packet);
+                       }
+               }
+       }
+
+       /*
+        * If *new != NULL now then there are UIDs on the new key that weren't
+        * on the old key. Add them.
+        */
+       for (curelem = *new; curelem != NULL;
+                       curelem = curelem->next) {
+               ADD_PACKET_TO_LIST((*old_end),
+                               packet_dup(curelem->packet));
+               if (*old == NULL) {
+                       *old = *old_end;
+               }
+               packet_list_add(&(*old_end)->sigs,
+                               &(*old_end)->last_sig,
+                               curelem->sigs);
+       }
+
+       return 0;
+}
+
+/**
+ *     merge_keys - Takes 2 public keys and merges them.
+ *     @a: The old key. The merged key is returned in this structure.
+ *     @b: The new key. The changed from old to new keys are returned in this
+ *             structure.
+ *
+ *     This function takes 2 keys and merges them. It then returns the merged
+ *     key in a and the difference between this new key and the original a
+ *     in b (ie newb contains the minimum amount of detail necessary to
+ *     convert olda to newa). The intention is that olda is provided from
+ *     internal storage and oldb from the remote user. newa is then stored in
+ *     internal storage and newb is sent to all our keysync peers.
+ */
+int merge_keys(struct openpgp_publickey *a, struct openpgp_publickey *b)
+{
+       int rc = 0; /* Return code */
+       struct openpgp_packet_list      *curpacket = NULL; 
+       struct openpgp_packet_list      *lastpacket = NULL;
+       struct openpgp_packet_list      *nextpacket = NULL;
+
+       if (a == NULL || b == NULL) {
+               /*
+                * Do nothing.
+                */
+               rc = 1;
+       } else if (get_keyid(a) != get_keyid(b)) {
+               /*
+                * Key IDs are different.
+                */
+               rc = -1;
+       } else {
+               /*
+                * Key IDs are the same, so I guess we have to merge them.
+                */
+               curpacket = b->revocations;
+               while (curpacket != NULL) {
+                       nextpacket = curpacket->next;
+                       if (find_packet(a->revocations, curpacket->packet)) {
+                               /*
+                                * We already have this revocation, remove it
+                                * from the difference list and free the memory
+                                * allocated for it.
+                                */
+
+                               if (lastpacket != NULL) {
+                                       lastpacket->next = curpacket->next;
+                               } else {
+                                       assert(curpacket == b->revocations);
+                                       b->revocations = curpacket->next;
+                               }
+                               curpacket->next = NULL;
+                               free_packet_list(curpacket);
+
+                       } else {
+                               lastpacket = curpacket;
+                       }
+                       curpacket = nextpacket;
+               }
+               b->last_revocation = lastpacket;
+
+               /*
+                * Anything left on b->revocations doesn't exist on
+                * a->revocations, so add them to the list.
+                */
+               packet_list_add(&a->revocations,
+                               &a->last_revocation,
+                               b->revocations);
+
+               /*
+                * Merge uids (signed list).
+                * Merge subkeys (signed list).
+                */
+               merge_signed_packets(&a->uids, &a->last_uid, 
+                               &b->uids, &b->last_uid);
+               merge_signed_packets(&a->subkeys, &a->last_uid,
+                               &b->subkeys, &b->last_subkey);
+
+       }
+
+       return rc;
+}
diff --git a/merge.h b/merge.h
new file mode 100644 (file)
index 0000000..5abbeca
--- /dev/null
+++ b/merge.h
@@ -0,0 +1,28 @@
+/*
+ * merge.h - Routines to merge OpenPGP public keys.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#ifndef __MERGE_H__
+
+#include "keystructs.h"
+
+/**
+ *     merge_keys - Takes 2 public keys and merges them.
+ *     @a: The old key. The merged key is returned in this structure.
+ *     @b: The new key. The changed from old to new keys are returned in this
+ *             structure.
+ *
+ *     This function takes 2 keys and merges them. It then returns the merged
+ *     key in a and the difference between this new key and the original a
+ *     in b (ie newb contains the minimum amount of detail necessary to
+ *     convert olda to newa). The intention is that olda is provided from
+ *     internal storage and oldb from the remote user. newa is then stored in
+ *     internal storage and newb is sent to all our keysync peers.
+ */
+int merge_keys(struct openpgp_publickey *a, struct openpgp_publickey *b);
+
+#endif
diff --git a/onak.sql b/onak.sql
new file mode 100644 (file)
index 0000000..6abb029
--- /dev/null
+++ b/onak.sql
@@ -0,0 +1,18 @@
+DROP TABLE onak_keys;
+DROP TABLE onak_uids;
+DROP TABLE onak_sigs;
+
+CREATE TABLE onak_keys (
+       keyid   char(8) NOT NULL,
+       keydata oid NOT NULL
+);
+
+CREATE TABLE onak_uids (
+       keyid   char(8) NOT NULL,
+       uid     varchar(6000) NOT NULL
+);
+
+CREATE TABLE onak_sigs (
+       signer  char(8) NOT NULL,
+       signee  char(8) NOT NULL
+);
diff --git a/parsekey.c b/parsekey.c
new file mode 100644 (file)
index 0000000..df77493
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * parsekey.c - Routines to parse an OpenPGP key.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "keyid.h"
+#include "keystructs.h"
+#include "ll.h"
+#include "mem.h"
+#include "parsekey.h"
+
+/**
+ *     add_key - Takes a key and adds it to the keyserver.
+ *     @key: The public key to add.
+ *
+ *     This function takes a public key and adds it to the keyserver.
+ *     It first of all sees if we already have the key locally. If we do then
+ *     we retrieve it and merge the two keys. We then store the resulting key
+ *     (or just the original we received if we don't already have it). We then
+ *     send out the appropriate updates to our keyserver peers.
+ */
+int add_key(struct openpgp_publickey *key) {
+       return 0;
+}
+
+/**
+ *     parse_keys - Process a stream of packets for public keys + sigs.
+ *     @packets: The packet list to parse.
+ *     @keys: The returned list of public keys.
+ *
+ *     This function takes an list of OpenPGP packets and attempts to parse it
+ *     into a list of public keys with signatures and subkeys.
+ */
+int parse_keys(struct openpgp_packet_list *packets,
+               struct openpgp_publickey **keys)
+{
+       struct openpgp_publickey *curkey = NULL;
+
+       while (packets != NULL) {
+               switch (packets->packet->tag) {
+               case 2:
+                       /*
+                        * It's a signature packet. Add it to either the public
+                        * key (it should be a revocation), to the current UID
+                        * or the current subkey.
+                        */
+                       assert(curkey != NULL);
+                       if (curkey->subkeys != NULL) {
+                               ADD_PACKET_TO_LIST_END(curkey->last_subkey,
+                                       sig,
+                                       packet_dup(packets->packet));
+                       } else if (curkey->uids != NULL) {
+                               ADD_PACKET_TO_LIST_END(curkey->last_uid,
+                                       sig,
+                                       packet_dup(packets->packet));
+                       } else {
+                               ADD_PACKET_TO_LIST_END(curkey,
+                                       revocation,
+                                       packet_dup(packets->packet));
+                       }
+                       break;
+               case 6:
+                       /*
+                        * It's a public key packet, so start a new key in our
+                        * list.
+                        */
+                       if (curkey != NULL) {
+                               curkey->next = malloc(sizeof (*curkey));
+                               curkey = curkey->next;
+                       } else {
+                               *keys = curkey =
+                                       malloc(sizeof (*curkey));
+                       }
+                       memset(curkey, 0, sizeof(*curkey));
+                       curkey->publickey = packet_dup(packets->packet);
+                       break;
+               case 13:
+               case 17:
+                       /*
+                        * It's a UID packet (or a photo id, which is similar).
+                        */
+                       assert(curkey != NULL);
+                       assert(curkey->subkeys == NULL);
+                       ADD_PACKET_TO_LIST_END(curkey,
+                               uid,
+                               packet_dup(packets->packet));
+                       break;
+               case 14:
+                       /*
+                        * It's a subkey packet.
+                        */
+                       assert(curkey != NULL);
+                       ADD_PACKET_TO_LIST_END(curkey,
+                               subkey,
+                               packet_dup(packets->packet));
+                       break;
+               default:
+                       printf("Unsupported packet type: %d\n",
+                                       packets->packet->tag);
+               }
+               packets = packets->next;
+       }
+
+       return 0;
+}
+
+/**
+ *     debug_packet - Print debug info about a packet
+ *     @packet: The packet to display.
+ *
+ *     This function takes an OpenPGP packet and displays some information
+ *     about it to stdout. Useful for debugging purposes or curiousity about
+ *     an OpenPGP packet stream.
+ */
+int debug_packet(struct openpgp_packet *packet)
+{
+       printf("\tNew format: %d, Tag: %d, Length: %d\n",
+                       packet->newformat,
+                       packet->tag,
+                       packet->length);
+
+       return 0;
+}
+
+/**
+ *     read_openpgp_stream - Reads a stream of OpenPGP packets.
+ *     @getchar_func: The function to get the next character from the stream.
+ *     @ctx: A pointer to the context structure for getchar_func.
+ *     @packets: The outputted list of packets.
+ *
+ *     This function uses getchar_func to read characters from an OpenPGP
+ *     packet stream and reads the packets into a linked list of packets
+ *     ready for parsing as a public key or whatever.
+ */
+int read_openpgp_stream(int (*getchar_func)(void *ctx, size_t count,
+                               unsigned char *c),
+                               void *ctx,
+                               struct openpgp_packet_list **packets)
+{
+       unsigned char                    curchar = 0;
+       unsigned long                    count = 0;
+       struct openpgp_packet_list      *curpacket = NULL;
+       int                              rc = 0;
+       bool                             inpacket = false;
+
+       assert(packets != NULL);
+
+       while (!rc && !getchar_func(ctx, 1, &curchar)) {
+               if (!inpacket && (curchar & 0x80)) {
+                       /*
+                        * New packet. Record the fact we're in a packet and
+                        * allocate memory for it.
+                        */
+                       inpacket = true;
+                       count = 0;
+                       if (curpacket != NULL) {
+                               curpacket->next = malloc(sizeof (*curpacket));
+                               curpacket = curpacket->next;
+                       } else {
+                               *packets = curpacket =
+                                       malloc(sizeof (*curpacket));
+                       }
+                       memset(curpacket, 0, sizeof(*curpacket));
+                       curpacket->packet =
+                               malloc(sizeof (*curpacket->packet));
+                       memset(curpacket->packet, 0,
+                                       sizeof(*curpacket->packet));
+
+                       curpacket->packet->newformat = (curchar & 0x40);
+
+                       // TODO: Better error checking on getchar_func.
+                       if (curpacket->packet->newformat) {
+                               curpacket->packet->tag = (curchar & 0x3F);
+                               rc = getchar_func(ctx, 1, &curchar);
+                               curpacket->packet->length = curchar;
+                               if (curpacket->packet->length > 191 &&
+                                       curpacket->packet->length < 224) {
+                                       rc = getchar_func(ctx, 1, &curchar);
+                                       curpacket->packet->length -= 192;
+                                       curpacket->packet->length <<= 8;
+                                       curpacket->packet->length += curchar;
+                                       curpacket->packet->length += 192;
+                               } else if (curpacket->packet->length > 223 &&
+                                       curpacket->packet->length < 255) {
+                                       printf("Partial length; not supported.\n");
+                               } else {
+                                       /*
+                                        * 5 byte length; ie 255 followed by 3
+                                        * bytes of MSB length.
+                                        */
+                                       rc = getchar_func(ctx, 1, &curchar);
+                                       curpacket->packet->length = curchar;
+                                       curpacket->packet->length <<= 8;
+                                       rc = getchar_func(ctx, 1, &curchar);
+                                       curpacket->packet->length = curchar;
+                                       curpacket->packet->length <<= 8;
+                                       rc = getchar_func(ctx, 1, &curchar);
+                                       curpacket->packet->length = curchar;
+                                       curpacket->packet->length <<= 8;
+                                       rc = getchar_func(ctx, 1, &curchar);
+                                       curpacket->packet->length = curchar;
+                               }
+
+                       } else {
+                               curpacket->packet->tag = (curchar & 0x3C) >> 2;
+                               switch (curchar & 3) {
+                               case 0:
+                                       rc = getchar_func(ctx, 1, &curchar);
+                                       curpacket->packet->length = curchar;
+                                       break;
+                               case 1:
+                                       rc = getchar_func(ctx, 1, &curchar);
+                                       curpacket->packet->length = curchar;
+                                       curpacket->packet->length <<= 8;
+                                       rc = getchar_func(ctx, 1, &curchar);
+                                       curpacket->packet->length += curchar;
+                                       break;
+                               case 2:
+                                       printf("Unsupported length type 2.\n");
+                                       break;
+                               case 3:
+                                       printf("Unsupported length type 3.\n");
+                                       break;
+                               }
+                       }
+                       curpacket->packet->data =
+                               malloc(curpacket->packet->length *
+                                       sizeof(unsigned char));
+                       rc = getchar_func(ctx, curpacket->packet->length,
+                                       curpacket->packet->data);
+                       inpacket = false;
+               } else {
+                       fprintf(stderr, "Unexpected character: 0x%X\n",
+                               curchar);
+               }
+       }
+
+       return (rc);
+}
+
+/**
+ *     write_openpgp_stream - Reads a stream of OpenPGP packets.
+ *     @putchar_func: The function to put the next character to the stream.
+ *     @ctx: A pointer to the context structure for putchar_func.
+ *     @packets: The list of packets.
+ *
+ *     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),
+                               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);
+
+                       if (packets->packet->length < 192) {
+                               putchar_func(ctx, packets->packet->length);
+                       } 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);
+
+                       } 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);
+                       } 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);
+                       } 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);
+                       }
+               }
+
+               for (i = 0; i < packets->packet->length; i++) {
+                       putchar_func(ctx, packets->packet->data[i]);
+               }
+               packets = packets->next;
+       }
+       return 0;
+}
+
+/**
+ *     flatten_publickey - Convert a publickey to an OpenPGP packet list.
+ *     @key: The public key.
+ *     @packets: The outputted packet list.
+ *
+ *     This function converts public key structure to a linked list of OpenPGP
+ *     packets ready for outputing or storage.
+ */
+int flatten_publickey(struct openpgp_publickey *key,
+                       struct openpgp_packet_list **packets,
+                       struct openpgp_packet_list **list_end)
+{
+       struct openpgp_signedpacket_list        *tmpsignedlist = NULL;
+       struct openpgp_packet_list              *tmplist = NULL;
+
+       while (key != NULL) {
+               /*
+                * First write the public key packet out.
+                */
+               ADD_PACKET_TO_LIST((*list_end), packet_dup(key->publickey));
+               if (*packets == NULL) {
+                       *packets = *list_end;
+               }
+
+               /*
+                * Now do any revocation signatures on the main key.
+                */
+               for (tmplist = key->revocations; tmplist != NULL;
+                               tmplist = tmplist->next) {
+                       ADD_PACKET_TO_LIST((*list_end),
+                                       packet_dup(tmplist->packet));
+               }
+
+               /*
+                * Output any UIDs along with their signatures.
+                */
+               for (tmpsignedlist = key->uids; tmpsignedlist != NULL;
+                               tmpsignedlist = tmpsignedlist->next) {
+
+                       ADD_PACKET_TO_LIST((*list_end),
+                               packet_dup(tmpsignedlist->packet));
+                       for (tmplist = tmpsignedlist->sigs; tmplist != NULL;
+                                       tmplist = tmplist->next) {
+                               ADD_PACKET_TO_LIST((*list_end), 
+                                       packet_dup(tmplist->packet));
+                       }
+               }
+
+               /*
+                * Output any subkeys along with their signatures.
+                */
+               for (tmpsignedlist = key->subkeys; tmpsignedlist != NULL;
+                               tmpsignedlist = tmpsignedlist->next) {
+
+                       ADD_PACKET_TO_LIST((*list_end),
+                               packet_dup(tmpsignedlist->packet));
+                       for (tmplist = tmpsignedlist->sigs; tmplist != NULL;
+                                       tmplist = tmplist->next) {
+                               ADD_PACKET_TO_LIST((*list_end), 
+                                       packet_dup(tmplist->packet));
+                       }
+               }
+               key = key->next;
+       }
+       return 0;
+}
diff --git a/parsekey.h b/parsekey.h
new file mode 100644 (file)
index 0000000..113f14a
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * parsekey.h - Routines to parse an OpenPGP key.
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+#ifndef __PARSEKEY_H__
+#define __PARSEKEY_H__
+
+#include "keystructs.h"
+
+/**
+ *     add_key - Takes a key and adds it to the keyserver.
+ *     @key: The public key to add.
+ *
+ *     This function takes a public key and adds it to the keyserver.
+ *     It first of all sees if we already have the key locally. If we do then
+ *     we retrieve it and merge the two keys. We then store the resulting key
+ *     (or just the original we received if we don't already have it). We then
+ *     send out the appropriate updates to our keyserver peers.
+ */
+int add_key(struct openpgp_publickey *key);
+
+/**
+ *     parse_keys - Process a stream of packets for public keys + sigs.
+ *     @packets: The packet list to parse.
+ *     @keys: The returned list of public keys.
+ *
+ *     This function takes an list of OpenPGP packets and attempts to parse it
+ *     into a list of public keys with signatures and subkeys.
+ */
+int parse_keys(struct openpgp_packet_list *packets,
+               struct openpgp_publickey **keys);
+
+/**
+ *     debug_packet - Print debug info about a packet
+ *     @packet: The packet to display.
+ *
+ *     This function takes an OpenPGP packet and displays some information
+ *     about it to stdout. Useful for debugging purposes or curiousity about
+ *     an OpenPGP packet stream.
+ */
+int debug_packet(struct openpgp_packet *packet);
+
+/**
+ *     read_openpgp_stream - Reads a stream of OpenPGP packets.
+ *     @getchar_func: The function to get the next character from the stream.
+ *     @ctx: A pointer to the context structure for getchar_func.
+ *     @packets: The outputted list of packets.
+ *
+ *     This function uses getchar_func to read characters from an OpenPGP
+ *     packet stream and reads the packets into a linked list of packets
+ *     ready for parsing as a public key or whatever.
+ */
+int read_openpgp_stream(int (*getchar_func)(void *ctx, size_t count,
+                               unsigned char *c),
+                               void *ctx,
+                               struct openpgp_packet_list **packets);
+
+/**
+ *     write_openpgp_stream - Reads a stream of OpenPGP packets.
+ *     @putchar_func: The function to put the next character to the stream.
+ *     @ctx: A pointer to the context structure for putchar_func.
+ *     @packets: The list of packets.
+ *
+ *     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),
+                               void *ctx,
+                               struct openpgp_packet_list *packets);
+
+/**
+ *     flatten_publickey - Convert a publickey to an OpenPGP packet list.
+ *     @key: The public key.
+ *     @packets: The outputted packet list.
+ *     @list_end: The end of the packet list.
+ *
+ *     This function converts public key structure to a linked list of OpenPGP
+ *     packets ready for outputing or storage. If we're not appending to an
+ *     existing list then both packets & list_end will be pointers to NULLs,
+ *     other wise packets should point to the start of the list and list_end
+ *     to the end so we can append to the end.
+ */
+int flatten_publickey(struct openpgp_publickey *key,
+                       struct openpgp_packet_list **packets,
+                       struct openpgp_packet_list **list_end);
+
+#endif /* __PARSEKEY_H__ */
diff --git a/pathtest.c b/pathtest.c
new file mode 100644 (file)
index 0000000..50dcdcf
--- /dev/null
@@ -0,0 +1,65 @@
+//#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/sha.c b/sha.c
new file mode 100644 (file)
index 0000000..cee12ab
--- /dev/null
+++ b/sha.c
@@ -0,0 +1,292 @@
+/* sha1.c - SHA1 hash function
+ *     Copyright (C) 2001 V. Alex Brennen
+ *
+ * Please see below for more legal information!
+ *
+ * This file is part of the CryptNET openPGP Public Keyserver (CKS).
+ *
+ * CKS is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * CKS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+/* This file was copied from GnuPG                               */
+/* Some portions Copyright (C) 1998 The Free Software Foundation */
+
+#include "sha.h"
+
+
+/*  Test vectors:
+ *
+ *  "abc"
+ *  A999 3E36 4706 816A BA3E  2571 7850 C26C 9CD0 D89D
+ *
+ *  "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ *  8498 3E44 1C3B D26E BAAE  4AA1 F951 29E5 E546 70F1
+ */
+
+void sha1_init( SHA1_CONTEXT *hd )
+{
+    hd->h0 = 0x67452301;
+    hd->h1 = 0xefcdab89;
+    hd->h2 = 0x98badcfe;
+    hd->h3 = 0x10325476;
+    hd->h4 = 0xc3d2e1f0;
+    hd->nblocks = 0;
+    hd->count = 0;
+}
+
+
+/****************
+ * Transform the message X which consists of 16 32-bit-words
+ */
+static void transform( SHA1_CONTEXT *hd, uint8_t *data )
+{
+    uint32_t a,b,c,d,e,tm;
+    uint32_t x[16];
+
+    /* get values from the chaining vars */
+    a = hd->h0;
+    b = hd->h1;
+    c = hd->h2;
+    d = hd->h3;
+    e = hd->h4;
+
+  #ifdef BIG_ENDIAN_HOST
+    memcpy( x, data, 64 );
+  #else
+    { int i;
+      uint8_t *p2;
+      for(i=0, p2=(unsigned char*)x; i < 16; i++, p2 += 4 ) {
+       p2[3] = *data++;
+       p2[2] = *data++;
+       p2[1] = *data++;
+       p2[0] = *data++;
+      }
+    }
+  #endif
+
+
+#define K1  0x5A827999L
+#define K2  0x6ED9EBA1L
+#define K3  0x8F1BBCDCL
+#define K4  0xCA62C1D6L
+#define F1(x,y,z)   ( z ^ ( x & ( y ^ z ) ) )
+#define F2(x,y,z)   ( x ^ y ^ z )
+#define F3(x,y,z)   ( ( x & y ) | ( z & ( x | y ) ) )
+#define F4(x,y,z)   ( x ^ y ^ z )
+
+
+#define M(i) ( tm =   x[i&0x0f] ^ x[(i-14)&0x0f] \
+                   ^ x[(i-8)&0x0f] ^ x[(i-3)&0x0f] \
+              , (x[i&0x0f] = rol(tm,1)) )
+
+#define R(a,b,c,d,e,f,k,m)  do { e += rol( a, 5 )     \
+                                     + f( b, c, d )  \
+                                     + k             \
+                                     + m;            \
+                                b = rol( b, 30 );    \
+                              } while(0)
+    R( a, b, c, d, e, F1, K1, x[ 0] );
+    R( e, a, b, c, d, F1, K1, x[ 1] );
+    R( d, e, a, b, c, F1, K1, x[ 2] );
+    R( c, d, e, a, b, F1, K1, x[ 3] );
+    R( b, c, d, e, a, F1, K1, x[ 4] );
+    R( a, b, c, d, e, F1, K1, x[ 5] );
+    R( e, a, b, c, d, F1, K1, x[ 6] );
+    R( d, e, a, b, c, F1, K1, x[ 7] );
+    R( c, d, e, a, b, F1, K1, x[ 8] );
+    R( b, c, d, e, a, F1, K1, x[ 9] );
+    R( a, b, c, d, e, F1, K1, x[10] );
+    R( e, a, b, c, d, F1, K1, x[11] );
+    R( d, e, a, b, c, F1, K1, x[12] );
+    R( c, d, e, a, b, F1, K1, x[13] );
+    R( b, c, d, e, a, F1, K1, x[14] );
+    R( a, b, c, d, e, F1, K1, x[15] );
+    R( e, a, b, c, d, F1, K1, M(16) );
+    R( d, e, a, b, c, F1, K1, M(17) );
+    R( c, d, e, a, b, F1, K1, M(18) );
+    R( b, c, d, e, a, F1, K1, M(19) );
+    R( a, b, c, d, e, F2, K2, M(20) );
+    R( e, a, b, c, d, F2, K2, M(21) );
+    R( d, e, a, b, c, F2, K2, M(22) );
+    R( c, d, e, a, b, F2, K2, M(23) );
+    R( b, c, d, e, a, F2, K2, M(24) );
+    R( a, b, c, d, e, F2, K2, M(25) );
+    R( e, a, b, c, d, F2, K2, M(26) );
+    R( d, e, a, b, c, F2, K2, M(27) );
+    R( c, d, e, a, b, F2, K2, M(28) );
+    R( b, c, d, e, a, F2, K2, M(29) );
+    R( a, b, c, d, e, F2, K2, M(30) );
+    R( e, a, b, c, d, F2, K2, M(31) );
+    R( d, e, a, b, c, F2, K2, M(32) );
+    R( c, d, e, a, b, F2, K2, M(33) );
+    R( b, c, d, e, a, F2, K2, M(34) );
+    R( a, b, c, d, e, F2, K2, M(35) );
+    R( e, a, b, c, d, F2, K2, M(36) );
+    R( d, e, a, b, c, F2, K2, M(37) );
+    R( c, d, e, a, b, F2, K2, M(38) );
+    R( b, c, d, e, a, F2, K2, M(39) );
+    R( a, b, c, d, e, F3, K3, M(40) );
+    R( e, a, b, c, d, F3, K3, M(41) );
+    R( d, e, a, b, c, F3, K3, M(42) );
+    R( c, d, e, a, b, F3, K3, M(43) );
+    R( b, c, d, e, a, F3, K3, M(44) );
+    R( a, b, c, d, e, F3, K3, M(45) );
+    R( e, a, b, c, d, F3, K3, M(46) );
+    R( d, e, a, b, c, F3, K3, M(47) );
+    R( c, d, e, a, b, F3, K3, M(48) );
+    R( b, c, d, e, a, F3, K3, M(49) );
+    R( a, b, c, d, e, F3, K3, M(50) );
+    R( e, a, b, c, d, F3, K3, M(51) );
+    R( d, e, a, b, c, F3, K3, M(52) );
+    R( c, d, e, a, b, F3, K3, M(53) );
+    R( b, c, d, e, a, F3, K3, M(54) );
+    R( a, b, c, d, e, F3, K3, M(55) );
+    R( e, a, b, c, d, F3, K3, M(56) );
+    R( d, e, a, b, c, F3, K3, M(57) );
+    R( c, d, e, a, b, F3, K3, M(58) );
+    R( b, c, d, e, a, F3, K3, M(59) );
+    R( a, b, c, d, e, F4, K4, M(60) );
+    R( e, a, b, c, d, F4, K4, M(61) );
+    R( d, e, a, b, c, F4, K4, M(62) );
+    R( c, d, e, a, b, F4, K4, M(63) );
+    R( b, c, d, e, a, F4, K4, M(64) );
+    R( a, b, c, d, e, F4, K4, M(65) );
+    R( e, a, b, c, d, F4, K4, M(66) );
+    R( d, e, a, b, c, F4, K4, M(67) );
+    R( c, d, e, a, b, F4, K4, M(68) );
+    R( b, c, d, e, a, F4, K4, M(69) );
+    R( a, b, c, d, e, F4, K4, M(70) );
+    R( e, a, b, c, d, F4, K4, M(71) );
+    R( d, e, a, b, c, F4, K4, M(72) );
+    R( c, d, e, a, b, F4, K4, M(73) );
+    R( b, c, d, e, a, F4, K4, M(74) );
+    R( a, b, c, d, e, F4, K4, M(75) );
+    R( e, a, b, c, d, F4, K4, M(76) );
+    R( d, e, a, b, c, F4, K4, M(77) );
+    R( c, d, e, a, b, F4, K4, M(78) );
+    R( b, c, d, e, a, F4, K4, M(79) );
+
+    /* update chainig vars */
+    hd->h0 += a;
+    hd->h1 += b;
+    hd->h2 += c;
+    hd->h3 += d;
+    hd->h4 += e;
+}
+
+
+/* Update the message digest with the contents
+ * of INBUF with length INLEN.
+ */
+void sha1_write( SHA1_CONTEXT *hd, unsigned char *inbuf, size_t inlen)
+{
+
+    if( hd->count == 64 ) { /* flush the buffer */
+       transform( hd, hd->buf );
+       hd->count = 0;
+       hd->nblocks++;
+    }
+    if( !inbuf )
+       return;
+    if( hd->count ) {
+       for( ; inlen && hd->count < 64; inlen-- )
+           hd->buf[hd->count++] = *inbuf++;
+       sha1_write( hd, NULL, 0 );
+       if( !inlen )
+           return;
+    }
+
+    while( inlen >= 64 ) {
+       transform( hd, inbuf );
+       hd->count = 0;
+       hd->nblocks++;
+       inlen -= 64;
+       inbuf += 64;
+    }
+    for( ; inlen && hd->count < 64; inlen-- )
+       hd->buf[hd->count++] = *inbuf++;
+}
+
+
+/* The routine final terminates the computation and
+ * returns the digest.
+ * The handle is prepared for a new cycle, but adding uint8_ts to the
+ * handle will the destroy the returned buffer.
+ * Returns: 20 uint8_ts representing the digest.
+ */
+
+void sha1_final(SHA1_CONTEXT *hd)
+{
+    unsigned int t, msb, lsb;
+    unsigned char *p;
+
+    sha1_write(hd, NULL, 0); /* flush */;
+
+    msb = 0;
+    t = hd->nblocks;
+    if( (lsb = t << 6) < t ) /* multiply by 64 to make a uint8_t count */
+       msb++;
+    msb += t >> 26;
+    t = lsb;
+    if( (lsb = t + hd->count) < t ) /* add the count */
+       msb++;
+    t = lsb;
+    if( (lsb = t << 3) < t ) /* multiply by 8 to make a bit count */
+       msb++;
+    msb += t >> 29;
+
+    if( hd->count < 56 ) { /* enough room */
+       hd->buf[hd->count++] = 0x80; /* pad */
+       while( hd->count < 56 )
+           hd->buf[hd->count++] = 0;  /* pad */
+    }
+    else { /* need one extra block */
+       hd->buf[hd->count++] = 0x80; /* pad character */
+       while( hd->count < 64 )
+           hd->buf[hd->count++] = 0;
+       sha1_write(hd, NULL, 0);  /* flush */;
+       memset(hd->buf, 0, 56 ); /* fill next block with zeroes */
+    }
+    /* append the 64 bit count */
+    hd->buf[56] = msb >> 24;
+    hd->buf[57] = msb >> 16;
+    hd->buf[58] = msb >>  8;
+    hd->buf[59] = msb     ;
+    hd->buf[60] = lsb >> 24;
+    hd->buf[61] = lsb >> 16;
+    hd->buf[62] = lsb >>  8;
+    hd->buf[63] = lsb     ;
+    transform( hd, hd->buf );
+
+    p = hd->buf;
+  #ifdef BIG_ENDIAN_HOST
+    #define X(a) do { *(uint32_t *)p = hd->h##a ; p += 4; } while(0)
+  #else /* little endian */
+    #define X(a) do { *p++ = hd->h##a >> 24; *p++ = hd->h##a >> 16;     \
+                     *p++ = hd->h##a >> 8; *p++ = hd->h##a; } while(0)
+  #endif
+    X(0);
+    X(1);
+    X(2);
+    X(3);
+    X(4);
+  #undef X
+
+}
+
+uint8_t *sha1_read( SHA1_CONTEXT *hd )
+{
+    return hd->buf;
+}
diff --git a/sha.h b/sha.h
new file mode 100644 (file)
index 0000000..829bb34
--- /dev/null
+++ b/sha.h
@@ -0,0 +1,25 @@
+#ifndef __SHA_H__
+#define __SHA_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+// #include <stdint.h>
+#include <inttypes.h>
+
+#include "bithelp.h"
+
+typedef struct {
+    uint32_t   h0,h1,h2,h3,h4;
+    uint32_t   nblocks;
+    uint8_t    buf[64];
+    int                count;
+} SHA1_CONTEXT;
+
+void sha1_init(SHA1_CONTEXT *);
+void sha1_write(SHA1_CONTEXT *, uint8_t *, size_t);
+void sha1_final(SHA1_CONTEXT *);
+unsigned char *sha1_read(SHA1_CONTEXT *);
+
+#endif /* __SHA_H__ */
diff --git a/stats.c b/stats.c
new file mode 100644 (file)
index 0000000..0e2662b
--- /dev/null
+++ b/stats.c
@@ -0,0 +1,145 @@
+/*
+ * stats.c - various routines to do stats on the key graph
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2000-2002 Project Purple
+ */
+
+#include <stdlib.h>
+
+#include "hash.h"
+#include "keydb.h"
+#include "ll.h"
+#include "stats.h"
+
+/**
+ *     initcolour - Clear the key graph ready for use.
+ *     @parent: Do we want to clear the parent pointers too?
+ *
+ *     Clears the parent and colour information on all elements in the key
+ *     graph.
+ */
+void initcolour(bool parent)
+{
+       unsigned long loop;
+       struct ll *curkey;
+
+       /*
+        * Init the colour/parent values. We get each entry list from the hash
+        * table and walk along it, zeroing the values.
+        */
+       for (loop = 0; loop < HASHSIZE; loop++) {
+               curkey = gethashtableentry(loop);
+               while (curkey != NULL) {
+                       ((struct stats_key *)curkey->object)->colour = 0;
+                       if (parent) {
+                               ((struct stats_key *)curkey->object)->parent =
+                                       0;
+                       }
+                       curkey = curkey->next;
+               }
+       }
+}
+
+/**
+ *     findpath - Given 2 keys finds a path between them.
+ *     @have: The key we have.
+ *     @want: The key we want to get to.
+ *
+ *     This does a breadth first search on the key tree, starting with the
+ *     key we have. It returns as soon as a path is found or when we run out
+ *     of keys; whichever comes sooner.
+ */
+unsigned long findpath(struct stats_key *have, struct stats_key *want)
+{
+       struct ll *keys = NULL;
+       struct ll *sigs = NULL;
+       struct ll *nextkeys = NULL;
+       long curdegree = 0;
+       long count = 0;
+       
+       curdegree = 1;
+       keys = lladd(NULL, want);
+
+       while (keys != NULL && have->colour == 0) {
+               sigs = hash_getkeysigs(((struct stats_key *)
+                                       keys->object)->keyid);
+               while (sigs != NULL && have->colour == 0) {
+                       /*
+                        * Check if we've seen this key before and if not mark
+                        * it and add its sigs to the list we want to look at.
+                        */
+                       if (((struct stats_key *)sigs->object)->colour == 0) {
+                               count++;
+                               ((struct stats_key *)sigs->object)->colour =
+                                       curdegree;
+                               ((struct stats_key *)sigs->object)->parent =
+                                       ((struct stats_key *)
+                                        keys->object)->keyid;
+                               nextkeys = lladd(nextkeys, sigs->object);
+                       }
+                       sigs = sigs->next;
+               }
+               keys = keys->next;
+               if (keys == NULL) {
+                       keys = nextkeys;
+                       nextkeys = NULL;
+                       curdegree++;
+               }
+       }
+
+       return count;
+}
+
+struct stats_key *furthestkey(struct stats_key *have)
+{
+       unsigned long count = 0;
+       unsigned long curdegree = 0;
+       struct ll *curll, *nextll, *tmp;
+       struct ll *sigs = NULL;
+       struct stats_key *max;
+
+       if (have == NULL) {
+               return NULL;
+       }
+
+       ++curdegree;
+
+       nextll = NULL;
+       max = have;
+       curll = lladd(NULL, have);
+
+       while (curll != NULL) {
+               sigs = hash_getkeysigs(((struct stats_key *)
+                               curll->object)->keyid);
+               while (sigs != NULL) {
+                       if (((struct stats_key *) sigs->object)->colour == 0) {
+                               /*
+                                * We've never seen it. Count it, mark it and
+                                * explore its subtree.
+                                */
+                               count++;
+                               max = (struct stats_key *)sigs->object;
+                               ((struct stats_key *)sigs->object)->colour = 
+                                       curdegree;
+                               ((struct stats_key *)sigs->object)->parent = 
+                                       ((struct stats_key *)
+                                        curll->object)->keyid;
+                               
+                               nextll=lladd(nextll, sigs->object);
+                       }
+                       sigs=sigs->next;
+               }
+               tmp = curll->next;
+               free(curll);
+               curll = tmp;
+               if (curll == NULL) {
+                       curll = nextll;
+                       nextll = NULL;
+                       ++curdegree;
+               };
+       }
+
+       return max;
+}
diff --git a/stats.h b/stats.h
new file mode 100644 (file)
index 0000000..af9f79f
--- /dev/null
+++ b/stats.h
@@ -0,0 +1,67 @@
+/*
+ * stats.c - various routines to do stats on the key graph
+ *
+ * Jonathan McDowell <noodles@earth.li>
+ *
+ * Copyright 2002 Project Purple
+ */
+
+/* MOSTSIGNED
+SIGNSMOST
+SIGNS <key>
+SIGS <key>
+SIXDEGREES <keyid>
+MAXPATH
+
+key_getsigs - get the sigs for a key.
+key_getsigns - get the keys a key signs. */
+
+#ifndef __STATS_H__
+#define __STATS_H__
+
+#include <stdbool.h>
+// #include <stdint.h>
+#include <inttypes.h>
+
+#include "ll.h"
+
+/**
+ *     struct stats_key - holds key details suitable for doing stats on.
+ *     @keyid: The keyid.
+ *     @colour: Used for marking during DFS/BFS.
+ *     @parent: The key that lead us to this one for DFS/BFS.
+ *     @sigs: A linked list of the signatures on this key.
+ *     @gotsigs: A bool indicating if we've initialized the sigs element yet.
+ */
+struct stats_key {
+       uint64_t keyid;
+       int colour;
+       uint64_t parent;
+       struct ll *sigs;
+       bool gotsigs;
+};
+
+/**
+ *     initcolour - Clear the key graph ready for use.
+ *     @parent: Do we want to clear the parent pointers too?
+ *
+ *     Clears the parent and colour information on all elements in the key
+ *     graph.
+ */
+void initcolour(bool parent);
+
+/**
+ *     findpath - Given 2 keys finds a path between them.
+ *     @have: The key we have.
+ *     @want: The key we want to get to.
+ *
+ *     This does a breadth first search on the key tree, starting with the
+ *     key we have. It returns as soon as a path is found or when we run out
+ *     of keys; whichever comes sooner.
+ */
+unsigned long findpath(struct stats_key *have, struct stats_key *want);
+
+
+struct stats_key *furthestkey(struct stats_key *have);
+
+#endif /* __STATS_H__ */
diff --git a/strace.out b/strace.out
new file mode 100644 (file)
index 0000000..9bc447a
--- /dev/null
@@ -0,0 +1,409 @@
+execve("./testparse", ["./testparse"], [/* 28 vars */]) = 0
+brk(0)                                  = 0x80549a4
+open("/etc/ld.so.preload", O_RDONLY)    = -1 ENOENT (No such file or directory)
+open("/etc/ld.so.cache", O_RDONLY)      = 4
+fstat(4, {st_mode=S_IFREG|0644, st_size=12180, ...}) = 0
+old_mmap(NULL, 12180, PROT_READ, MAP_PRIVATE, 4, 0) = 0x40014000
+close(4)                                = 0
+open("/usr/lib/libdb2.so.2", O_RDONLY)  = 4
+fstat(4, {st_mode=S_IFREG|0644, st_size=278604, ...}) = 0
+read(4, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\200\177"..., 4096) = 4096
+old_mmap(NULL, 275180, PROT_READ|PROT_EXEC, MAP_PRIVATE, 4, 0) = 0x40017000
+mprotect(0x40059000, 4844, PROT_NONE)   = 0
+old_mmap(0x40059000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 4, 0x41000) = 0x40059000
+close(4)                                = 0
+open("/lib/libc.so.6", O_RDONLY)        = 4
+fstat(4, {st_mode=S_IFREG|0755, st_size=888064, ...}) = 0
+read(4, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\244\213"..., 4096) = 4096
+old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4005b000
+old_mmap(NULL, 902396, PROT_READ|PROT_EXEC, MAP_PRIVATE, 4, 0) = 0x4005c000
+mprotect(0x40131000, 29948, PROT_NONE)  = 0
+old_mmap(0x40131000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 4, 0xd4000) = 0x40131000
+old_mmap(0x40135000, 13564, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40135000
+close(4)                                = 0
+munmap(0x40014000, 12180)               = 0
+getpid()                                = 5891
+write(2, "Doing read_openpgp_stream():\n", 29) = 29
+read(0, "\231", 1)                      = 1
+brk(0)                                  = 0x80549a4
+brk(0x80549c4)                          = 0x80549c4
+brk(0x8055000)                          = 0x8055000
+read(0, "\1", 1)                        = 1
+read(0, "\242", 1)                      = 1
+read(0, "\0048\25\307\311\21\4\0\340\203\230\232mP\306b\211\007"..., 418) = 418
+read(0, "\264", 1)                      = 1
+read(0, "$", 1)                         = 1
+read(0, "Jonathan McDowell <noodles@earth"..., 36) = 36
+read(0, "\210", 1)                      = 1
+read(0, "U", 1)                         = 1
+read(0, "\4\23\21\2\0\25\5\0028\25\307\311\3\v\n\3\3\25\3\2\3\26"..., 85) = 85
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\26=\5\0\n\t\20\353|\272\246\200\310"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028R\263\23\0\n\t\20\355\211\231\6\324"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028]+s\0\n\t\20>\222Z9\251}\375G\320"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\237Fb\0\n\t\20\200,\276\345B\366"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "?", 1)                         = 1
+read(0, "\3\5\0208\253\330P\261\202\275k\306?\346\22\21\2\24A\0"..., 63) = 63
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\265\313\277\0\n\t\20f\250\372\212"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\265\345\20\0\n\t\20\\\366T?2\372"..., 70) = 70
+read(0, "\211", 1)                      = 1
+read(0, "\0", 1)                        = 1
+read(0, "\225", 1)                      = 1
+brk(0x8056000)                          = 0x8056000
+read(0, "\3\5\0208\265\313\263 \24Y\314\200\2108\1\1\1\3622\4\0"..., 149) = 149
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\265\242\36\0\n\t\20\16\377\227\f"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "E", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\265\306~\0\n\t\20\263\276\v\213\5"..., 69) = 69
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\265\327Q\0\n\t\20;:O\n\307\236[j"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\266\325&\0\n\t\20\215$#\20U\35t\20"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\266\332\332\0\n\t\20$\305\303\201"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\267\21\265\0\n\t\20\212!t1Y|\322"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\265\367\30\0\n\t\20c;\205(Qx\342"..., 70) = 70
+read(0, "\211", 1)                      = 1
+read(0, "\1", 1)                        = 1
+read(0, "\25", 1)                       = 1
+read(0, "\3\5\0208\267\354\373\253VS\356\316\327l\1\1\1\237\336"..., 277) = 277
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\265\316\216\0\n\t\20p\377|je\26\1"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\2701b\0\n\t\20\200LyTvhw\263\017"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\272`E\0\n\t\20\211Z\313\r\27 \f("..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\274`\202\0\n\t\20\213\216\355\324"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\275j\16\0\n\t\20\221\274u\262\270"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\275\244\303\0\n\t\20`\24%0\3\337"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "?", 1)                         = 1
+read(0, "\3\5\0208\265\275\215\227CRw\36\370H,\21\2\300v\0\240\355"..., 63) = 63
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\276UK\0\n\t\20\267\0356\371\303\321"..., 70) = 70
+read(0, "\211", 1)                      = 1
+read(0, "\1", 1)                        = 1
+read(0, "\25", 1)                       = 1
+read(0, "\3\5\0208\310**V\r52\317`V\1\1\1\214\251\10\0\207R\366"..., 277) = 277
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\267\373\212\0\n\t\20r\373\375\226"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\326\266\245\0\n\t\20\20\372D\231"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\326\315\314\0\n\t\20\rIu\217\362"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0028\372\351\27\0\n\t\20\303\274\3669"..., 70) = 70
+read(0, "\211", 1)                      = 1
+read(0, "\0", 1)                        = 1
+read(0, "\225", 1)                      = 1
+read(0, "\3\5\02099^s\271\205\230\"M\304\347\375\1\0015d\4\0\235"..., 149) = 149
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0029;\346\v\0\n\t\20\0174\5w\320\227\242"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0029bW\363\0\n\t\20c0\342\243\247.\21"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\0029\207V\372\0\n\t\20\4\265\30\35\235"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2:\200H\304\0\n\t\20)\276]\"h\375T\237"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2:\246\34 \0\n\t\0201\7e\373\337\\\342"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2:\353\357\327\0\n\t\0201Z\321\36I\27"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2;F\376\333\0\n\t\20]\312\337\3415\1\346"..., 70) = 70
+read(0, "\210", 1)                      = 1
+brk(0x8057000)                          = 0x8057000
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2:\367\325$\0\n\t\20b\3\212K\335\233\231"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2:\367\324\263\0\n\t\20q\177\270\244\27"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2:\353\224\213\0\n\t\20|;yp\210\307\301"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2:\321\227\10\0\n\t\20\210\320\362T3\267"..., 70) = 70
+read(0, "\211", 1)                      = 1
+read(0, "\0", 1)                        = 1
+read(0, "\225", 1)                      = 1
+read(0, "\3\5\20:\354\245\227\266\233\30.\276\223\267\231\1\1s\355"..., 149) = 149
+read(0, "\211", 1)                      = 1
+read(0, "\0", 1)                        = 1
+read(0, "\225", 1)                      = 1
+read(0, "\3\5\20:\354\245\240\303\26\216\272#\365\255\333\1\1~z"..., 149) = 149
+read(0, "\211", 1)                      = 1
+read(0, "\0", 1)                        = 1
+read(0, "\225", 1)                      = 1
+read(0, "\2\5\20:\367\325f\305(\32\345\4d\347\345\1\1-H\4\0\203"..., 149) = 149
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2:\354\242\247\0\n\t\20\372\10\276\256"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2;I\372t\0\n\t\20!g%p\27O\3565Z\n\0\235"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2;\302\16\201\0\n\t\20s\277\226\253\240"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2;\25287\0\n\t\20\200?\356\22\'\24\33"..., 70) = 70
+read(0, "\211", 1)                      = 1
+read(0, "\1", 1)                        = 1
+read(0, "\36", 1)                       = 1
+read(0, "\4\20\24\3\0\6\5\2<\0\377\314\0\n\t\20\345\345\23\r\212"..., 286) = 286
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2<\2=\6\0\n\t\20=\3066\256\363\212]\215"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2<\25bR\0\n\t\20\235\331\36\234(c\313"..., 70) = 70
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\20\21\2\0\6\5\2<\22\221\312\0\n\t\20\253\16\3]\265M"..., 70) = 70
+read(0, "\271", 1)                      = 1
+read(0, "\2", 1)                        = 1
+read(0, "\r", 1)                        = 1
+read(0, "\0048\25\310\304\20\10\0\247\245\305\16\362\372\22\273"..., 525) = 525
+read(0, "\210", 1)                      = 1
+read(0, "F", 1)                         = 1
+read(0, "\4\30\21\2\0\6\5\0028\25\310\304\0\n\t\20\361\275K\344"..., 70) = 70
+read(0, "", 1)                          = 0
+write(2, "Doing parse_keys():\n", 20)   = 20
+brk(0x8058000)                          = 0x8058000
+brk(0x8059000)                          = 0x8059000
+fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 5), ...}) = 0
+old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40014000
+ioctl(1, TCGETS, {B9600 opost isig icanon echo ...}) = 0
+write(1, "Key id is 0xF1BD4BE45B430367\n", 29) = 29
+open("/community/pgp-keyserver/db-copy/DB_CONFIG", O_RDONLY) = -1 ENOENT (No such file or directory)
+stat("/var/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=4096, ...}) = 0
+open("/community/pgp-keyserver/db-copy/__db_lock.share", O_RDWR) = 4
+fcntl(4, F_SETFD, FD_CLOEXEC)           = 0
+fstat(4, {st_mode=S_IFREG|0640, st_size=729088, ...}) = 0
+old_mmap(NULL, 729088, PROT_READ|PROT_WRITE, MAP_SHARED, 4, 0) = 0x40139000
+open("/community/pgp-keyserver/db-copy/__db_mpool.share", O_RDWR) = 5
+fcntl(5, F_SETFD, FD_CLOEXEC)           = 0
+fstat(5, {st_mode=S_IFREG|0640, st_size=327680, ...}) = 0
+old_mmap(NULL, 327680, PROT_READ|PROT_WRITE, MAP_SHARED, 5, 0) = 0x401eb000
+open("/community/pgp-keyserver/db-copy/keydb000", O_RDONLY) = 6
+fcntl(6, F_SETFD, FD_CLOEXEC)           = 0
+read(6, "<\7\0\0\22\376{\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0"..., 512) = 512
+close(6)                                = 0
+brk(0x805a000)                          = 0x805a000
+open("/community/pgp-keyserver/db-copy/keydb000", O_RDONLY) = 6
+fcntl(6, F_SETFD, FD_CLOEXEC)           = 0
+fstat(6, {st_mode=S_IFREG|0644, st_size=195452928, ...}) = 0
+brk(0x805d000)                          = 0x805d000
+open("/etc/fstab", O_RDONLY)            = 7
+fstat(7, {st_mode=S_IFREG|0644, st_size=638, ...}) = 0
+old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40015000
+read(7, "# /etc/fstab: static file system"..., 4096) = 638
+close(7)                                = 0
+munmap(0x40015000, 4096)                = 0
+open("/proc/cpuinfo", O_RDONLY)         = 7
+fstat(7, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
+old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40015000
+read(7, "processor\t: 0\nvendor_id\t: Genuin"..., 1024) = 381
+read(7, "", 1024)                       = 0
+close(7)                                = 0
+munmap(0x40015000, 4096)                = 0
+pread(6, "<\7\0\0\22\376{\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb001", O_RDONLY) = 7
+fcntl(7, F_SETFD, FD_CLOEXEC)           = 0
+read(7, "<\7\0\0\351\206z\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0"..., 512) = 512
+close(7)                                = 0
+open("/community/pgp-keyserver/db-copy/keydb001", O_RDONLY) = 7
+fcntl(7, F_SETFD, FD_CLOEXEC)           = 0
+fstat(7, {st_mode=S_IFREG|0644, st_size=348774400, ...}) = 0
+brk(0x8060000)                          = 0x8060000
+pread(7, "<\7\0\0\351\206z\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb002", O_RDONLY) = 8
+fcntl(8, F_SETFD, FD_CLOEXEC)           = 0
+read(8, "<\7\0\0\255\307u\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 512) = 512
+close(8)                                = 0
+open("/community/pgp-keyserver/db-copy/keydb002", O_RDONLY) = 8
+fcntl(8, F_SETFD, FD_CLOEXEC)           = 0
+fstat(8, {st_mode=S_IFREG|0644, st_size=195371008, ...}) = 0
+pread(8, "<\7\0\0\255\307u\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb003", O_RDONLY) = 9
+fcntl(9, F_SETFD, FD_CLOEXEC)           = 0
+read(9, "<\7\0\0|S{\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0\5"..., 512) = 512
+close(9)                                = 0
+open("/community/pgp-keyserver/db-copy/keydb003", O_RDONLY) = 9
+fcntl(9, F_SETFD, FD_CLOEXEC)           = 0
+fstat(9, {st_mode=S_IFREG|0644, st_size=201302016, ...}) = 0
+brk(0x8063000)                          = 0x8063000
+pread(9, "<\7\0\0|S{\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0\5"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb004", O_RDONLY) = 10
+fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
+read(10, "<\7\0\0p\254t\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 512) = 512
+close(10)                               = 0
+open("/community/pgp-keyserver/db-copy/keydb004", O_RDONLY) = 10
+fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
+fstat(10, {st_mode=S_IFREG|0644, st_size=195772416, ...}) = 0
+brk(0x8066000)                          = 0x8066000
+pread(10, "<\7\0\0p\254t\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb005", O_RDONLY) = 11
+fcntl(11, F_SETFD, FD_CLOEXEC)          = 0
+read(11, "<\7\0\0005Uw\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0\0\0"..., 512) = 512
+close(11)                               = 0
+open("/community/pgp-keyserver/db-copy/keydb005", O_RDONLY) = 11
+fcntl(11, F_SETFD, FD_CLOEXEC)          = 0
+fstat(11, {st_mode=S_IFREG|0644, st_size=347947008, ...}) = 0
+pread(11, "<\7\0\0005Uw\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0\0\0"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb006", O_RDONLY) = 12
+fcntl(12, F_SETFD, FD_CLOEXEC)          = 0
+read(12, "<\7\0\0\225\201s\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 512) = 512
+close(12)                               = 0
+open("/community/pgp-keyserver/db-copy/keydb006", O_RDONLY) = 12
+fcntl(12, F_SETFD, FD_CLOEXEC)          = 0
+fstat(12, {st_mode=S_IFREG|0644, st_size=195354624, ...}) = 0
+brk(0x8069000)                          = 0x8069000
+pread(12, "<\7\0\0\225\201s\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb007", O_RDONLY) = 13
+fcntl(13, F_SETFD, FD_CLOEXEC)          = 0
+read(13, "<\7\0\0tFu\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0\363"..., 512) = 512
+close(13)                               = 0
+open("/community/pgp-keyserver/db-copy/keydb007", O_RDONLY) = 13
+fcntl(13, F_SETFD, FD_CLOEXEC)          = 0
+fstat(13, {st_mode=S_IFREG|0644, st_size=201105408, ...}) = 0
+brk(0x806c000)                          = 0x806c000
+pread(13, "<\7\0\0tFu\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0\363"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb008", O_RDONLY) = 14
+fcntl(14, F_SETFD, FD_CLOEXEC)          = 0
+read(14, "<\7\0\0\232Ir\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 512) = 512
+close(14)                               = 0
+open("/community/pgp-keyserver/db-copy/keydb008", O_RDONLY) = 14
+fcntl(14, F_SETFD, FD_CLOEXEC)          = 0
+fstat(14, {st_mode=S_IFREG|0644, st_size=195674112, ...}) = 0
+brk(0x806f000)                          = 0x806f000
+pread(14, "<\7\0\0\232Ir\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb009", O_RDONLY) = 15
+fcntl(15, F_SETFD, FD_CLOEXEC)          = 0
+read(15, "<\7\0\0\207\211{\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0"..., 512) = 512
+close(15)                               = 0
+open("/community/pgp-keyserver/db-copy/keydb009", O_RDONLY) = 15
+fcntl(15, F_SETFD, FD_CLOEXEC)          = 0
+fstat(15, {st_mode=S_IFREG|0644, st_size=348651520, ...}) = 0
+pread(15, "<\7\0\0\207\211{\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb010", O_RDONLY) = 16
+fcntl(16, F_SETFD, FD_CLOEXEC)          = 0
+read(16, "<\7\0\0_Mz\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0f\27"..., 512) = 512
+close(16)                               = 0
+open("/community/pgp-keyserver/db-copy/keydb010", O_RDONLY) = 16
+fcntl(16, F_SETFD, FD_CLOEXEC)          = 0
+fstat(16, {st_mode=S_IFREG|0644, st_size=195780608, ...}) = 0
+brk(0x8072000)                          = 0x8072000
+pread(16, "<\7\0\0_Mz\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0f\27"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb011", O_RDONLY) = 17
+fcntl(17, F_SETFD, FD_CLOEXEC)          = 0
+read(17, "<\7\0\0\304\375x\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 512) = 512
+close(17)                               = 0
+open("/community/pgp-keyserver/db-copy/keydb011", O_RDONLY) = 17
+fcntl(17, F_SETFD, FD_CLOEXEC)          = 0
+fstat(17, {st_mode=S_IFREG|0644, st_size=200957952, ...}) = 0
+brk(0x8075000)                          = 0x8075000
+pread(17, "<\7\0\0\304\375x\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb012", O_RDONLY) = 18
+fcntl(18, F_SETFD, FD_CLOEXEC)          = 0
+read(18, "<\7\0\0\312\317k\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 512) = 512
+close(18)                               = 0
+open("/community/pgp-keyserver/db-copy/keydb012", O_RDONLY) = 18
+fcntl(18, F_SETFD, FD_CLOEXEC)          = 0
+fstat(18, {st_mode=S_IFREG|0644, st_size=195829760, ...}) = 0
+pread(18, "<\7\0\0\312\317k\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb013", O_RDONLY) = 19
+fcntl(19, F_SETFD, FD_CLOEXEC)          = 0
+read(19, "<\7\0\0\320\220x\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0"..., 512) = 512
+close(19)                               = 0
+open("/community/pgp-keyserver/db-copy/keydb013", O_RDONLY) = 19
+fcntl(19, F_SETFD, FD_CLOEXEC)          = 0
+fstat(19, {st_mode=S_IFREG|0644, st_size=349478912, ...}) = 0
+brk(0x8078000)                          = 0x8078000
+pread(19, "<\7\0\0\320\220x\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb014", O_RDONLY) = 20
+fcntl(20, F_SETFD, FD_CLOEXEC)          = 0
+read(20, "<\7\0\0\366@F\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 512) = 512
+close(20)                               = 0
+open("/community/pgp-keyserver/db-copy/keydb014", O_RDONLY) = 20
+fcntl(20, F_SETFD, FD_CLOEXEC)          = 0
+fstat(20, {st_mode=S_IFREG|0644, st_size=195387392, ...}) = 0
+brk(0x807b000)                          = 0x807b000
+pread(20, "<\7\0\0\366@F\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 8192, 0) = 8192
+open("/community/pgp-keyserver/db-copy/keydb015", O_RDONLY) = 21
+fcntl(21, F_SETFD, FD_CLOEXEC)          = 0
+read(21, "<\7\0\0=\33z\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 512) = 512
+close(21)                               = 0
+open("/community/pgp-keyserver/db-copy/keydb015", O_RDONLY) = 21
+fcntl(21, F_SETFD, FD_CLOEXEC)          = 0
+fstat(21, {st_mode=S_IFREG|0644, st_size=201031680, ...}) = 0
+brk(0x807e000)                          = 0x807e000
+pread(21, "<\7\0\0=\33z\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 8192, 0) = 8192
+pread(14, "\322\6\0\0*\305n\0\3\0\0\0\0\0\0\0\250Q\0\0\f\0\221\2\0"..., 8192, 24576) = 8192
+pread(14, "\363\4\0\0\5\24E\0\250Q\0\0\3\0\0\0\0\0\0\0\4\0\17\25\0"..., 8192, 171245568) = 8192
+close(6)                                = 0
+close(7)                                = 0
+close(8)                                = 0
+close(9)                                = 0
+close(10)                               = 0
+close(11)                               = 0
+close(12)                               = 0
+close(13)                               = 0
+close(14)                               = 0
+close(15)                               = 0
+close(16)                               = 0
+close(17)                               = 0
+close(18)                               = 0
+close(19)                               = 0
+close(20)                               = 0
+close(21)                               = 0
+brk(0x805e000)                          = 0x805e000
+close(5)                                = 0
+munmap(0x401eb000, 327680)              = 0
+close(4)                                = 0
+munmap(0x40139000, 729088)              = 0
+munmap(0x40014000, 4096)                = 0
+_exit(0)                                = ?