Source: encrypt/sqlite3-consumer-db.js

/**
 * Copyright (C) 2015-2016 Regents of the University of California.
 * @author: Jeff Thompson <jefft0@remap.ucla.edu>
 * @author: From ndn-group-encrypt src/consumer-db https://github.com/named-data/ndn-group-encrypt
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * A copy of the GNU Lesser General Public License is in the file COPYING.
 */

/** @ignore */
var Blob = require('../util/blob.js').Blob; /** @ignore */
var Sqlite3Promise = require('../util/sqlite3-promise.js').Sqlite3Promise; /** @ignore */
var TlvWireFormat = require('../encoding/tlv-wire-format').TlvWireFormat; /** @ignore */
var SyncPromise = require('../util/sync-promise.js').SyncPromise; /** @ignore */
var ConsumerDb = require('./consumer-db.js').ConsumerDb;

/**
 * Sqlite3ConsumerDb extends ConsumerDb to implement the storage of decryption
 * keys for the consumer using SQLite3.
 * Create a Sqlite3ConsumerDb to use the given SQLite3 file.
 * @param {string} databaseFilePath The path of the SQLite file.
 * @throws ConsumerDb.Error for a database error.
 * @note This class is an experimental feature. The API may change.
 * @constructor
 */
var Sqlite3ConsumerDb = function Sqlite3ConsumerDb(databaseFilePath)
{
  // Call the base constructor.
  ConsumerDb.call(this);

  this.database_ = new Sqlite3Promise
    (databaseFilePath, Sqlite3ConsumerDb.initializeDatabasePromise_);
};

Sqlite3ConsumerDb.prototype = new ConsumerDb();
Sqlite3ConsumerDb.prototype.name = "Sqlite3ConsumerDb";

exports.Sqlite3ConsumerDb = Sqlite3ConsumerDb;

/**
 * Get the key with keyName from the database.
 * @param {Name} keyName The key name.
 * @param {boolean} useSync (optional) If true then return a rejected promise
 * since this only supports async code.
 * @return {Promise} A promise that returns a Blob with the encoded key (or an
 * isNull Blob if cannot find the key with keyName), or that is
 * rejected with ConsumerDb.Error for a database error.
 */
Sqlite3ConsumerDb.prototype.getKeyPromise = function(keyName, useSync)
{
  if (useSync)
    return Promise.reject(new ConsumerDb.Error(new Error
      ("Sqlite3ConsumerDb.getKey is only supported for async")));

  return this.getPromise_
    ("SELECT key_buf FROM decryptionkeys WHERE key_name=?",
     keyName.wireEncode(TlvWireFormat.get()).buf())
  .then(function(row) {
    if (row)
      return Promise.resolve(new Blob(row.key_buf, false));
    else
      return Promise.resolve(new Blob());
  });
};

/**
 * Add the key with keyName and keyBlob to the database.
 * @param {Name} keyName The key name.
 * @param {Blob} keyBlob The encoded key.
 * @param {boolean} useSync (optional) If true then return a rejected promise
 * since this only supports async code.
 * @return {Promise} A promise that fulfills when the key is added, or that
 * is rejected with ConsumerDb.Error if a key with the same keyName already
 * exists, or other database error.
 */
Sqlite3ConsumerDb.prototype.addKeyPromise = function(keyName, keyBlob, useSync)
{
  if (useSync)
    return Promise.reject(new ConsumerDb.Error(new Error
      ("Sqlite3ConsumerDb.addKey is only supported for async")));

  return this.runPromise_
    ("INSERT INTO decryptionkeys(key_name, key_buf) values (?, ?)",
     [keyName.wireEncode(TlvWireFormat.get()).buf(), keyBlob.buf()]);
};

/**
 * Delete the key with keyName from the database. If there is no key with
 * keyName, do nothing.
 * @param {Name} keyName The key name.
 * @param {boolean} useSync (optional) If true then return a rejected promise
 * since this only supports async code.
 * @return {Promise} A promise that fulfills when the key is deleted (or there
 * is no such key), or that is rejected with ConsumerDb.Error for a database
 * error.
 */
Sqlite3ConsumerDb.prototype.deleteKeyPromise = function(keyName, useSync)
{
  if (useSync)
    return Promise.reject(new ConsumerDb.Error(new Error
      ("Sqlite3ConsumerDb.deleteKey is only supported for async")));

  return this.runPromise_
    ("DELETE FROM decryptionkeys WHERE key_name=?",
     keyName.wireEncode(TlvWireFormat.get()).buf());
};

/**
 * Call Sqlite3Promise.runPromise, wrapping an Error in ConsumerDb.Error.
 */
Sqlite3ConsumerDb.prototype.runPromise_ = function(sql, params)
{
  return this.database_.runPromise(sql, params)
  .catch(function(error) {
    return Promise.reject(new ConsumerDb.Error(error));
  });
};

/**
 * Call Sqlite3Promise.getPromise, wrapping an Error in ConsumerDb.Error.
 */
Sqlite3ConsumerDb.prototype.getPromise_ = function(sql, params)
{
  return this.database_.getPromise(sql, params)
  .catch(function(error) {
    return Promise.reject(new ConsumerDb.Error(error));
  });
};

Sqlite3ConsumerDb.initializeDatabasePromise_ = function(database)
{
  return database.runPromise(Sqlite3ConsumerDb.INITIALIZATION1)
  .then(function() {
    return database.runPromise(Sqlite3ConsumerDb.INITIALIZATION2);
  });
};

Sqlite3ConsumerDb.INITIALIZATION1 =
  "CREATE TABLE IF NOT EXISTS                         \n" +
  "  decryptionkeys(                                  \n" +
  "    key_id              INTEGER PRIMARY KEY,       \n" +
  "    key_name            BLOB NOT NULL,             \n" +
  "    key_buf             BLOB NOT NULL              \n" +
  "  );                                               \n";
Sqlite3ConsumerDb.INITIALIZATION2 =
  "CREATE UNIQUE INDEX IF NOT EXISTS                  \n" +
  "   KeyNameIndex ON decryptionkeys(key_name);       \n";