cscvs to tla changeset 1
[onak.git] / keydb_pg.c
1 /*
2  * keydb_pg.c - Routines to store and fetch keys in a PostGres database.
3  *
4  * Jonathan McDowell <noodles@earth.li>
5  *
6  * Copyright 2002 Project Purple
7  */
8
9 #include <postgresql/libpq-fe.h>
10 #include <postgresql/libpq/libpq-fs.h>
11
12 //#include <libpq-fe.h>
13 //#include <libpq/libpq-fs.h>
14 #include <sys/types.h>
15 #include <sys/uio.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22
23 #include "keydb.h"
24 #include "keyid.h"
25 #include "keyindex.h"
26 #include "keystructs.h"
27 #include "mem.h"
28 #include "parsekey.h"
29
30 /**
31  *      dbconn - our connection to the database.
32  */
33 static PGconn *dbconn = NULL;
34
35 /**
36  *      keydb_fetchchar - Fetches a char from a file.
37  */
38 static int keydb_fetchchar(void *fd, size_t count, unsigned char *c)
39 {
40         return (!lo_read(dbconn, *(int *) fd, c, count));
41 }
42
43 /**
44  *      keydb_putchar - Puts a char to a file.
45  */
46 static int keydb_putchar(void *fd, unsigned char c)
47 {
48         return !(lo_write(dbconn, *(int *) fd, &c, sizeof(c)));
49 }
50
51 /**
52  *      initdb - Initialize the key database.
53  *
54  *      This function should be called before any of the other functions in
55  *      this file are called in order to allow the DB to be initialized ready
56  *      for access.
57  */
58 void initdb(void)
59 {
60         dbconn = PQsetdbLogin(NULL, // host
61                         NULL, // port
62                         NULL, // options
63                         NULL, // tty
64                         "noodles", // database
65                         NULL,  //login
66                         NULL); // password
67
68         if (PQstatus(dbconn) == CONNECTION_BAD) {
69                 fprintf(stderr, "Connection to database failed.\n");
70                 fprintf(stderr, "%s\n", PQerrorMessage(dbconn));
71                 PQfinish(dbconn);
72                 dbconn = NULL;
73                 exit(1);
74         }
75 }
76
77 /**
78  *      cleanupdb - De-initialize the key database.
79  *
80  *      This function should be called upon program exit to allow the DB to
81  *      cleanup after itself.
82  */
83 void cleanupdb(void)
84 {
85         PQfinish(dbconn);
86         dbconn = NULL;
87 }
88
89 /**
90  *      fetch_key - Given a keyid fetch the key from storage.
91  *      @keyid: The keyid to fetch.
92  *      @publickey: A pointer to a structure to return the key in.
93  *
94  *      We use the hex representation of the keyid as the filename to fetch the
95  *      key from. The key is stored in the file as a binary OpenPGP stream of
96  *      packets, so we can just use read_openpgp_stream() to read the packets
97  *      in and then parse_keys() to parse the packets into a publickey
98  *      structure.
99  */
100 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey)
101 {
102         struct openpgp_packet_list *packets = NULL;
103         PGresult *result = NULL;
104         char *oids = NULL;
105         char statement[1024];
106         int fd = -1;
107         Oid key_oid;
108
109         result = PQexec(dbconn, "BEGIN");
110         PQclear(result);
111         
112         snprintf(statement, 1023,
113                         "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
114                         keyid & 0xFFFFFFFF);
115         result = PQexec(dbconn, statement);
116
117         if (PQresultStatus(result) == PGRES_TUPLES_OK &&
118                         PQntuples(result) == 1) {
119                 oids = PQgetvalue(result, 0, 0);
120                 key_oid = (Oid) atoi(oids);
121
122                 fd = lo_open(dbconn, key_oid, INV_READ);
123                 if (fd < 0) {
124                         fprintf(stderr, "Can't open large object.\n");
125                 } else {
126                         read_openpgp_stream(keydb_fetchchar, &fd, &packets);
127                         parse_keys(packets, publickey);
128                         lo_close(dbconn, fd);
129                 }
130         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
131                 fprintf(stderr, "Problem retrieving key (%llX) from DB.\n",
132                                 keyid);
133         }
134
135         PQclear(result);
136
137         result = PQexec(dbconn, "COMMIT");
138         PQclear(result);
139         return (fd > -1);
140 }
141
142 /**
143  *      store_key - Takes a key and stores it.
144  *      @publickey: A pointer to the public key to store.
145  *
146  *      Again we just use the hex representation of the keyid as the filename
147  *      to store the key to. We flatten the public key to a list of OpenPGP
148  *      packets and then use write_openpgp_stream() to write the stream out to
149  *      the file.
150  */
151 int store_key(struct openpgp_publickey *publickey)
152 {
153         struct openpgp_packet_list *packets = NULL;
154         struct openpgp_packet_list *list_end = NULL;
155         struct openpgp_publickey *next = NULL;
156         PGresult *result = NULL;
157         char statement[1024];
158         Oid key_oid;
159         int fd;
160
161
162         /*
163          * Delete the key if we already have it.
164          *
165          * TODO: Can we optimize this perhaps? Possibly when other data is
166          * involved as well? I suspect this is easiest and doesn't make a lot
167          * of difference though - the largest chunk of data is the keydata and
168          * it definitely needs updated.
169          */
170         delete_key(get_keyid(publickey));
171
172         result = PQexec(dbconn, "BEGIN");
173         PQclear(result);
174
175         next = publickey->next;
176         publickey->next = NULL;
177         flatten_publickey(publickey, &packets, &list_end);
178         publickey->next = next;
179                 
180         key_oid = lo_creat(dbconn, INV_READ | INV_WRITE);
181         if (key_oid == 0) {
182                 fprintf(stderr, "Can't create key OID\n");
183         } else {
184                 fd = lo_open(dbconn, key_oid, INV_WRITE);
185                 write_openpgp_stream(keydb_putchar, &fd, packets);
186                 lo_close(dbconn, fd);
187         }
188
189         snprintf(statement, 1023, 
190                         "INSERT INTO onak_keys (keyid, keydata) VALUES "
191                         "('%llX', '%d')", 
192                         get_keyid(publickey) & 0xFFFFFFFF,
193                         key_oid);
194         result = PQexec(dbconn, statement);
195
196         if (PQresultStatus(result) != PGRES_COMMAND_OK) {
197                 fprintf(stderr, "Problem storing key in DB.\n");
198                 fprintf(stderr, "%s\n", PQresultErrorMessage(result));
199         }
200         PQclear(result);
201
202         result = PQexec(dbconn, "COMMIT");
203         PQclear(result);
204         
205         return 0;
206 }
207
208 /**
209  *      delete_key - Given a keyid delete the key from storage.
210  *      @keyid: The keyid to delete.
211  *
212  *      This function deletes a public key from whatever storage mechanism we
213  *      are using. Returns 0 if the key existed.
214  */
215 int delete_key(uint64_t keyid)
216 {
217         PGresult *result = NULL;
218         char *oids = NULL;
219         char statement[1024];
220         int found = 1;
221         Oid key_oid;
222
223         result = PQexec(dbconn, "BEGIN");
224         PQclear(result);
225         
226         snprintf(statement, 1023,
227                         "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
228                         keyid & 0xFFFFFFFF);
229         result = PQexec(dbconn, statement);
230
231         if (PQresultStatus(result) == PGRES_TUPLES_OK &&
232                         PQntuples(result) == 1) {
233                 found = 0;
234                 oids = PQgetvalue(result, 0, 0);
235                 key_oid = (Oid) atoi(oids);
236                 lo_unlink(dbconn, key_oid);
237                 PQclear(result);
238                 snprintf(statement, 1023,
239                         "DELETE * FROM onak_keys WHERE keyid = '%llX'",
240                         keyid & 0xFFFFFFFF);
241                 result = PQexec(dbconn, statement);
242         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
243                 fprintf(stderr, "Problem retrieving key (%llX) from DB.\n",
244                                 keyid);
245         }
246
247         PQclear(result);
248
249         result = PQexec(dbconn, "COMMIT");
250         PQclear(result);
251         return (found);
252 }
253
254 /*
255  * Include the basic keydb routines.
256  */
257 #include "keydb.c"