+ struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+ struct openpgp_packet_list *packets = NULL;
+ struct openpgp_packet_list *list_end = NULL;
+ struct openpgp_publickey *next = NULL;
+ int ret = 0;
+ int i = 0;
+ struct buffer_ctx storebuf;
+ DBT key;
+ DBT data;
+ uint64_t keyid = 0;
+ uint32_t shortkeyid = 0;
+ uint64_t *subkeyids = NULL;
+ char **uids = NULL;
+ char *primary = NULL;
+ unsigned char worddb_data[12];
+ struct ll *wordlist = NULL;
+ struct ll *curword = NULL;
+ bool deadlock = false;
+ struct skshash hash;
+
+ if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
+ logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
+ return 0;
+ }
+
+ if (!intrans) {
+ db4_starttrans(dbctx);
+ }
+
+ /*
+ * 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.
+ */
+ if (update) {
+ deadlock = (db4_delete_key(dbctx, keyid, true) == -1);
+ }
+
+ /*
+ * Convert the key to a flat set of binary data.
+ */
+ if (!deadlock) {
+ next = publickey->next;
+ publickey->next = NULL;
+ flatten_publickey(publickey, &packets, &list_end);
+ publickey->next = next;
+
+ storebuf.offset = 0;
+ storebuf.size = 8192;
+ storebuf.buffer = malloc(8192);
+
+ write_openpgp_stream(buffer_putchar, &storebuf, packets);
+
+ /*
+ * Now we have the key data store it in the DB; the keyid is
+ * the key.
+ */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = &keyid;
+ key.size = sizeof(keyid);
+ data.size = storebuf.offset;
+ data.data = storebuf.buffer;
+
+ ret = keydb(privctx, keyid)->put(keydb(privctx, keyid),
+ privctx->txn,
+ &key,
+ &data,
+ 0); /* flags*/
+ if (ret != 0) {
+ logthing(LOGTHING_ERROR,
+ "Problem storing key: %s",
+ db_strerror(ret));
+ if (ret == DB_LOCK_DEADLOCK) {
+ deadlock = true;
+ }
+ }
+
+ free(storebuf.buffer);
+ storebuf.buffer = NULL;
+ storebuf.size = 0;
+ storebuf.offset = 0;
+
+ free_packet_list(packets);
+ packets = NULL;
+ }
+
+ /*
+ * Walk through our uids storing the words into the db with the keyid.
+ */
+ if (!deadlock) {
+ uids = keyuids(publickey, &primary);
+ }
+ if (uids != NULL) {
+ for (i = 0; ret == 0 && uids[i] != NULL; i++) {
+ wordlist = makewordlist(wordlist, uids[i]);
+ }
+
+ for (curword = wordlist; curword != NULL && !deadlock;
+ curword = curword->next) {
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = curword->object;
+ key.size = strlen(key.data);
+ data.data = worddb_data;
+ data.size = sizeof(worddb_data);
+
+ /*
+ * Our data is the key creation time followed by the
+ * key id.
+ */
+ worddb_data[ 0] = publickey->publickey->data[1];
+ worddb_data[ 1] = publickey->publickey->data[2];
+ worddb_data[ 2] = publickey->publickey->data[3];
+ worddb_data[ 3] = publickey->publickey->data[4];
+ worddb_data[ 4] = (keyid >> 56) & 0xFF;
+ worddb_data[ 5] = (keyid >> 48) & 0xFF;
+ worddb_data[ 6] = (keyid >> 40) & 0xFF;
+ worddb_data[ 7] = (keyid >> 32) & 0xFF;
+ worddb_data[ 8] = (keyid >> 24) & 0xFF;
+ worddb_data[ 9] = (keyid >> 16) & 0xFF;
+ worddb_data[10] = (keyid >> 8) & 0xFF;
+ worddb_data[11] = keyid & 0xFF;
+ ret = privctx->worddb->put(privctx->worddb,
+ privctx->txn,
+ &key,
+ &data,
+ 0);
+ if (ret != 0) {
+ logthing(LOGTHING_ERROR,
+ "Problem storing word: %s",
+ db_strerror(ret));
+ if (ret == DB_LOCK_DEADLOCK) {
+ deadlock = true;
+ }
+ }
+ }
+
+ /*
+ * Free our UID and word lists.
+ */
+ llfree(wordlist, NULL);
+ for (i = 0; uids[i] != NULL; i++) {
+ free(uids[i]);
+ uids[i] = NULL;
+ }
+ free(uids);
+ uids = NULL;
+ }
+
+ /*
+ * Write the truncated 32 bit keyid so we can lookup the full id for
+ * queries.
+ */
+ if (!deadlock) {
+ shortkeyid = keyid & 0xFFFFFFFF;
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = &shortkeyid;
+ key.size = sizeof(shortkeyid);
+ data.data = &keyid;
+ data.size = sizeof(keyid);
+
+ ret = privctx->id32db->put(privctx->id32db,
+ privctx->txn,
+ &key,
+ &data,
+ 0);
+ if (ret != 0) {
+ logthing(LOGTHING_ERROR,
+ "Problem storing short keyid: %s",
+ db_strerror(ret));
+ if (ret == DB_LOCK_DEADLOCK) {
+ deadlock = true;
+ }
+ }
+ }
+
+ if (!deadlock) {
+ subkeyids = keysubkeys(publickey);
+ i = 0;
+ while (subkeyids != NULL && subkeyids[i] != 0) {
+ /* Store the subkey ID -> main key ID mapping */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = &subkeyids[i];
+ key.size = sizeof(subkeyids[i]);
+ data.data = &keyid;
+ data.size = sizeof(keyid);
+
+ ret = privctx->subkeydb->put(privctx->subkeydb,
+ privctx->txn,
+ &key,
+ &data,
+ 0);
+ if (ret != 0) {
+ logthing(LOGTHING_ERROR,
+ "Problem storing subkey keyid: %s",
+ db_strerror(ret));
+ if (ret == DB_LOCK_DEADLOCK) {
+ deadlock = true;
+ }
+ }
+
+ /* Store the short subkey ID -> main key ID mapping */
+ shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = &shortkeyid;
+ key.size = sizeof(shortkeyid);
+ data.data = &keyid;
+ data.size = sizeof(keyid);
+
+ ret = privctx->id32db->put(privctx->id32db,
+ privctx->txn,
+ &key,
+ &data,
+ 0);
+ if (ret != 0) {
+ logthing(LOGTHING_ERROR,
+ "Problem storing short keyid: %s",
+ db_strerror(ret));
+ if (ret == DB_LOCK_DEADLOCK) {
+ deadlock = true;
+ }
+ }
+ }
+ if (subkeyids != NULL) {
+ free(subkeyids);
+ subkeyids = NULL;
+ }
+ }
+
+ if (!deadlock) {
+ get_skshash(publickey, &hash);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hash.hash;
+ key.size = sizeof(hash.hash);
+ data.data = &keyid;
+ data.size = sizeof(keyid);
+
+ ret = privctx->skshashdb->put(privctx->skshashdb,
+ privctx->txn,
+ &key,
+ &data,
+ 0);
+ if (ret != 0) {
+ logthing(LOGTHING_ERROR,
+ "Problem storing SKS hash: %s",
+ db_strerror(ret));
+ if (ret == DB_LOCK_DEADLOCK) {
+ deadlock = true;
+ }
+ }
+ }
+
+ if (!intrans) {
+ db4_endtrans(dbctx);
+ }
+
+ return deadlock ? -1 : 0 ;
+}
+
+/**
+ * iterate_keys - call a function once for each key in the db.
+ * @iterfunc: The function to call.
+ * @ctx: A context pointer
+ *
+ * Calls iterfunc once for each key in the database. ctx is passed
+ * unaltered to iterfunc. This function is intended to aid database dumps
+ * and statistic calculations.
+ *
+ * Returns the number of keys we iterated over.
+ */
+static int db4_iterate_keys(struct onak_dbctx *dbctx,
+ void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
+ void *ctx)
+{
+ struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+ DBT dbkey, data;
+ DBC *cursor = NULL;
+ int ret = 0;
+ int i = 0;