--- /dev/null
+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
+
--- /dev/null
+
+/pks/lookup:
+
+op index | vindex | get required
+search required
+fingerprint boolean (true == on)
+exact boolean
+
+
+/pks/add:
+
+keytext for ADD. required
--- /dev/null
+0.0.1 - 16th May 2002.
+
+* First release.
+* Merges gpgstats 0.0.2 (never released).
--- /dev/null
+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
--- /dev/null
+# 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
--- /dev/null
+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. ;)
--- /dev/null
+* 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).
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/* 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__ */
--- /dev/null
+/*
+ * 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, "<", 1023 - strlen(buf));
+ string = nextptr;
+ }
+
+ ptr = strchr(string, '>');
+ if (ptr != NULL) {
+ nextptr = ptr + 1;
+ *ptr = 0;
+ strncat(buf, string, 1023 - strlen(buf));
+ strncat(buf, ">", 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;
+}
--- /dev/null
+#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_ */
--- /dev/null
+# 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
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+#!/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++];
+ }
+}
--- /dev/null
+#!/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";
+ }
+}
--- /dev/null
+#!/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";
--- /dev/null
+/*
+ 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=¶m;
+ commands[4].var=commands[5].var=commands[8].var=¶m;
+ commands[12].var=¶m;
+
+ 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;
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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;
+}
+
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+#!/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";
+
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+<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"><noodles-onak@earth.li></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>
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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"
--- /dev/null
+/*
+ * 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"
--- /dev/null
+/*
+ * 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"
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+#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;
+}
--- /dev/null
+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 { * }
+ }
+ }
+ }
+
+}
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+#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__ */
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+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
+);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+//#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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+#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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+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) = ?