Files
dbtool/engine.cc

647 lines
16 KiB
C++
Raw Normal View History

2015-05-14 18:34:10 +02:00
/*
* 'dbtool' is a simple but powerful commandline interface to the
* GNU gdbm system (or, alternatively the Berkeley DB), useful for
* shellscripts which needs a database for data storage.
*/
/*
*
* This file is part of the DBTOOL program.
*
* By accessing this software, DBTOOL, you are duly informed
* of and agree to be bound by the conditions described below
* in this notice:
*
* This software product, DBTOOL, is developed by T.v. Dein
* and copyrighted (C) 2001-2015 by T.v. Dein, with all
* rights reserved.
*
* There is no charge for DBTOOL software. You can redistribute
* it and/or modify it under the terms of the GNU General Public
* License, which is incorporated by reference herein.
*
* DBTOOL is distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS,
* OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE or that
* the use of it will not infringe on any third party's intellec-
* tual property rights.
*
* You should have received a copy of the GNU General Public
* License along with DBTOOL. Copies can also be obtained from:
*
* http://www.gnu.org/licenses/gpl.txt
*
* or by writing to:
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307
* USA
*
* Or contact:
*
* "T.v. Dein" <tlinden@cpan.org>
*
*
*/
#include "dbtool.h"
#include "engine.h"
#include "platform.h"
/*
* Initialize the database and get a new Db instance pointer
*/
void Engine::init() {
pkg = PACKAGE;
int mode;
#ifdef HAVE_BERKELEY
int err;
#endif
if(config.filename == "") {
cerr << pkg << ": no database file specified!" << endl;
exit(1);
}
#ifdef HAVE_BERKELEY
db = new Db(NULL,0);
db->set_error_stream(&cerr);
db->set_errpfx(pkg.c_str());
if(config.readonly == 1)
mode = DB_RDONLY;
else
mode = DB_CREATE;
#else
if(config.readonly == 1)
mode = GDBM_READER;
else
mode = GDBM_WRCREAT;
#endif
if(config.force == 1) {
#ifdef HAVE_BERKELEY
if ((err = db->open(NULL, config.filename.c_str(), NULL, DB_HASH, mode, FILEMODE)) != 0) {
cerr << "Failed to open " + config.filename << "(" << strerror(err) << ")" << endl;
exit(1);
}
2025-11-21 00:33:11 +01:00
#else
2015-05-14 18:34:10 +02:00
db = gdbm_open(
2025-11-21 00:33:11 +01:00
(char *)config.filename.c_str(),
BLOCKSIZE,
mode,
FILEMODE,
0
);
2015-05-14 18:34:10 +02:00
#endif
}
else {
#ifdef HAVE_BERKELEY
if ((err = db->open(NULL, config.filename.c_str(), NULL, DB_HASH, mode, FILEMODE)) != 0) {
cerr << "Failed to open " + config.filename << "(" << strerror(err) << ")" << endl;
exit(1);
}
#else
db = gdbm_open(
2025-11-21 00:33:11 +01:00
(char *)config.filename.c_str(),
BLOCKSIZE,
mode,
FILEMODE,
0
);
2015-05-14 18:34:10 +02:00
#endif
}
#ifndef HAVE_BERKELEY
if(!db) {
cerr << pkg << ": " << gdbm_strerror(gdbm_errno) << endl;
exit(1);
}
#endif
/*
* ok, at this point everything is ok, usage is correct,
* database is open, now check the passphrase, if encrypted
* database requested.
*/
if(config.encrypted) {
if(config.phrase == "") {
config.phrase = readpass();
}
rijn.init(config.phrase); /* this might fail */
config.phrase = " ";
}
}
/*
* dump out all data in db
*/
void Engine::dump() {
init();
#ifdef HAVE_BERKELEY
try {
Dbc *dbcp;
db->cursor(NULL, &dbcp, 0);
Dbt key;
Dbt data;
while (dbcp->get(&key, &data, DB_NEXT) == 0) {
/* iterate over every tuple and dump it out */
string K((char *)key.get_data(), key.get_size());
string V((char *)data.get_data(), data.get_size());
if(config.reverse == 1) {
2025-11-21 00:33:11 +01:00
cout << decode(V) << config.separator << K << endl;
2015-05-14 18:34:10 +02:00
}
else {
2025-11-21 00:33:11 +01:00
cout << K << config.separator << decode(V) << endl;
2015-05-14 18:34:10 +02:00
}
}
dbcp->close();
}
catch (DbException &dbe) {
cerr << pkg << ": " << dbe.what() << "\n";
}
db->close(0);
#else
datum key;
datum value;
key = gdbm_firstkey(db);
while ( key.dptr != NULL ) {
/* iterate over every tuple and dump it out */
2025-11-21 00:33:11 +01:00
value = gdbm_fetch(db, key);
2015-05-14 18:34:10 +02:00
string K(key.dptr, key.dsize);
string V(value.dptr, value.dsize);
if(config.reverse == 1) {
cout << decode(V) << config.separator << K << endl;
2025-11-21 00:33:11 +01:00
}
2015-05-14 18:34:10 +02:00
else {
cout << K << config.separator << decode(V) << endl;
}
key = gdbm_nextkey(db,key);
}
gdbm_close(db);
#endif
}
/*
* search for regexp given in config.key
*/
void Engine::regexp() {
init();
int num;
int sub_len = 9;
int *sub_vec;
2025-11-21 00:33:11 +01:00
int errnumber;
PCRE2_SIZE erroffset;
pcre2_match_data *match_data;
pcre2_code *p_pcre = pcre2_compile(
(PCRE2_SPTR)config.key.c_str(), /* the pattern */
PCRE2_ZERO_TERMINATED, /* indicates pattern is zero-terminated */
0, /* default options */
&errnumber, /* for error number */
&erroffset, /* for error offset */
NULL); /* use default compile context */
if (p_pcre == NULL) {
PCRE2_UCHAR buffer[256];
pcre2_get_error_message(errnumber, buffer, sizeof(buffer));
cerr << "PCRE2 compilation failed at offset: " << erroffset << " with: " << buffer << endl;
exit(1);
}
match_data = pcre2_match_data_create_from_pattern(p_pcre, NULL);
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
Dbc *dbcp;
db->cursor(NULL, &dbcp, 0);
Dbt key;
Dbt data;
while (dbcp->get(&key, &data, DB_NEXT) == 0) {
string K((char *)key.get_data(), key.get_size());
string V((char *)data.get_data(), data.get_size());
2015-05-14 18:34:10 +02:00
#else
datum key;
datum value;
key = gdbm_firstkey(db);
while ( key.dptr != NULL ) {
value = gdbm_fetch(db, key);
string K(key.dptr, key.dsize);
string V(value.dptr, value.dsize);
#endif
2025-11-21 00:33:11 +01:00
2015-05-14 18:34:10 +02:00
sub_vec = new int(sub_len);
2025-11-21 00:33:11 +01:00
num = pcre2_match(p_pcre, (PCRE2_SPTR)K.c_str(), (PCRE2_SIZE)K.length(), 0, 0, match_data, NULL);
2015-05-14 18:34:10 +02:00
if(num == 1){
2025-11-21 00:33:11 +01:00
if(config.reverse == 1) {
cout << decode(V);
if(config.with == 1) {
cout << config.separator << K;
}
cout << endl;
}
else {
if(config.with == 1) {
cout << K << config.separator;
}
cout << decode(V) << endl;
}
2015-05-14 18:34:10 +02:00
}
K = "";
V = "";
delete(sub_vec);
#ifndef HAVE_BERKELEY
key = gdbm_nextkey(db,key);
#endif
}
2025-11-21 00:33:11 +01:00
pcre2_match_data_free(match_data);
pcre2_code_free(p_pcre);
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
dbcp->close();
db->close(0);
#else
2025-11-21 00:33:11 +01:00
gdbm_close(db);
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
}
2015-05-14 18:34:10 +02:00
2025-11-21 00:33:11 +01:00
/*
* Insert data into the db
*/
void Engine::from_input() {
init();
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
int err;
2015-05-14 18:34:10 +02:00
#else
2025-11-21 00:33:11 +01:00
int ret;
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
FILE *stream;
stream = fdopen(0, "r");
char c;
string mode = "key";
string key = "";
string value = "";
string line = "";
int num;
int errnumber;
PCRE2_SIZE erroffset;
pcre2_match_data *match_data;
PCRE2_SIZE *ovector;
pcre2_code *p_pcre = pcre2_compile(
(PCRE2_SPTR)config.token.c_str(),
PCRE2_ZERO_TERMINATED,
0,
&errnumber,
&erroffset,
NULL);
if (p_pcre == NULL) {
PCRE2_UCHAR buffer[256];
pcre2_get_error_message(errnumber, buffer, sizeof(buffer));
cerr << "PCRE2 compilation failed at offset: " << erroffset << " with: " << buffer << endl;
exit(1);
}
match_data = pcre2_match_data_create_from_pattern(p_pcre, NULL);
2015-05-14 18:34:10 +02:00
while((c = fgetc(stream)) != EOF) {
2025-11-21 00:33:11 +01:00
if(c == '\n') {
// record end
mode = "key";
if(config.token != "") {
2025-11-21 14:06:32 +01:00
string *subs = new string[2];
2025-11-21 00:33:11 +01:00
num = pcre2_match(p_pcre, (PCRE2_SPTR)line.c_str(), (PCRE2_SIZE)line.length(), 0, 0, match_data, NULL);
if(num < 0)
cerr << "Token \"" << config.token << "\" did not match on \"" << line << "\"!\n";
else if(num == 1)
cerr << "Token " << config.token << " did not produce sub strings!\n";
else {
ovector = pcre2_get_ovector_pointer(match_data);
2025-11-21 14:06:32 +01:00
const char *constline = const_cast<char*>(line.c_str());
2025-11-21 00:33:11 +01:00
2025-11-21 14:06:32 +01:00
for (int i = 1; i < num; i++) {
PCRE2_SPTR substring_start = (PCRE2_SPTR8)constline + ovector[2*i];
PCRE2_SIZE substring_length = ovector[2*i+1] - ovector[2*i];
2025-11-21 00:33:11 +01:00
2025-11-21 14:06:32 +01:00
char *part = (char *)malloc(substring_length+1);
part = strncpy(part, (char *)substring_start, substring_length);
part[substring_length] = '\0';
2025-11-21 00:33:11 +01:00
2025-11-21 14:06:32 +01:00
subs[i-1] = part;
free(part);
}
2025-11-21 00:33:11 +01:00
if(config.reverse) {
value = subs[0];
key = subs[1];
}
else {
value = subs[1];
key = subs[0];
}
}
2025-11-21 14:06:32 +01:00
delete[] subs;
2025-11-21 00:33:11 +01:00
line = "";
}
value = encode(value);
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
Dbt d_key((char *)key.c_str(), key.length());
Dbt d_value((char *)value.c_str(), value.length());
2015-05-14 18:34:10 +02:00
#else
2025-11-21 00:33:11 +01:00
datum d_key = {(char *)key.c_str(), key.length()};
datum d_value = {(char *)value.c_str(), value.length()};
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
if(config.force == 1) {
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
if((err = db->put(0, &d_key, &d_value, 0)) != 0) {
cerr << "Database error" << "(" << strerror(err) << ")" << endl;
exit(1);
}
2015-05-14 18:34:10 +02:00
#else
2025-11-21 00:33:11 +01:00
ret = gdbm_store(db, d_key, d_value, GDBM_REPLACE);
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
}
else {
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
if((err = db->put(NULL, &d_key, &d_value, DB_NOOVERWRITE)) != 0) {
if(err == DB_KEYEXIST) {
cerr << "Key " + config.key + " already exists" << "(" << strerror(err) << ")" << endl;
exit(1);
}
else {
cerr << "Database error" << "(" << strerror(err) << ")" << endl;
exit(1);
}
}
2015-05-14 18:34:10 +02:00
#else
2025-11-21 00:33:11 +01:00
ret = gdbm_store(db, d_key, d_value, GDBM_INSERT);
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
}
2015-05-14 18:34:10 +02:00
#ifndef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
if(ret != 0) {
cerr << pkg << ": " << gdbm_strerror(gdbm_errno) << endl;
exit(1);
}
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
key = "";
value = "";
continue;
}
else if(c == config.separator && mode != "value" && config.token == "") {
// key ready, the following stuff is the data!
mode = "value";
continue;
}
if(config.token != "") {
/* we have a split token */
line += char(c);
}
else {
if(mode == "key")
key += char(c);
else
value += char(c);
}
2015-05-14 18:34:10 +02:00
}
2025-11-21 00:33:11 +01:00
pcre2_match_data_free(match_data);
pcre2_code_free(p_pcre);
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
db->close(0);
2015-05-14 18:34:10 +02:00
#else
2025-11-21 00:33:11 +01:00
gdbm_close(db);
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
}
2015-05-14 18:34:10 +02:00
2025-11-21 00:33:11 +01:00
/*
* Insert data into the db
*/
void Engine::insert() {
init();
string __value;
__value = encode(config.value);
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
int err;
char *k;
char *v;
k = (char *)config.key.c_str();
v = (char *)__value.c_str();
Dbt key(k, strlen(k));
Dbt value(v, strlen(v));
2015-05-14 18:34:10 +02:00
#else
2025-11-21 00:33:11 +01:00
int ret;
datum key = {(char *)config.key.c_str(), config.key.length()};
datum value = {(char *)__value.c_str(), __value.length()};
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
if(config.force == 1) {
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
if((err = db->put(0, &key, &value, 0)) != 0) {
cerr << "Database error" << "(" << strerror(err) << ")" << endl;
exit(1);
}
2015-05-14 18:34:10 +02:00
#else
2025-11-21 00:33:11 +01:00
ret = gdbm_store(db, key, value, GDBM_REPLACE);
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
}
else {
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
if((err = db->put(NULL, &key, &value, DB_NOOVERWRITE)) != 0) {
if(err == DB_KEYEXIST) {
cerr << "Key " + config.key + " already exists" << "(" << strerror(err) << ")" << endl;
exit(1);
}
else {
cerr << "Database error" << "(" << strerror(err) << ")" << endl;
exit(1);
}
2015-05-14 18:34:10 +02:00
}
#else
2025-11-21 00:33:11 +01:00
ret = gdbm_store(db, key, value, GDBM_INSERT);
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
}
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
db->close(0);
2015-05-14 18:34:10 +02:00
#else
2025-11-21 00:33:11 +01:00
if(ret != 0) {
cerr << pkg << ": " << gdbm_strerror(gdbm_errno) << endl;
exit(1);
}
gdbm_close(db);
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
}
2015-05-14 18:34:10 +02:00
2025-11-21 00:33:11 +01:00
/*
* update a database
*/
void Engine::update() {
init();
string __value;
__value = encode(config.value);
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
int err;
char *k;
char *v;
k = (char *)config.key.c_str();
v = (char *)__value.c_str();
Dbt key(k, strlen(k));
Dbt value(v, strlen(v));
2015-05-14 18:34:10 +02:00
#else
2025-11-21 00:33:11 +01:00
int ret;
datum key = {(char *)config.key.c_str(), config.key.length()};
datum value = {(char *)__value.c_str(), __value.length()};
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
if(config.force == 1) {
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
if((err = db->put(0, &key, &value, 0)) != 0) {
2025-11-21 00:33:11 +01:00
cerr << "Database error" << "(" << strerror(err) << ")" << endl;
exit(1);
2015-05-14 18:34:10 +02:00
}
#else
ret = gdbm_store(db, key, value, GDBM_REPLACE);
2025-11-21 00:33:11 +01:00
#endif
2015-05-14 18:34:10 +02:00
}
else {
2025-11-21 00:33:11 +01:00
#ifdef HAVE_BERKELEY
Dbt val;
err = db->get(0, &key, &val, 0);
if(err == 0) {
/* key exists, do the update */
if((err = db->put(0, &key, &value, 0)) != 0) {
cerr << "Database error" << "(" << strerror(err) << ")" << endl;
exit(1);
}
}
else if(err == DB_NOTFOUND) {
cerr << "Key " << config.key << " does not exist\n";
exit(1);
}
else {
cerr << "Database error" << "(" << strerror(err) << ")" << endl;
exit(1);
}
#else
ret = gdbm_exists(db, key);
if(ret) {
ret = gdbm_store(db, key, value, GDBM_REPLACE);
}
else {
cerr << "Key " << config.key << " does not exist\n";
exit(1);
}
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
}
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
db->close(0);
2015-05-14 18:34:10 +02:00
#else
2025-11-21 00:33:11 +01:00
if(ret != 0) {
cerr << pkg << ": " << gdbm_strerror(gdbm_errno) << endl;
exit(1);
}
gdbm_close(db);
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
}
2015-05-14 18:34:10 +02:00
2025-11-21 00:33:11 +01:00
/*
* remove an entry
*/
void Engine::remove() {
init();
2025-11-21 14:06:32 +01:00
#ifdef HAVE_BERKELEY
char *k = (char *)config.key.c_str();
Dbt key(k, strlen(k));
2025-11-21 00:33:11 +01:00
int ret;
if((ret = db->del(NULL, &key, 0)) != 0) {
cerr << "Database error" << "(" << strerror(ret) << ")" << endl;
exit(1);
}
db->close(0);
2015-05-14 18:34:10 +02:00
#else
2025-11-21 00:33:11 +01:00
datum key = {(char *)config.key.c_str(), config.key.length()};
int ret;
if((ret = gdbm_delete(db, key)) != 0) {
cerr << "Database error" << "(" << strerror(ret) << ")" << endl;
exit(1);
}
gdbm_close(db);
2015-05-14 18:34:10 +02:00
#endif
2025-11-21 00:33:11 +01:00
}
2015-05-14 18:34:10 +02:00
2025-11-21 00:33:11 +01:00
/*
* search for specific data
*/
void Engine::select() {
init();
2015-05-14 18:34:10 +02:00
#ifdef HAVE_BERKELEY
2025-11-21 00:33:11 +01:00
int err;
char *k;
k = (char *)config.key.c_str();
Dbt key(k, strlen(k));
Dbt value;
err = db->get(0, &key, &value, 0);
if(err == 0) {
string gotvalue((char *)value.get_data(), value.get_size());
if(config.reverse == 1) {
cout << decode(gotvalue);
if(config.with == 1) {
cout << config.separator << config.key;
}
cout << endl;
}
else {
if(config.with == 1) {
cout << config.key << config.separator;
}
cout << decode(gotvalue) << endl;
2015-05-14 18:34:10 +02:00
}
}
else {
2025-11-21 00:33:11 +01:00
cerr << "Database error" << "(" << strerror(err) << ")" << endl;
exit(1);
2015-05-14 18:34:10 +02:00
}
2025-11-21 00:33:11 +01:00
db->close(0);
2015-05-14 18:34:10 +02:00
#else
2025-11-21 00:33:11 +01:00
datum content;
datum key = {(char *)config.key.c_str(), config.key.length()};
content = gdbm_fetch(db, key);
string V(content.dptr, content.dsize);
if(config.with == 1)
cout << config.key << config.separator;
cout << decode(V) << endl;
gdbm_close(db);
2015-05-14 18:34:10 +02:00
#endif
}
2025-11-21 00:33:11 +01:00
string Engine::encode(const string& data) {
if(config.encrypted) {
return rijn.encrypt(data);
}
else
return data;
2015-05-14 18:34:10 +02:00
}
2025-11-21 00:33:11 +01:00
string Engine::decode(const string& data) {
if(config.encrypted) {
return rijn.decrypt(data);
}
else
return data;
}