]> the.earth.li Git - onak.git/blobdiff - keydb_pg.c
Add ability to drop overly large packets
[onak.git] / keydb_pg.c
index 25881c0820e1c09d8992b88d6947edd0e260e274..0b58d28f6e753e4d1563db777105da0b8a9b99b3 100644 (file)
@@ -1,9 +1,19 @@
 /*
  * keydb_pg.c - Routines to store and fetch keys in a PostGres database.
  *
- * Jonathan McDowell <noodles@earth.li>
+ * Copyright 2002-2004 Jonathan McDowell <noodles@earth.li>
  *
- * Copyright 2002-2004 Project Purple
+ * 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, see <https://www.gnu.org/licenses/>.
  */
 
 #include <postgresql/libpq-fe.h>
 #include "onak-conf.h"
 #include "parsekey.h"
 
-/**
- *     dbconn - our connection to the database.
- */
-static PGconn *dbconn = NULL;
+struct pg_fc_ctx {
+       PGconn *dbconn;
+       int fd;
+};
 
 /**
  *     keydb_fetchchar - Fetches a char from a file.
  */
-static int keydb_fetchchar(void *fd, size_t count, unsigned char *c)
+static int keydb_fetchchar(void *_ctx, size_t count, void *c)
 {
-       return (!lo_read(dbconn, *(int *) fd, (char *) c, count));
-}
+       struct pg_fc_ctx *ctx = (struct pg_fc_ctx *) _ctx;
 
-/**
- *     keydb_putchar - Puts a char to a file.
- */
-static int keydb_putchar(void *fd, size_t count, unsigned char *c)
-{
-       return !(lo_write(dbconn, *(int *) fd, (char *) c, count));
+       return (!lo_read(ctx->dbconn, ctx->fd, (char *) c, count));
 }
 
 /**
- *     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.
+ *     keydb_putchar - Puts a char to a file.
  */
