]> the.earth.li Git - onak.git/blob - wotsap.c
Bump debhelper compat level to 13
[onak.git] / wotsap.c
1 /*
2  * wotsap.c - Output a set of wotsap files from an onak keyring
3  *
4  * See:
5  *
6  * http://www.lysator.liu.se/~jc/wotsap/wotfileformat.txt
7  *
8  * for more details of the format.
9  *
10  * Copyright 2013 Jonathan McDowell <noodles@earth.li>
11  *
12  * This program is free software: you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License as published by the Free
14  * Software Foundation; version 2 of the License.
15  *
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19  * more details.
20  *
21  * You should have received a copy of the GNU General Public License along with
22  * this program.  If not, see <https://www.gnu.org/licenses/>.
23  */
24
25 #include <getopt.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <arpa/inet.h>
30
31 #include "build-config.h"
32
33 #include "hash.h"
34 #include "log.h"
35 #include "onak-conf.h"
36 #include "stats.h"
37
38 static struct ll *sortkeyll(struct ll *keys)
39 {
40         struct ll *newll, *tmp, **curobj;
41         struct stats_key *curkey, *toadd;
42
43         newll = NULL;
44         while (keys) {
45                 toadd = (struct stats_key *) keys->object;
46                 curobj = &newll;
47                 while (*curobj) {
48                         curkey = (struct stats_key *) (*curobj)->object;
49                         if (curkey->keyid >= toadd->keyid) {
50                                 break;
51                         }
52                         curobj = &((*curobj)->next);
53                 }
54
55                 tmp = keys->next;
56                 if (*curobj == NULL || curkey->keyid != toadd->keyid) {
57                         keys->next = *curobj;
58                         *curobj = keys;
59                 }
60                 keys = tmp;
61         }
62         return newll;
63 }
64
65 static void output_key(struct onak_dbctx *dbctx,
66                 FILE *names, FILE *keys, uint64_t keyid)
67 {
68         fprintf(names, "%s\n", dbctx->keyid2uid(dbctx, keyid));
69         fprintf(keys, "%c%c%c%c", (int) (keyid >> 24) & 0xFF,
70                         (int) (keyid >> 16) & 0xFF,
71                         (int) (keyid >>  8) & 0xFF,
72                         (int) (keyid      ) & 0xFF);
73 }
74
75 static void wotsap(struct onak_dbctx *dbctx, uint64_t keyid, char *dir)
76 {
77         struct ll *pending, *sigll, *sigsave;
78         uint32_t curidx = 0;
79         struct stats_key *curkey, *addkey;
80         char *uid;
81         FILE *names, *keys, *sigs, *file;
82         char *tmppath;
83         uint32_t sigcount, sigentry;
84
85         /* Length of dir + "/" + "signatures" + NUL */
86         tmppath = malloc(strlen(dir) + 12);
87         if (tmppath == NULL) {
88                 fprintf(stderr, "Couldn't allocate memory for directory\n");
89                 goto err;
90         }
91
92         sprintf(tmppath, "%s/WOTVERSION", dir);
93         file = fopen(tmppath, "w");
94         if (file == NULL) {
95                 fprintf(stderr, "Couldn't open %s\n", tmppath);
96                 goto err;
97         }
98         fprintf(file, "0.2\n");
99         fclose(file);
100
101         sprintf(tmppath, "%s/README", dir);
102         file = fopen(tmppath, "w");
103         if (file == NULL) {
104                 fprintf(stderr, "Couldn't open %s\n", tmppath);
105                 goto err;
106         }
107         fprintf(file, "This is a Web of Trust archive.\n");
108         fprintf(file, "The file format is documented at:\n");
109         fprintf(file, "  http://www.lysator.liu.se/~jc/wotsap/wotfileformat.txt\n\n");
110         fprintf(file, "This file was generated by onak " ONAK_VERSION " \n");
111         fclose(file);
112
113         sprintf(tmppath, "%s/names", dir);
114         names = fopen(tmppath, "w");
115         if (names == NULL) {
116                 fprintf(stderr, "Couldn't open %s\n", tmppath);
117                 goto err;
118         }
119         sprintf(tmppath, "%s/keys", dir);
120         keys = fopen(tmppath, "wb");
121         if (keys == NULL) {
122                 fprintf(stderr, "Couldn't open %s\n", tmppath);
123                 goto err;
124         }
125         sprintf(tmppath, "%s/signatures", dir);
126         sigs = fopen(tmppath, "wb");
127         if (sigs == NULL) {
128                 fprintf(stderr, "Couldn't open %s\n", tmppath);
129                 goto err;
130         }
131
132         dbctx->cached_getkeysigs(dbctx, keyid);
133         curkey = findinhash(keyid);
134         curkey->colour = ++curidx;
135         pending = lladd(NULL, curkey);
136
137         output_key(dbctx, names, keys, curkey->keyid);
138
139         while (pending != NULL) {
140                 curkey = (struct stats_key *) pending->object;
141                 sigll = dbctx->cached_getkeysigs(dbctx, curkey->keyid);
142                 sigsave = sigll = sortkeyll(sigll);
143                 sigcount = 0;
144                 while (sigll != NULL) {
145                         addkey = (struct stats_key *) sigll->object;
146                         if (addkey->colour == 0 && !addkey->revoked) {
147                                 uid = dbctx->keyid2uid(dbctx, addkey->keyid);
148                                 if (uid != NULL) {
149                                         /* Force it to be loaded so we know if it's revoked */
150                                         dbctx->cached_getkeysigs(dbctx,
151                                                         addkey->keyid);
152                                         if (!addkey->revoked) {
153                                                 addkey->colour = ++curidx;
154                                                 pending = lladdend(pending, addkey);
155                                                 output_key(dbctx, names, keys,
156                                                         addkey->keyid);
157                                         }
158                                 }
159                         }
160                         if (addkey->colour != 0) {
161                                 sigcount++;
162                         }
163                         sigll = sigll->next;
164                 }
165                 /* Now output the signatures */
166                 sigcount = htonl(sigcount);
167                 fwrite(&sigcount, sizeof (sigcount), 1, sigs);
168                 sigll = sigsave;
169                 while (sigll != NULL) {
170                         addkey = (struct stats_key *) sigll->object;
171                         if (addkey->colour != 0) {
172                                 sigentry = addkey->colour - 1;
173                                 /* Pretend it's on the primary UID for now */
174                                 sigentry |= 0x40000000;
175                                 sigentry = htonl(sigentry);
176                                 fwrite(&sigentry, sizeof (sigentry), 1, sigs);
177                         }
178                         sigll = sigll->next;
179                 }
180                 pending = pending->next;
181         }
182
183         fclose(sigs);
184         fclose(keys);
185         fclose(names);
186 err:
187         free(tmppath);
188 }
189
190 int main(int argc, char *argv[])
191 {
192         int optchar;
193         char *configfile = NULL, *dir = NULL;
194         uint64_t keyid = 0x94FA372B2DA8B985;
195         struct onak_dbctx *dbctx;
196
197         while ((optchar = getopt(argc, argv, "c:")) != -1 ) {
198                 switch (optchar) {
199                 case 'c':
200                         configfile = strdup(optarg);
201                         break;
202                 }
203         }
204
205         if (optind < argc) {
206                 dir = argv[optind];
207         }
208
209         readconfig(configfile);
210         initlogthing("wotsap", config.logfile);
211         dbctx = config.dbinit(config.backend, true);
212         if (dbctx != NULL) {
213                 inithash();
214                 wotsap(dbctx, keyid, dir ? dir : ".");
215                 destroyhash();
216                 dbctx->cleanupdb(dbctx);
217         } else {
218                 fprintf(stderr, "Couldn't initialize key database.\n");
219         }
220         cleanuplogthing();
221         cleanupconfig();
222         free(configfile);
223 }