1 /*
2 * 'dbtool' is a simple but powerful commandline interface to the
3 * GNU gdbm system (or, alternatively the Berkeley DB), useful for
4 * shellscripts which needs a database for data storage.
5 */
6
7 /*
8 *
9 * This file is part of the DBTOOL program.
10 *
11 * By accessing this software, DBTOOL, you are duly informed
12 * of and agree to be bound by the conditions described below
13 * in this notice:
14 *
15 * This software product, DBTOOL, is developed by T.v. Dein
16 * and copyrighted (C) 2001-2015 by T.v. Dein, with all
17 * rights reserved.
18 *
19 * There is no charge for DBTOOL software. You can redistribute
20 * it and/or modify it under the terms of the GNU General Public
21 * License, which is incorporated by reference herein.
22 *
23 * DBTOOL is distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS,
24 * OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE or that
25 * the use of it will not infringe on any third party's intellec-
26 * tual property rights.
27 *
28 * You should have received a copy of the GNU General Public
29 * License along with DBTOOL. Copies can also be obtained from:
30 *
31 * http://www.gnu.org/licenses/gpl.txt
32 *
33 * or by writing to:
34 *
35 * Free Software Foundation, Inc.
36 * 59 Temple Place, Suite 330
37 * Boston, MA 02111-1307
38 * USA
39 *
40 * Or contact:
41 *
42 * "T.v. Dein" <tlinden@cpan.org>
43 *
44 *
45 */
46
47
48
49
50 #include "dbtool.h"
51 #include "engine.h"
52 #include "platform.h"
53 /*
54 * Initialize the database and get a new Db instance pointer
55 */
init()56 void Engine::init() {
57 pkg = PACKAGE;
58 int mode;
59 #ifdef HAVE_BERKELEY
60 int err;
61 #endif
62 if(config.filename == "") {
63 cerr << pkg << ": no database file specified!" << endl;
64 exit(1);
65 }
66 #ifdef HAVE_BERKELEY
67 db = new Db(NULL,0);
68 db->set_error_stream(&cerr);
69 db->set_errpfx(pkg.c_str());
70 if(config.readonly == 1)
71 mode = DB_RDONLY;
72 else
73 mode = DB_CREATE;
74 #else
75 if(config.readonly == 1)
76 mode = GDBM_READER;
77 else
78 mode = GDBM_WRCREAT;
79 #endif
80
81
82 if(config.force == 1) {
83 #ifdef HAVE_BERKELEY
84 if ((err = db->open(NULL, config.filename.c_str(), NULL, DB_HASH, mode, FILEMODE)) != 0) {
85 cerr << "Failed to open " + config.filename << "(" << strerror(err) << ")" << endl;
86 exit(1);
87 }
88 #else
89
90 db = gdbm_open(
91 (char *)config.filename.c_str(),
92 BLOCKSIZE,
93 mode,
94 FILEMODE,
95 0
96 );
97 #endif
98 }
99 else {
100 #ifdef HAVE_BERKELEY
101 if ((err = db->open(NULL, config.filename.c_str(), NULL, DB_HASH, mode, FILEMODE)) != 0) {
102 cerr << "Failed to open " + config.filename << "(" << strerror(err) << ")" << endl;
103 exit(1);
104 }
105 #else
106 db = gdbm_open(
107 (char *)config.filename.c_str(),
108 BLOCKSIZE,
109 mode,
110 FILEMODE,
111 0
112 );
113 #endif
114 }
115 #ifndef HAVE_BERKELEY
116 if(!db) {
117 cerr << pkg << ": " << gdbm_strerror(gdbm_errno) << endl;
118 exit(1);
119 }
120 #endif
121
122 /*
123 * ok, at this point everything is ok, usage is correct,
124 * database is open, now check the passphrase, if encrypted
125 * database requested.
126 */
127 if(config.encrypted) {
128 if(config.phrase == "") {
129 config.phrase = readpass();
130 }
131 rijn.init(config.phrase); /* this might fail */
132 config.phrase = " ";
133 }
134 }
135
136
137 /*
138 * dump out all data in db
139 */
dump()140 void Engine::dump() {
141 init();
142 #ifdef HAVE_BERKELEY
143 try {
144 Dbc *dbcp;
145 db->cursor(NULL, &dbcp, 0);
146 Dbt key;
147 Dbt data;
148 while (dbcp->get(&key, &data, DB_NEXT) == 0) {
149 /* iterate over every tuple and dump it out */
150 string K((char *)key.get_data(), key.get_size());
151 string V((char *)data.get_data(), data.get_size());
152 if(config.reverse == 1) {
153 cout << decode(V) << config.separator << K << endl;
154 }
155 else {
156 cout << K << config.separator << decode(V) << endl;
157 }
158 }
159 dbcp->close();
160 }
161 catch (DbException &dbe) {
162 cerr << pkg << ": " << dbe.what() << "\n";
163 }
164 db->close(0);
165 #else
166 datum key;
167 datum value;
168 key = gdbm_firstkey(db);
169 while ( key.dptr != NULL ) {
170 /* iterate over every tuple and dump it out */
171 value = gdbm_fetch(db, key);
172 string K(key.dptr, key.dsize);
173 string V(value.dptr, value.dsize);
174 if(config.reverse == 1) {
175 cout << decode(V) << config.separator << K << endl;
176 }
177 else {
178 cout << K << config.separator << decode(V) << endl;
179 }
180 key = gdbm_nextkey(db,key);
181 }
182 gdbm_close(db);
183 #endif
184 }
185
186
187
188
189 /*
190 * search for regexp given in config.key
191 */
regexp()192 void Engine::regexp() {
193 init();
194 int num;
195 pcre *p_pcre;
196 pcre_extra *p_pcre_extra;
197 char *err_str;
198 int sub_len = 9;
199 int *sub_vec;
200 int erroffset;
201 p_pcre_extra = NULL;
202 p_pcre = pcre_compile((char *)config.key.c_str(), 0,
203 (const char **)(&err_str), &erroffset, NULL);
204 #ifdef HAVE_BERKELEY
205 Dbc *dbcp;
206 db->cursor(NULL, &dbcp, 0);
207 Dbt key;
208 Dbt data;
209 while (dbcp->get(&key, &data, DB_NEXT) == 0) {
210 string K((char *)key.get_data(), key.get_size());
211 string V((char *)data.get_data(), data.get_size());
212 #else
213 datum key;
214 datum value;
215 key = gdbm_firstkey(db);
216 while ( key.dptr != NULL ) {
217 value = gdbm_fetch(db, key);
218 string K(key.dptr, key.dsize);
219 string V(value.dptr, value.dsize);
220 #endif
221 sub_vec = new int(sub_len);
222 num = pcre_exec(p_pcre, p_pcre_extra, (char *)K.c_str(),
223 (int)K.length(), 0, 0, (int *)sub_vec, sub_len);
224 if(num == 1){
225 if(config.reverse == 1) {
226 cout << decode(V);
227 if(config.with == 1) {
228 cout << config.separator << K;
229 }
230 cout << endl;
231 }
232 else {
233 if(config.with == 1) {
234 cout << K << config.separator;
235 }
236 cout << decode(V) << endl;
237 }
238 }
239 K = "";
240 V = "";
241 delete(sub_vec);
242 #ifndef HAVE_BERKELEY
243 key = gdbm_nextkey(db,key);
244 #endif
245 }
246 pcre_free(p_pcre);
247 #ifdef HAVE_BERKELEY
248 dbcp->close();
249 db->close(0);
250 #else
251 gdbm_close(db);
252 #endif
253 }
254
255
256
257
258
259
260 /*
261 * Insert data into the db
262 */
263 void Engine::from_input() {
264 init();
265 #ifdef HAVE_BERKELEY
266 int err;
267 #else
268 int ret;
269 #endif
270
271 FILE *stream;
272 stream = fdopen(0, "r");
273 char c;
274 string mode = "key";
275 string key = "";
276 string value = "";
277 string line = "";
278
279 int num, match;
280 pcre *p_pcre;
281 pcre_extra *p_pcre_extra;
282 char *err_str;
283 int sub_len = 9;
284 int *sub_vec;
285 char *v1;
286 char *v2;
287 int erroffset;
288 p_pcre_extra = NULL;
289 p_pcre = pcre_compile((char *)config.token.c_str(), 0,
290 (const char **)(&err_str), &erroffset, NULL);
291 while((c = fgetc(stream)) != EOF) {
292 if(c == '\n') {
293 // record end
294 mode = "key";
295 if(config.token != "") {
296 v1 = new char[line.length()];
297 v2 = new char[line.length()];
298 sub_vec = new int[sub_len];
299 num = pcre_exec(p_pcre, p_pcre_extra, (char *)line.c_str(),
300 (int)line.length(), 0, 0, (int *)sub_vec, sub_len);
301 if(num < 0)
302 cerr << "Token \"" << config.token << "\" did not match on \"" << line << "\"!\n";
303 else if(num == 1)
304 cerr << "Token " << config.token << " did not produce sub strings!\n";
305 else {
306 match = pcre_copy_substring((char *)line.c_str(), sub_vec, num, 1, v1, line.length());
307 match = pcre_copy_substring((char *)line.c_str(), sub_vec, num, 2, v2, line.length());
308 if(config.reverse) {
309 value = v1;
310 key = v2;
311 }
312 else {
313 value = v2;
314 key = v1;
315 }
316 }
317 delete(sub_vec);
318 pcre_free((void *)v1);
319 pcre_free((void *)v2);
320 line = "";
321 }
322 value = encode(value);
323 #ifdef HAVE_BERKELEY
324 Dbt d_key((char *)key.c_str(), key.length());
325 Dbt d_value((char *)value.c_str(), value.length());
326 #else
327 datum d_key = {(char *)key.c_str(), static_cast<int>(key.length())};
328 datum d_value = {(char *)value.c_str(), static_cast<int>(value.length())};
329 #endif
330 if(config.force == 1) {
331 #ifdef HAVE_BERKELEY
332 if((err = db->put(0, &d_key, &d_value, 0)) != 0) {
333 cerr << "Database error" << "(" << strerror(err) << ")" << endl;
334 exit(1);
335 }
336 #else
337 ret = gdbm_store(db, d_key, d_value, GDBM_REPLACE);
338 #endif
339 }
340 else {
341 #ifdef HAVE_BERKELEY
342 if((err = db->put(NULL, &d_key, &d_value, DB_NOOVERWRITE)) != 0) {
343 if(err == DB_KEYEXIST) {
344 cerr << "Key " + config.key + " already exists" << "(" << strerror(err) << ")" << endl;
345 exit(1);
346 }
347 else {
348 cerr << "Database error" << "(" << strerror(err) << ")" << endl;
349 exit(1);
350 }
351 }
352 #else
353 ret = gdbm_store(db, d_key, d_value, GDBM_INSERT);
354 #endif
355 }
356 #ifndef HAVE_BERKELEY
357 if(ret != 0) {
358 cerr << pkg << ": " << gdbm_strerror(gdbm_errno) << endl;
359 exit(1);
360 }
361 #endif
362 key = "";
363 value = "";
364 continue;
365 }
366 else if(c == config.separator && mode != "value" && config.token == "") {
367 // key ready, the following stuff is the data!
368 mode = "value";
369 continue;
370 }
371 if(config.token != "") {
372 /* we have a split token */
373 line += char(c);
374 }
375 else {
376 if(mode == "key")
377 key += char(c);
378 else
379 value += char(c);
380 }
381 }
382 pcre_free(p_pcre);
383 #ifdef HAVE_BERKELEY
384 db->close(0);
385 #else
386 gdbm_close(db);
387 #endif
388 }
389
390
391 /*
392 * Insert data into the db
393 */
394 void Engine::insert() {
395 init();
396 string __value;
397 __value = encode(config.value);
398 #ifdef HAVE_BERKELEY
399 int err;
400 char *k;
401 char *v;
402 k = (char *)config.key.c_str();
403 v = (char *)__value.c_str();
404 Dbt key(k, strlen(k));
405 Dbt value(v, strlen(v));
406 #else
407 int ret;
408 datum key = {(char *)config.key.c_str(), static_cast<int>(config.key.length())};
409 datum value = {(char *)__value.c_str(), static_cast<int>(__value.length())};
410 #endif
411
412 if(config.force == 1) {
413 #ifdef HAVE_BERKELEY
414 if((err = db->put(0, &key, &value, 0)) != 0) {
415 cerr << "Database error" << "(" << strerror(err) << ")" << endl;
416 exit(1);
417 }
418 #else
419 ret = gdbm_store(db, key, value, GDBM_REPLACE);
420 #endif
421 }
422 else {
423 #ifdef HAVE_BERKELEY
424 if((err = db->put(NULL, &key, &value, DB_NOOVERWRITE)) != 0) {
425 if(err == DB_KEYEXIST) {
426 cerr << "Key " + config.key + " already exists" << "(" << strerror(err) << ")" << endl;
427 exit(1);
428 }
429 else {
430 cerr << "Database error" << "(" << strerror(err) << ")" << endl;
431 exit(1);
432 }
433 }
434 #else
435 ret = gdbm_store(db, key, value, GDBM_INSERT);
436 #endif
437 }
438 #ifdef HAVE_BERKELEY
439 db->close(0);
440 #else
441 if(ret != 0) {
442 cerr << pkg << ": " << gdbm_strerror(gdbm_errno) << endl;
443 exit(1);
444 }
445 gdbm_close(db);
446 #endif
447 }
448
449
450
451 /*
452 * update a database
453 */
454 void Engine::update() {
455 init();
456 string __value;
457 __value = encode(config.value);
458 #ifdef HAVE_BERKELEY
459 int err;
460 char *k;
461 char *v;
462 k = (char *)config.key.c_str();
463 v = (char *)__value.c_str();
464 Dbt key(k, strlen(k));
465 Dbt value(v, strlen(v));
466 #else
467 int ret;
468 datum key = {(char *)config.key.c_str(), static_cast<int>(config.key.length())};
469 datum value = {(char *)__value.c_str(), static_cast<int>(__value.length())};
470 #endif
471
472 if(config.force == 1) {
473 #ifdef HAVE_BERKELEY
474 if((err = db->put(0, &key, &value, 0)) != 0) {
475 cerr << "Database error" << "(" << strerror(err) << ")" << endl;
476 exit(1);
477 }
478 #else
479 ret = gdbm_store(db, key, value, GDBM_REPLACE);
480 #endif
481 }
482 else {
483 #ifdef HAVE_BERKELEY
484 Dbt val;
485 err = db->get(0, &key, &val, 0);
486 if(err == 0) {
487 /* key exists, do the update */
488 if((err = db->put(0, &key, &value, 0)) != 0) {
489 cerr << "Database error" << "(" << strerror(err) << ")" << endl;
490 exit(1);
491 }
492 }
493 else if(err == DB_NOTFOUND) {
494 cerr << "Key " << config.key << " does not exist\n";
495 exit(1);
496 }
497 else {
498 cerr << "Database error" << "(" << strerror(err) << ")" << endl;
499 exit(1);
500 }
501 #else
502 ret = gdbm_exists(db, key);
503 if(ret) {
504 ret = gdbm_store(db, key, value, GDBM_REPLACE);
505 }
506 else {
507 cerr << "Key " << config.key << " does not exist\n";
508 exit(1);
509 }
510 #endif
511 }
512 #ifdef HAVE_BERKELEY
513 db->close(0);
514 #else
515 if(ret != 0) {
516 cerr << pkg << ": " << gdbm_strerror(gdbm_errno) << endl;
517 exit(1);
518 }
519 gdbm_close(db);
520 #endif
521 }
522
523
524
525 /*
526 * remove an entry
527 */
528 void Engine::remove() {
529 init();
530 int ret;
531 #ifdef HAVE_BERKELEY
532 Dbt key((char *)config.key.c_str(), config.key.length() + 1);
533 if((ret = db->del(NULL, &key, 0)) != 0) {
534 cerr << "Database error" << "(" << strerror(ret) << ")" << endl;
535 exit(1);
536 }
537 db->close(0);
538 #else
539 datum key = {(char *)config.key.c_str(), static_cast<int>(config.key.length())};
540 ret = gdbm_delete(db, key);
541 gdbm_close(db);
542 #endif
543 }
544
545
546
547 /*
548 * search for specific data
549 */
550 void Engine::select() {
551 init();
552 #ifdef HAVE_BERKELEY
553 int err;
554 char *k;
555 k = (char *)config.key.c_str();
556 Dbt key(k, strlen(k));
557 Dbt value;
558 err = db->get(0, &key, &value, 0);
559 if(err == 0) {
560 string gotvalue((char *)value.get_data(), value.get_size());
561 if(config.reverse == 1) {
562 cout << decode(gotvalue);
563 if(config.with == 1) {
564 cout << config.separator << config.key;
565 }
566 cout << endl;
567 }
568 else {
569 if(config.with == 1) {
570 cout << config.key << config.separator;
571 }
572 cout << decode(gotvalue) << endl;
573 }
574 }
575 else {
576 cerr << "Database error" << "(" << strerror(err) << ")" << endl;
577 exit(1);
578 }
579 db->close(0);
580 #else
581 datum content;
582 datum key = {(char *)config.key.c_str(), static_cast<int>(config.key.length())};
583 content = gdbm_fetch(db, key);
584 string V(content.dptr, content.dsize);
585 if(config.with == 1)
586 cout << config.key << config.separator;
587 cout << decode(V) << endl;
588 gdbm_close(db);
589 #endif
590 }
591
592 string Engine::encode(const string& data) {
593 if(config.encrypted) {
594 return rijn.encrypt(data);
595 }
596 else
597 return data;
598 }
599
600 string Engine::decode(const string& data) {
601 if(config.encrypted) {
602 return rijn.decrypt(data);
603 }
604 else
605 return data;
606 }
607
608