-static void pg_initdb(bool readonly)
+static int keydb_putchar(void *_ctx, size_t count, void *c)
 {
-       dbconn = PQsetdbLogin(config.pg_dbhost, // host
-                       NULL, // port
-                       NULL, // options
-                       NULL, // tty
-                       config.pg_dbname, // database
-                       config.pg_dbuser,  //login
-                       config.pg_dbpass); // password
+       struct pg_fc_ctx *ctx = (struct pg_fc_ctx *) _ctx;
 
-       if (PQstatus(dbconn) == CONNECTION_BAD) {
-               logthing(LOGTHING_CRITICAL, "Connection to database failed.");
-               logthing(LOGTHING_CRITICAL, "%s", 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.
- */
-static void pg_cleanupdb(void)
-{
-       PQfinish(dbconn);
-       dbconn = NULL;
+       return !(lo_write(ctx->dbconn, ctx->fd, (char *) c, count));
 }
 
 /**
@@ -94,8 +70,9 @@ static void pg_cleanupdb(void)
  *     operations on the database to help speed it all up, or if we want
  *     something to only succeed if all relevant operations are successful.
  */
-static bool pg_starttrans(void)
+static bool pg_starttrans(struct onak_dbctx *dbctx)
 {
+       PGconn *dbconn = (PGconn *) dbctx->priv;
        PGresult *result = NULL;
        
        result = PQexec(dbconn, "BEGIN");
@@ -109,8 +86,9 @@ static bool pg_starttrans(void)
  *
  *     Ends a transaction.
  */
-static void pg_endtrans(void)
+static void pg_endtrans(struct onak_dbctx *dbctx)
 {
+       PGconn *dbconn = (PGconn *) dbctx->priv;
        PGresult *result = NULL;
 
        result = PQexec(dbconn, "COMMIT");
@@ -120,7 +98,7 @@ static void pg_endtrans(void)
 }
 
 /**
- *     fetch_key - Given a keyid fetch the key from storage.
+ *     fetch_key_id - Given a keyid fetch the key from storage.
  *     @keyid: The keyid to fetch.
  *     @publickey: A pointer to a structure to return the key in.
  *     @intrans: If we're already in a transaction.
@@ -131,17 +109,20 @@ static void pg_endtrans(void)
  *     in and then parse_keys() to parse the packets into a publickey
  *     structure.
  */
-static int pg_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
+static int pg_fetch_key_id(struct onak_dbctx *dbctx,
+               uint64_t keyid,
+               struct openpgp_publickey **publickey,
                bool intrans)
 {
        struct openpgp_packet_list *packets = NULL;
+       PGconn *dbconn = (PGconn *) dbctx->priv;
        PGresult *result = NULL;
        char *oids = NULL;
        char statement[1024];
-       int fd = -1;
        int i = 0;
        int numkeys = 0;
        Oid key_oid;
+       struct pg_fc_ctx fcctx;
 
        if (!intrans) {
                result = PQexec(dbconn, "BEGIN");
@@ -150,12 +131,13 @@ static int pg_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
        
        if (keyid > 0xFFFFFFFF) {
                snprintf(statement, 1023,
-                       "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
+                       "SELECT keydata FROM onak_keys WHERE keyid = '%"
+                       PRIX64 "'",
                        keyid);
        } else {
                snprintf(statement, 1023,
                        "SELECT keydata FROM onak_keys WHERE keyid "
-                       "LIKE '%%%llX'",
+                       "LIKE '%%%" PRIX64 "'",
                        keyid);
        }
        result = PQexec(dbconn, statement);
@@ -166,15 +148,16 @@ static int pg_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
                        oids = PQgetvalue(result, i, 0);
                        key_oid = (Oid) atoi(oids);
 
-                       fd = lo_open(dbconn, key_oid, INV_READ);
-                       if (fd < 0) {
+                       fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
+                       if (fcctx.fd < 0) {
                                logthing(LOGTHING_ERROR,
                                                "Can't open large object.");
                        } else {
-                               read_openpgp_stream(keydb_fetchchar, &fd,
+                               fcctx.dbconn = dbconn;
+                               read_openpgp_stream(keydb_fetchchar, &fcctx,
                                                &packets, 0);
                                parse_keys(packets, publickey);
-                               lo_close(dbconn, fd);
+                               lo_close(dbconn, fcctx.fd);
                                free_packet_list(packets);
                                packets = NULL;
                        }
@@ -200,25 +183,27 @@ static int pg_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
  *     This function searches for the supplied text and returns the keys that
  *     contain it.
  */
-static int pg_fetch_key_text(const char *search,
+static int pg_fetch_key_text(struct onak_dbctx *dbctx,
+               const char *search,
                struct openpgp_publickey **publickey)
 {
        struct openpgp_packet_list *packets = NULL;
+       PGconn *dbconn = (PGconn *) dbctx->priv;
        PGresult *result = NULL;
        char *oids = NULL;
        char statement[1024];
-       int fd = -1;
        int i = 0;
        int numkeys = 0;
        Oid key_oid;
        char *newsearch = NULL;
+       struct pg_fc_ctx fcctx;
 
        result = PQexec(dbconn, "BEGIN");
        PQclear(result);
 
        newsearch = malloc(strlen(search) * 2 + 1);
        memset(newsearch, 0, strlen(search) * 2 + 1);
-       PQescapeString(newsearch, search, strlen(search));
+       PQescapeStringConn(dbconn, newsearch, search, strlen(search), NULL);
        snprintf(statement, 1023,
                        "SELECT DISTINCT onak_keys.keydata FROM onak_keys, "
                        "onak_uids WHERE onak_keys.keyid = onak_uids.keyid "
@@ -234,16 +219,17 @@ static int pg_fetch_key_text(const char *search,
                        oids = PQgetvalue(result, i, 0);
                        key_oid = (Oid) atoi(oids);
 
-                       fd = lo_open(dbconn, key_oid, INV_READ);
-                       if (fd < 0) {
+                       fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
+                       if (fcctx.fd < 0) {
                                logthing(LOGTHING_ERROR,
                                                "Can't open large object.");
                        } else {
-                               read_openpgp_stream(keydb_fetchchar, &fd,
+                               fcctx.dbconn = dbconn;
+                               read_openpgp_stream(keydb_fetchchar, &fcctx,
                                                &packets,
                                                0);
                                parse_keys(packets, publickey);
-                               lo_close(dbconn, fd);
+                               lo_close(dbconn, fcctx.fd);
                                free_packet_list(packets);
                                packets = NULL;
                        }
@@ -267,8 +253,9 @@ static int pg_fetch_key_text(const char *search,
  *     This function deletes a public key from whatever storage mechanism we
  *     are using. Returns 0 if the key existed.
  */
-static int pg_delete_key(uint64_t keyid, bool intrans)
+static int pg_delete_key(struct onak_dbctx *dbctx, uint64_t keyid, bool intrans)
 {
+       PGconn *dbconn = (PGconn *) dbctx->priv;
        PGresult *result = NULL;
        char *oids = NULL;
        char statement[1024];
@@ -282,7 +269,8 @@ static int pg_delete_key(uint64_t keyid, bool intrans)
        }
        
        snprintf(statement, 1023,
-                       "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
+                       "SELECT keydata FROM onak_keys WHERE keyid = '%"
+                       PRIX64 "'",
                        keyid);
        result = PQexec(dbconn, statement);
 
@@ -298,24 +286,25 @@ static int pg_delete_key(uint64_t keyid, bool intrans)
                PQclear(result);
 
                snprintf(statement, 1023,
-                       "DELETE FROM onak_keys WHERE keyid = '%llX'",
+                       "DELETE FROM onak_keys WHERE keyid = '%" PRIX64 "'",
                        keyid);
                result = PQexec(dbconn, statement);
                PQclear(result);
 
                snprintf(statement, 1023,
-                       "DELETE FROM onak_sigs WHERE signee = '%llX'",
+                       "DELETE FROM onak_sigs WHERE signee = '%" PRIX64 "'",
                        keyid);
                result = PQexec(dbconn, statement);
                PQclear(result);
 
                snprintf(statement, 1023,
-                       "DELETE FROM onak_uids WHERE keyid = '%llX'",
+                       "DELETE FROM onak_uids WHERE keyid = '%" PRIX64 "'",
                        keyid);
                result = PQexec(dbconn, statement);
        } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
                logthing(LOGTHING_ERROR,
-                               "Problem retrieving key (%llX) from DB.",
+                               "Problem retrieving key (%" PRIX64
+                               ") from DB.",
                                keyid);
        }
 
@@ -340,27 +329,35 @@ static int pg_delete_key(uint64_t keyid, bool intrans)
  *     the file. If update is true then we delete the old key first, otherwise
  *     we trust that it doesn't exist.
  */
-static int pg_store_key(struct openpgp_publickey *publickey, bool intrans,
+static int pg_store_key(struct onak_dbctx *dbctx,
+               struct openpgp_publickey *publickey, bool intrans,
                bool update)
 {
        struct openpgp_packet_list *packets = NULL;
        struct openpgp_packet_list *list_end = NULL;
        struct openpgp_publickey *next = NULL;
        struct openpgp_signedpacket_list *curuid = NULL;
+       PGconn *dbconn = (PGconn *) dbctx->priv;
        PGresult *result = NULL;
        char statement[1024];
        Oid key_oid;
-       int fd;
        char **uids = NULL;
        char *primary = NULL;
        char *safeuid = NULL;
        int i;
+       uint64_t keyid;
+       struct pg_fc_ctx fcctx;
 
        if (!intrans) {
                result = PQexec(dbconn, "BEGIN");
                PQclear(result);
        }
 
+       if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
+               logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
+               return 0;
+       }
+
        /*
         * Delete the key if we already have it.
         *
@@ -370,7 +367,7 @@ static int pg_store_key(struct openpgp_publickey *publickey, bool intrans,
         * it definitely needs updated.
         */
        if (update) {
-               pg_delete_key(get_keyid(publickey), true);
+               pg_delete_key(dbctx, keyid, true);
        }
 
        next = publickey->next;
@@ -382,17 +379,18 @@ static int pg_store_key(struct openpgp_publickey *publickey, bool intrans,
        if (key_oid == 0) {
                logthing(LOGTHING_ERROR, "Can't create key OID");
        } else {
-               fd = lo_open(dbconn, key_oid, INV_WRITE);
-               write_openpgp_stream(keydb_putchar, &fd, packets);
-               lo_close(dbconn, fd);
+               fcctx.fd = lo_open(dbconn, key_oid, INV_WRITE);
+               fcctx.dbconn = dbconn;
+               write_openpgp_stream(keydb_putchar, &fcctx, packets);
+               lo_close(dbconn, fcctx.fd);
        }
        free_packet_list(packets);
        packets = NULL;
 
        snprintf(statement, 1023, 
                        "INSERT INTO onak_keys (keyid, keydata) VALUES "
-                       "('%llX', '%d')", 
-                       get_keyid(publickey),
+                       "('%" PRIX64 "', '%d')", 
+                       keyid,
                        key_oid);
        result = PQexec(dbconn, statement);
 
@@ -408,14 +406,14 @@ static int pg_store_key(struct openpgp_publickey *publickey, bool intrans,
                        safeuid = malloc(strlen(uids[i]) * 2 + 1);
                        if (safeuid != NULL) {
                                memset(safeuid, 0, strlen(uids[i]) * 2 + 1);
-                               PQescapeString(safeuid, uids[i],
-                                               strlen(uids[i]));
+                               PQescapeStringConn(dbconn, safeuid, uids[i],
+                                               strlen(uids[i]), NULL);
 
                                snprintf(statement, 1023,
                                        "INSERT INTO onak_uids "
                                        "(keyid, uid, pri) "
-                                       "VALUES ('%llX', '%s', '%c')",
-                                       get_keyid(publickey),
+                                       "VALUES ('%" PRIX64 "', '%s', '%c')",
+                                       keyid,
                                        safeuid,
                                        (uids[i] == primary) ? 't' : 'f');
                                result = PQexec(dbconn, statement);
@@ -448,9 +446,9 @@ static int pg_store_key(struct openpgp_publickey *publickey, bool intrans,
                                packets = packets->next) {
                        snprintf(statement, 1023,
                                "INSERT INTO onak_sigs (signer, signee) "
-                               "VALUES ('%llX', '%llX')",
+                               "VALUES ('%" PRIX64 "', '%" PRIX64 "')",
                                sig_keyid(packets->packet),
-                               get_keyid(publickey));
+                               keyid);
                        result = PQexec(dbconn, statement);
                        PQclear(result);
                }
@@ -468,14 +466,16 @@ static int pg_store_key(struct openpgp_publickey *publickey, bool intrans,
  *     keyid2uid - Takes a keyid and returns the primary UID for it.
  *     @keyid: The keyid to lookup.
  */
-static char *pg_keyid2uid(uint64_t keyid)
+static char *pg_keyid2uid(struct onak_dbctx *dbctx, uint64_t keyid)
 {
+       PGconn *dbconn = (PGconn *) dbctx->priv;
        PGresult *result = NULL;
        char statement[1024];
        char *uid = NULL;
 
        snprintf(statement, 1023,
-               "SELECT uid FROM onak_uids WHERE keyid = '%llX' AND pri = 't'",
+               "SELECT uid FROM onak_uids WHERE keyid = '%" PRIX64
+               "' AND pri = 't'",
                keyid);
        result = PQexec(dbconn, statement);
 
@@ -491,7 +491,8 @@ static char *pg_keyid2uid(uint64_t keyid)
                uid = strdup(PQgetvalue(result, 0, 0));
        } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
                logthing(LOGTHING_ERROR,
-                               "Problem retrieving key (%llX) from DB.",
+                               "Problem retrieving key (%" PRIX64
+                               ") from DB.",
                                keyid);
        }
 
@@ -508,9 +509,11 @@ static char *pg_keyid2uid(uint64_t keyid)
  *     This function gets the list of signatures on a key. Used for key 
  *     indexing and doing stats bits.
  */
-static struct ll *pg_getkeysigs(uint64_t keyid, bool *revoked)
+static struct ll *pg_getkeysigs(struct onak_dbctx *dbctx,
+                       uint64_t keyid, bool *revoked)
 {
        struct ll *sigs = NULL;
+       PGconn *dbconn = (PGconn *) dbctx->priv;
        PGresult *result = NULL;
        uint64_t signer;
        char statement[1024];
@@ -525,7 +528,8 @@ static struct ll *pg_getkeysigs(uint64_t keyid, bool *revoked)
        }
 
        snprintf(statement, 1023,
-               "SELECT DISTINCT signer FROM onak_sigs WHERE signee = '%llX'",
+               "SELECT DISTINCT signer FROM onak_sigs WHERE signee = '%"
+               PRIX64 "'",
                keyid);
        result = PQexec(dbconn, statement);
 
@@ -580,18 +584,19 @@ static struct ll *pg_getkeysigs(uint64_t keyid, bool *revoked)
  *
  *     Returns the number of keys we iterated over.
  */
-static int pg_iterate_keys(void (*iterfunc)(void *ctx,
+static int pg_iterate_keys(struct onak_dbctx *dbctx,
+               void (*iterfunc)(void *ctx,
                struct openpgp_publickey *key), void *ctx)
 {
        struct openpgp_packet_list *packets = NULL;
        struct openpgp_publickey *key = NULL;
+       PGconn *dbconn = (PGconn *) dbctx->priv;
        PGresult *result = NULL;
        char *oids = NULL;
-       char statement[1024];
-       int fd = -1;
        int i = 0;
        int numkeys = 0;
        Oid key_oid;
+       struct pg_fc_ctx fcctx;
 
        result = PQexec(dbconn, "SELECT keydata FROM onak_keys;");
 
@@ -601,15 +606,16 @@ static int pg_iterate_keys(void (*iterfunc)(void *ctx,
                        oids = PQgetvalue(result, i, 0);
                        key_oid = (Oid) atoi(oids);
 
-                       fd = lo_open(dbconn, key_oid, INV_READ);
-                       if (fd < 0) {
+                       fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
+                       if (fcctx.fd < 0) {
                                logthing(LOGTHING_ERROR,
                                                "Can't open large object.");
                        } else {
-                               read_openpgp_stream(keydb_fetchchar, &fd,
+                               fcctx.dbconn = dbconn;
+                               read_openpgp_stream(keydb_fetchchar, &fcctx,
                                                &packets, 0);
-                               parse_keys(packets, key);
-                               lo_close(dbconn, fd);
+                               parse_keys(packets, &key);
+                               lo_close(dbconn, fcctx.fd);
 
                                iterfunc(ctx, key);
                                        
@@ -633,21 +639,75 @@ static int pg_iterate_keys(void (*iterfunc)(void *ctx,
  */
 #define NEED_GETFULLKEYID 1
 #define NEED_UPDATEKEYS 1
+#define NEED_GET_FP 1
 #include "keydb.c"
 
-struct dbfuncs keydb_pg_funcs = {
-       .initdb                 = pg_initdb,
-       .cleanupdb              = pg_cleanupdb,
-       .starttrans             = pg_starttrans,
-       .endtrans               = pg_endtrans,
-       .fetch_key              = pg_fetch_key,
-       .fetch_key_text         = pg_fetch_key_text,
-       .store_key              = pg_store_key,
-       .update_keys            = generic_update_keys,
-       .delete_key             = pg_delete_key,
-       .getkeysigs             = pg_getkeysigs,
-       .cached_getkeysigs      = generic_cached_getkeysigs,
-       .keyid2uid              = pg_keyid2uid,
-       .getfullkeyid           = generic_getfullkeyid,
-       .iterate_keys           = pg_iterate_keys,
-};
+/**
+ *     cleanupdb - De-initialize the key database.
+ *
+ *     This function should be called upon program exit to allow the DB to
+ *     cleanup after itself.
+ */
+static void pg_cleanupdb(struct onak_dbctx *dbctx)
+{
+       PGconn *dbconn = (PGconn *) dbctx->priv;
+
+       PQfinish(dbconn);
+       dbconn = NULL;
+
+       free(dbctx);
+}
+
+/**
+ *     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.
+ */
+struct onak_dbctx *keydb_pg_init(struct onak_db_config *dbcfg, bool readonly)
+{
+       struct onak_dbctx *dbctx;
+       PGconn *dbconn;
+
+       dbctx = malloc(sizeof(struct onak_dbctx));
+       if (dbctx == NULL) {
+               return NULL;
+       }
+       dbctx->config = dbcfg;
+
+       dbconn = PQsetdbLogin(dbcfg->hostname, // host
+                       NULL, // port
+                       NULL, // options
+                       NULL, // tty
+                       dbcfg->location, // database
+                       dbcfg->username,  //login
+                       dbcfg->password); // password
+
+       if (PQstatus(dbconn) == CONNECTION_BAD) {
+               logthing(LOGTHING_CRITICAL, "Connection to database failed.");
+               logthing(LOGTHING_CRITICAL, "%s", PQerrorMessage(dbconn));
+               PQfinish(dbconn);
+               dbconn = NULL;
+               exit(1);
+       }
+
+       dbctx->priv = dbconn;
+
+       dbctx->cleanupdb                = pg_cleanupdb;
+       dbctx->starttrans               = pg_starttrans;
+       dbctx->endtrans                 = pg_endtrans;
+       dbctx->fetch_key_id             = pg_fetch_key_id;
+       dbctx->fetch_key_fp             = generic_fetch_key_fp;
+       dbctx->fetch_key_text           = pg_fetch_key_text;
+       dbctx->store_key                = pg_store_key;
+       dbctx->update_keys              = generic_update_keys;
+       dbctx->delete_key               = pg_delete_key;
+       dbctx->getkeysigs               = pg_getkeysigs;
+       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
+       dbctx->keyid2uid                = pg_keyid2uid;
+       dbctx->getfullkeyid             = generic_getfullkeyid;
+       dbctx->iterate_keys             = pg_iterate_keys;
+
+       return dbctx;
+}