Add initial wotsap file generation tool
[onak.git] / wotsap.c
diff --git a/wotsap.c b/wotsap.c
new file mode 100644 (file)
index 0000000..339b949
--- /dev/null
+++ b/wotsap.c
@@ -0,0 +1,205 @@
+/*
+ * wotsap.c - Output a set of wotsap files from an onak keyring
+ *
+ * See:
+ *
+ * http://www.lysator.liu.se/~jc/wotsap/wotfileformat.txt
+ *
+ * for more details of the format.
+ *
+ * Copyright 2013 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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; version 2 of the License.
+ *
+ * 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "hash.h"
+#include "log.h"
+#include "onak-conf.h"
+#include "stats.h"
+#include "version.h"
+
+static struct ll *sortkeyll(struct ll *keys)
+{
+       struct ll *newll, *tmp, **curobj;
+       struct stats_key *curkey, *toadd;
+
+       newll = NULL;
+       while (keys) {
+               toadd = (struct stats_key *) keys->object;
+               curobj = &newll;
+               while (*curobj) {
+                       curkey = (struct stats_key *) (*curobj)->object;
+                       if (curkey->keyid >= toadd->keyid) {
+                               break;
+                       }
+                       curobj = &((*curobj)->next);
+               }
+
+               tmp = keys->next;
+               if (*curobj == NULL || curkey->keyid != toadd->keyid) {
+                       keys->next = *curobj;
+                       *curobj = keys;
+               }
+               keys = tmp;
+       }
+       return newll;
+}
+
+static void output_key(FILE *names, FILE *keys, uint64_t keyid)
+{
+       fprintf(names, "%s\n", config.dbbackend->keyid2uid(keyid));
+       fprintf(keys, "%c%c%c%c", (int) (keyid >> 24) & 0xFF,
+                       (int) (keyid >> 16) & 0xFF,
+                       (int) (keyid >>  8) & 0xFF,
+                       (int) (keyid      ) & 0xFF);
+}
+
+static void wotsap(uint64_t keyid, char *dir)
+{
+       struct ll *pending, *sigll, *sigsave;
+       uint32_t curidx = 0;
+       struct stats_key *curkey, *addkey;
+       char *uid;
+       FILE *names, *keys, *sigs, *file;
+       char *tmppath;
+       uint32_t sigcount, sigentry;
+
+       /* Length of dir + "/" + "signatures" + NUL */
+       tmppath = malloc(strlen(dir) + 12);
+
+       sprintf(tmppath, "%s/WOTVERSION", dir);
+       file = fopen(tmppath, "w");
+       if (file == NULL) {
+               fprintf(stderr, "Couldn't open %s\n", tmppath);
+               return;
+       }
+       fprintf(file, "0.2\n");
+       fclose(file);
+
+       sprintf(tmppath, "%s/README", dir);
+       file = fopen(tmppath, "w");
+       if (file == NULL) {
+               fprintf(stderr, "Couldn't open %s\n", tmppath);
+               return;
+       }
+       fprintf(file, "This is a Web of Trust archive.\n");
+       fprintf(file, "The file format is documented at:\n");
+       fprintf(file, "  http://www.lysator.liu.se/~jc/wotsap/wotfileformat.txt\n\n");
+       fprintf(file, "This file was generated by onak " ONAK_VERSION " \n");
+       fclose(file);
+
+       sprintf(tmppath, "%s/names", dir);
+       names = fopen(tmppath, "w");
+       if (names == NULL) {
+               fprintf(stderr, "Couldn't open %s\n", tmppath);
+               return;
+       }
+       sprintf(tmppath, "%s/keys", dir);
+       keys = fopen(tmppath, "wb");
+       if (keys == NULL) {
+               fprintf(stderr, "Couldn't open %s\n", tmppath);
+               return;
+       }
+       sprintf(tmppath, "%s/signatures", dir);
+       sigs = fopen(tmppath, "wb");
+       if (sigs == NULL) {
+               fprintf(stderr, "Couldn't open %s\n", tmppath);
+               return;
+       }
+       free(tmppath);
+
+       config.dbbackend->cached_getkeysigs(keyid);
+       curkey = findinhash(keyid);
+       curkey->colour = ++curidx;
+       pending = lladd(NULL, curkey);
+
+       output_key(names, keys, curkey->keyid);
+
+       while (pending != NULL) {
+               curkey = (struct stats_key *) pending->object;
+               sigll = config.dbbackend->cached_getkeysigs(curkey->keyid);
+               sigsave = sigll = sortkeyll(sigll);
+               sigcount = 0;
+               while (sigll != NULL) {
+                       addkey = (struct stats_key *) sigll->object;
+                       if (addkey->colour == 0) {
+                               uid = config.dbbackend->keyid2uid(addkey->keyid);
+                               if (uid != NULL) {
+                                       addkey->colour = ++curidx;
+                                       pending = lladdend(pending, addkey);
+                                       output_key(names, keys, addkey->keyid);
+                               }
+                       }
+                       if (addkey->colour != 0) {
+                               sigcount++;
+                       }
+                       sigll = sigll->next;
+               }
+               /* Now output the signatures */
+               sigcount = htonl(sigcount);
+               fwrite(&sigcount, sizeof (sigcount), 1, sigs);
+               sigll = sigsave;
+               while (sigll != NULL) {
+                       addkey = (struct stats_key *) sigll->object;
+                       if (addkey->colour != 0) {
+                               sigentry = addkey->colour - 1;
+                               /* Pretend it's on the primary UID for now */
+                               sigentry |= 0x40000000;
+                               sigentry = htonl(sigentry);
+                               fwrite(&sigentry, sizeof (sigentry), 1, sigs);
+                       }
+                       sigll = sigll->next;
+               }
+               pending = pending->next;
+       }
+
+       fclose(sigs);
+       fclose(keys);
+       fclose(names);
+}
+
+int main(int argc, char *argv[])
+{
+       int optchar;
+       char *configfile = NULL, *dir = NULL;
+       uint64_t keyid = 0x2DA8B985;
+
+       while ((optchar = getopt(argc, argv, "c:")) != -1 ) {
+               switch (optchar) {
+               case 'c':
+                       configfile = strdup(optarg);
+                       break;
+               }
+       }
+
+       if (optind < argc) {
+               dir = argv[optind];
+       }
+
+       readconfig(configfile);
+       initlogthing("wotsap", config.logfile);
+       config.dbbackend->initdb(true);
+       inithash();
+       wotsap(config.dbbackend->getfullkeyid(keyid), dir ? dir : ".");
+       destroyhash();
+       config.dbbackend->cleanupdb();
+       cleanuplogthing();
+       cleanupconfig();
+}