2013-01-29 04:07:54 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* Author: Valient Gough <vgough@pobox.com>
|
|
|
|
*
|
|
|
|
*****************************************************************************
|
|
|
|
* Copyright (c) 2004, Valient Gough
|
2013-10-20 00:35:26 +02:00
|
|
|
*
|
|
|
|
* This program is free software; you can distribute it and/or modify it under
|
2013-01-29 04:07:54 +01:00
|
|
|
* the terms of the GNU General Public License (GPL), as published by the Free
|
|
|
|
* Software Foundation; either version 2 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 General Public License for
|
|
|
|
* more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "fs/encfs.h"
|
|
|
|
|
|
|
|
#include "base/autosprintf.h"
|
|
|
|
#include "base/config.h"
|
|
|
|
#include "base/Error.h"
|
|
|
|
#include "base/i18n.h"
|
|
|
|
|
2013-03-05 07:36:32 +01:00
|
|
|
#include "cipher/CipherV1.h"
|
2013-03-05 07:32:27 +01:00
|
|
|
#include "cipher/BlockCipher.h"
|
|
|
|
#include "cipher/MAC.h"
|
|
|
|
#include "cipher/StreamCipher.h"
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
#include "fs/FileUtils.h"
|
|
|
|
#include "fs/Context.h"
|
|
|
|
#include "fs/FileNode.h"
|
|
|
|
#include "fs/DirNode.h"
|
|
|
|
|
|
|
|
#include <glog/logging.h>
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <string>
|
|
|
|
#include <cstdio>
|
2013-03-05 07:32:27 +01:00
|
|
|
#include <list>
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
2013-03-05 07:29:58 +01:00
|
|
|
using namespace encfs;
|
2013-03-05 07:39:51 +01:00
|
|
|
using gnu::autosprintf;
|
|
|
|
using std::cerr;
|
|
|
|
using std::cin;
|
|
|
|
using std::cout;
|
|
|
|
using std::endl;
|
|
|
|
using std::string;
|
|
|
|
using std::vector;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int showInfo(int argc, char **argv);
|
|
|
|
static int showVersion(int argc, char **argv);
|
|
|
|
static int showCiphers(int argc, char **argv);
|
|
|
|
static int chpasswd(int argc, char **argv);
|
|
|
|
static int chpasswdAutomaticly(int argc, char **argv);
|
|
|
|
static int cmd_ls(int argc, char **argv);
|
|
|
|
static int cmd_decode(int argc, char **argv);
|
|
|
|
static int cmd_encode(int argc, char **argv);
|
|
|
|
static int cmd_showcruft(int argc, char **argv);
|
|
|
|
static int cmd_cat(int argc, char **argv);
|
|
|
|
static int cmd_export(int argc, char **argv);
|
|
|
|
static int cmd_showKey(int argc, char **argv);
|
|
|
|
|
|
|
|
struct CommandOpts {
|
2013-01-29 04:07:54 +01:00
|
|
|
const char *name;
|
|
|
|
int minOptions;
|
|
|
|
int maxOptions;
|
|
|
|
int (*func)(int argc, char **argv);
|
|
|
|
const char *argStr;
|
|
|
|
const char *usageStr;
|
2013-10-20 00:35:26 +02:00
|
|
|
} commands[] = {
|
2013-10-20 00:42:44 +02:00
|
|
|
{"info", 1, 1, showInfo, "(root dir)",
|
2013-10-20 00:35:26 +02:00
|
|
|
// xgroup(usage)
|
2013-10-20 00:42:44 +02:00
|
|
|
gettext_noop(" -- show information")},
|
|
|
|
{"showKey", 1, 1, cmd_showKey, "(root dir)",
|
2013-10-20 00:35:26 +02:00
|
|
|
// xgroup(usage)
|
|
|
|
gettext_noop(" -- show key")},
|
2013-10-20 00:42:44 +02:00
|
|
|
{"passwd", 1, 1, chpasswd, "(root dir)",
|
2013-10-20 00:35:26 +02:00
|
|
|
// xgroup(usage)
|
|
|
|
gettext_noop(" -- change password for volume")},
|
|
|
|
{"autopasswd", 1, 1, chpasswdAutomaticly, "(root dir)",
|
|
|
|
// xgroup(usage)
|
|
|
|
gettext_noop(
|
|
|
|
" -- change password for volume, taking password"
|
|
|
|
" from standard input.\n\tNo prompts are issued.")},
|
|
|
|
{"ls", 1, 2, cmd_ls, 0, 0},
|
2013-10-20 00:42:44 +02:00
|
|
|
{"showcruft", 1, 1, cmd_showcruft, "(root dir)",
|
2013-10-20 00:35:26 +02:00
|
|
|
// xgroup(usage)
|
|
|
|
gettext_noop(" -- show undecodable filenames in the volume")},
|
2013-10-20 00:42:44 +02:00
|
|
|
{"cat", 2, 2, cmd_cat, "(root dir) path",
|
2013-10-20 00:35:26 +02:00
|
|
|
// xgroup(usage)
|
|
|
|
gettext_noop(" -- decodes the file and cats it to standard out")},
|
2013-10-20 00:42:44 +02:00
|
|
|
{"decode", 1, 100, cmd_decode,
|
2013-10-20 00:35:26 +02:00
|
|
|
"[--extpass=prog] (root dir) [encoded-name ...]",
|
|
|
|
// xgroup(usage)
|
|
|
|
gettext_noop(" -- decodes name and prints plaintext version")},
|
2013-10-20 00:42:44 +02:00
|
|
|
{"encode", 1, 100, cmd_encode,
|
2013-10-20 00:35:26 +02:00
|
|
|
"[--extpass=prog] (root dir) [plaintext-name ...]",
|
|
|
|
// xgroup(usage)
|
|
|
|
gettext_noop(" -- encodes a filename and print result")},
|
2013-10-20 00:42:44 +02:00
|
|
|
{"export", 2, 2, cmd_export, "(root dir) path",
|
2013-10-20 00:35:26 +02:00
|
|
|
// xgroup(usage)
|
|
|
|
gettext_noop(" -- decrypts a volume and writes results to path")},
|
2013-10-20 00:42:44 +02:00
|
|
|
{"ciphers", 0, 0, showCiphers, "",
|
2013-10-20 00:35:26 +02:00
|
|
|
// xgroup(usage)
|
|
|
|
gettext_noop(" -- show available ciphers")},
|
2013-10-20 00:42:44 +02:00
|
|
|
{"version", 0, 0, showVersion, "",
|
2013-10-20 00:35:26 +02:00
|
|
|
// xgroup(usage)
|
|
|
|
gettext_noop(" -- print version number and exit")},
|
|
|
|
{0, 0, 0, 0, 0, 0}};
|
|
|
|
|
|
|
|
static void usage(const char *name) {
|
2013-01-29 04:07:54 +01:00
|
|
|
cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n"
|
2013-10-20 00:42:44 +02:00
|
|
|
<< _("Usage:\n");
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
int offset = 0;
|
2013-10-20 00:35:26 +02:00
|
|
|
while (commands[offset].name != 0) {
|
|
|
|
if (commands[offset].argStr != 0) {
|
|
|
|
cerr << "encfsctl " << commands[offset].name << " "
|
|
|
|
<< commands[offset].argStr << "\n"
|
|
|
|
<< gettext(commands[offset].usageStr) << "\n";
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
++offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
cerr << "\n"
|
2013-10-20 00:35:26 +02:00
|
|
|
// xgroup(usage)
|
|
|
|
<< autosprintf(_("Example: \n%s info ~/.crypt\n"), name) << "\n";
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static bool checkDir(string &rootDir) {
|
|
|
|
if (!isDirectory(rootDir.c_str())) {
|
|
|
|
cout << autosprintf(_("directory %s does not exist.\n"), rootDir.c_str());
|
2013-01-29 04:07:54 +01:00
|
|
|
return false;
|
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
if (rootDir[rootDir.length() - 1] != '/') rootDir.append("/");
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int showVersion(int argc, char **argv) {
|
2013-01-29 04:07:54 +01:00
|
|
|
(void)argc;
|
|
|
|
(void)argv;
|
|
|
|
// xgroup(usage)
|
2013-03-05 07:32:27 +01:00
|
|
|
cout << autosprintf(_("encfsctl version %s"), VERSION) << "\n";
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int showCiphers(int argc, char **argv) {
|
2013-03-05 07:32:27 +01:00
|
|
|
(void)argc;
|
|
|
|
(void)argv;
|
2013-03-05 07:36:32 +01:00
|
|
|
|
|
|
|
cout << _("Block modes:\n");
|
2013-10-20 00:35:26 +02:00
|
|
|
for (const string &name : BlockCipher::GetRegistry().GetAll()) {
|
2013-03-05 07:32:27 +01:00
|
|
|
auto props = BlockCipher::GetRegistry().GetProperties(name.c_str());
|
|
|
|
cout << _("Implementation: ") << name << "\n";
|
2013-03-05 07:36:32 +01:00
|
|
|
cout << "\t" << _("Provider: ") << props->library << "\n";
|
2013-10-20 00:35:26 +02:00
|
|
|
cout << "\t" << _("Block cipher: ") << props->cipher << " / " << props->mode
|
|
|
|
<< "\n";
|
2013-03-05 07:32:27 +01:00
|
|
|
cout << "\t" << _("Key Sizes: ") << props->keySize << "\n";
|
|
|
|
}
|
2013-03-05 07:36:32 +01:00
|
|
|
cout << "\n";
|
|
|
|
|
|
|
|
cout << _("Stream modes:\n");
|
2013-10-20 00:35:26 +02:00
|
|
|
for (const string &name : StreamCipher::GetRegistry().GetAll()) {
|
2013-03-05 07:32:27 +01:00
|
|
|
auto props = StreamCipher::GetRegistry().GetProperties(name.c_str());
|
|
|
|
cout << _("Implementation: ") << name << "\n";
|
2013-03-05 07:36:32 +01:00
|
|
|
cout << "\t" << _("Provider: ") << props->library << "\n";
|
|
|
|
cout << "\t" << _("Stream cipher: ") << props->cipher << " / "
|
2013-10-20 00:35:26 +02:00
|
|
|
<< props->mode << "\n";
|
2013-03-05 07:32:27 +01:00
|
|
|
cout << "\t" << _("Key Sizes: ") << props->keySize << "\n";
|
|
|
|
}
|
2013-03-05 07:36:32 +01:00
|
|
|
cout << "\n";
|
2013-10-20 00:35:26 +02:00
|
|
|
|
2013-03-05 07:36:32 +01:00
|
|
|
cout << _("MAC modes:\n");
|
2013-10-20 00:35:26 +02:00
|
|
|
for (const string &name : MAC::GetRegistry().GetAll()) {
|
2013-03-05 07:36:32 +01:00
|
|
|
auto props = MAC::GetRegistry().GetProperties(name.c_str());
|
2013-03-05 07:32:27 +01:00
|
|
|
cout << _("Implementation: ") << name << "\n";
|
2013-03-05 07:36:32 +01:00
|
|
|
cout << "\t" << _("Provider: ") << props->library << "\n";
|
|
|
|
cout << "\t" << _("Hash mode: ") << props->hashFunction << " / "
|
2013-10-20 00:35:26 +02:00
|
|
|
<< props->mode << "\n";
|
2013-03-05 07:32:27 +01:00
|
|
|
cout << "\t" << _("Block size: ") << props->blockSize << "\n";
|
|
|
|
}
|
2013-03-05 07:36:32 +01:00
|
|
|
cout << "\n";
|
|
|
|
|
|
|
|
cout << _("PBKDF modes:\n");
|
2013-10-20 00:35:26 +02:00
|
|
|
for (const string &name : PBKDF::GetRegistry().GetAll()) {
|
2013-03-05 07:36:32 +01:00
|
|
|
auto props = PBKDF::GetRegistry().GetProperties(name.c_str());
|
|
|
|
cout << _("Implementation: ") << name << "\n";
|
|
|
|
cout << "\t" << _("Provider: ") << props->library << "\n";
|
|
|
|
cout << "\t" << _("Mode: ") << props->mode << "\n";
|
|
|
|
}
|
|
|
|
cout << "\n";
|
|
|
|
|
2013-03-05 07:32:27 +01:00
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int showInfo(int argc, char **argv) {
|
2013-01-29 04:07:54 +01:00
|
|
|
(void)argc;
|
|
|
|
string rootDir = argv[1];
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!checkDir(rootDir)) return EXIT_FAILURE;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
EncfsConfig config;
|
2013-10-20 00:35:26 +02:00
|
|
|
ConfigType type = readConfig(rootDir, config);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
// show information stored in config..
|
2013-10-20 00:35:26 +02:00
|
|
|
switch (type) {
|
|
|
|
case Config_None:
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << _("Unable to load or parse config file\n");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
case Config_Prehistoric:
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << _("A really old EncFS filesystem was found. \n"
|
|
|
|
"It is not supported in this EncFS build.\n");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
case Config_V3:
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << "\n" << autosprintf(_("Version 3 configuration; "
|
|
|
|
"created by %s\n"),
|
|
|
|
config.creator().c_str());
|
|
|
|
break;
|
|
|
|
case Config_V4:
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << "\n" << autosprintf(_("Version 4 configuration; "
|
|
|
|
"created by %s\n"),
|
|
|
|
config.creator().c_str());
|
|
|
|
break;
|
|
|
|
case Config_V5:
|
|
|
|
case Config_V6:
|
|
|
|
case Config_V7:
|
|
|
|
// xgroup(diag)
|
|
|
|
cout << "\n"
|
|
|
|
<< autosprintf(_("Version %i configuration; "
|
|
|
|
"created by %s (revision %i)\n"),
|
|
|
|
type, config.creator().c_str(), config.revision());
|
|
|
|
break;
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
showFSInfo(config);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static RootPtr initRootInfo(int &argc, char **&argv) {
|
2013-01-29 04:07:54 +01:00
|
|
|
RootPtr result;
|
2013-10-20 00:35:26 +02:00
|
|
|
shared_ptr<EncFS_Opts> opts(new EncFS_Opts());
|
2013-01-29 04:07:54 +01:00
|
|
|
opts->createIfNotFound = false;
|
|
|
|
opts->checkKey = false;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static struct option long_options[] = {{"extpass", 1, 0, 'p'}, {0, 0, 0, 0}};
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
for (;;) {
|
2013-01-29 04:07:54 +01:00
|
|
|
int option_index = 0;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int res = getopt_long(argc, argv, "", long_options, &option_index);
|
|
|
|
if (res == -1) break;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
switch (res) {
|
|
|
|
case 'p':
|
|
|
|
opts->passwordProgram.assign(optarg);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG(WARNING) << "getopt error: " << res;
|
|
|
|
break;
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (argc == 0) {
|
2013-01-29 04:07:54 +01:00
|
|
|
cerr << _("Incorrect number of arguments") << "\n";
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
|
|
|
opts->rootDir = string(argv[0]);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
--argc;
|
|
|
|
++argv;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (checkDir(opts->rootDir)) result = initFS(NULL, opts);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!result)
|
2013-01-29 04:07:54 +01:00
|
|
|
cerr << _("Unable to initialize encrypted filesystem - check path.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static RootPtr initRootInfo(const char *crootDir) {
|
2013-01-29 04:07:54 +01:00
|
|
|
string rootDir(crootDir);
|
|
|
|
RootPtr result;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (checkDir(rootDir)) {
|
|
|
|
shared_ptr<EncFS_Opts> opts(new EncFS_Opts());
|
2013-01-29 04:07:54 +01:00
|
|
|
opts->rootDir = rootDir;
|
|
|
|
opts->createIfNotFound = false;
|
|
|
|
opts->checkKey = false;
|
2013-10-20 00:35:26 +02:00
|
|
|
result = initFS(NULL, opts);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!result)
|
2013-01-29 04:07:54 +01:00
|
|
|
cerr << _("Unable to initialize encrypted filesystem - check path.\n");
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int cmd_showKey(int argc, char **argv) {
|
2013-01-29 04:07:54 +01:00
|
|
|
RootPtr rootInfo = initRootInfo(argv[1]);
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!rootInfo)
|
2013-01-29 04:07:54 +01:00
|
|
|
return EXIT_FAILURE;
|
2013-10-20 00:35:26 +02:00
|
|
|
else {
|
2013-01-29 04:07:54 +01:00
|
|
|
// encode with itself
|
2013-10-20 00:35:26 +02:00
|
|
|
string b64Key = rootInfo->cipher->encodeAsString(rootInfo->volumeKey);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
cout << b64Key << "\n";
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int cmd_decode(int argc, char **argv) {
|
2013-01-29 04:07:54 +01:00
|
|
|
RootPtr rootInfo = initRootInfo(argc, argv);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!rootInfo) return EXIT_FAILURE;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (argc > 0) {
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
|
|
string name = rootInfo->root->plainPath(argv[i]);
|
2013-01-29 04:07:54 +01:00
|
|
|
cout << name << "\n";
|
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
|
|
|
char buf[PATH_MAX + 1];
|
|
|
|
while (cin.getline(buf, PATH_MAX)) {
|
|
|
|
cout << rootInfo->root->plainPath(buf) << "\n";
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int cmd_encode(int argc, char **argv) {
|
2013-01-29 04:07:54 +01:00
|
|
|
RootPtr rootInfo = initRootInfo(argc, argv);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!rootInfo) return EXIT_FAILURE;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (argc > 0) {
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
2013-01-29 04:07:54 +01:00
|
|
|
string name = rootInfo->root->cipherPathWithoutRoot(argv[i]);
|
|
|
|
cout << name << "\n";
|
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
|
|
|
char buf[PATH_MAX + 1];
|
|
|
|
while (cin.getline(buf, PATH_MAX)) {
|
|
|
|
cout << rootInfo->root->cipherPathWithoutRoot(buf) << "\n";
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int cmd_ls(int argc, char **argv) {
|
2013-01-29 04:07:54 +01:00
|
|
|
(void)argc;
|
|
|
|
|
|
|
|
RootPtr rootInfo = initRootInfo(argv[1]);
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!rootInfo) return EXIT_FAILURE;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
// show files in directory
|
|
|
|
{
|
|
|
|
DirTraverse dt = rootInfo->root->openDir("/");
|
2013-10-20 00:35:26 +02:00
|
|
|
if (dt.valid()) {
|
|
|
|
for (string name = dt.nextPlaintextName(); !name.empty();
|
|
|
|
name = dt.nextPlaintextName()) {
|
|
|
|
shared_ptr<FileNode> fnode =
|
|
|
|
rootInfo->root->lookupNode(name.c_str(), "encfsctl-ls");
|
2013-01-29 04:07:54 +01:00
|
|
|
struct stat stbuf;
|
2013-10-20 00:35:26 +02:00
|
|
|
fnode->getAttr(&stbuf);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
struct tm stm;
|
2013-10-20 00:35:26 +02:00
|
|
|
localtime_r(&stbuf.st_mtime, &stm);
|
2013-01-29 04:07:54 +01:00
|
|
|
stm.tm_year += 1900;
|
|
|
|
// TODO: when I add "%s" to the end and name.c_str(), I get a
|
|
|
|
// seg fault from within strlen. Why ???
|
2013-10-20 00:35:26 +02:00
|
|
|
printf("%11i %4i-%02i-%02i %02i:%02i:%02i %s\n", int(stbuf.st_size),
|
|
|
|
int(stm.tm_year), int(stm.tm_mon), int(stm.tm_mday),
|
|
|
|
int(stm.tm_hour), int(stm.tm_min), int(stm.tm_sec),
|
|
|
|
name.c_str());
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// apply an operation to every block in the file
|
2013-10-20 00:35:26 +02:00
|
|
|
template <typename T>
|
|
|
|
int processContents(const shared_ptr<EncFS_Root> &rootInfo, const char *path,
|
|
|
|
T &op) {
|
2013-01-29 04:07:54 +01:00
|
|
|
int errCode = 0;
|
2013-10-20 00:35:26 +02:00
|
|
|
shared_ptr<FileNode> node =
|
|
|
|
rootInfo->root->openNode(path, "encfsctl", O_RDONLY, &errCode);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!node) {
|
2013-01-29 04:07:54 +01:00
|
|
|
// try treating filename as an enciphered path
|
2013-10-20 00:35:26 +02:00
|
|
|
string plainName = rootInfo->root->plainPath(path);
|
|
|
|
node = rootInfo->root->lookupNode(plainName.c_str(), "encfsctl");
|
|
|
|
if (node) {
|
|
|
|
errCode = node->open(O_RDONLY);
|
|
|
|
if (errCode < 0) node.reset();
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!node) {
|
2013-01-29 04:07:54 +01:00
|
|
|
cerr << "unable to open " << path << "\n";
|
|
|
|
return errCode;
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
2013-01-29 04:07:54 +01:00
|
|
|
unsigned char buf[512];
|
2013-10-20 00:35:26 +02:00
|
|
|
int blocks = (node->getSize() + sizeof(buf) - 1) / sizeof(buf);
|
2013-01-29 04:07:54 +01:00
|
|
|
// read all the data in blocks
|
2013-10-20 00:35:26 +02:00
|
|
|
for (int i = 0; i < blocks; ++i) {
|
|
|
|
int bytes = node->read(i * sizeof(buf), buf, sizeof(buf));
|
2013-01-29 04:07:54 +01:00
|
|
|
int res = op(buf, bytes);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (res < 0) return res;
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
2013-10-20 00:35:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class WriteOutput {
|
2013-01-29 04:07:54 +01:00
|
|
|
int _fd;
|
2013-10-20 00:35:26 +02:00
|
|
|
|
|
|
|
public:
|
2013-01-29 04:07:54 +01:00
|
|
|
WriteOutput(int fd) { _fd = fd; }
|
|
|
|
~WriteOutput() { close(_fd); }
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int operator()(const void *buf, int count) {
|
2013-01-29 04:07:54 +01:00
|
|
|
return (int)write(_fd, buf, count);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int cmd_cat(int argc, char **argv) {
|
2013-01-29 04:07:54 +01:00
|
|
|
(void)argc;
|
|
|
|
RootPtr rootInfo = initRootInfo(argv[1]);
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!rootInfo) return EXIT_FAILURE;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
const char *path = argv[2];
|
|
|
|
WriteOutput output(STDOUT_FILENO);
|
2013-10-20 00:35:26 +02:00
|
|
|
int errCode = processContents(rootInfo, path, output);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
return errCode;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int copyLink(const struct stat &stBuf,
|
|
|
|
const shared_ptr<EncFS_Root> &rootInfo, const string &cpath,
|
|
|
|
const string &destName) {
|
|
|
|
vector<char> buf(stBuf.st_size + 1, 0);
|
|
|
|
int res = ::readlink(cpath.c_str(), &buf[0], stBuf.st_size);
|
|
|
|
if (res == -1) {
|
2013-01-29 04:07:54 +01:00
|
|
|
cerr << "unable to readlink of " << cpath << "\n";
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[res] = '\0';
|
|
|
|
string decodedLink = rootInfo->root->plainPath(&buf[0]);
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
res = ::symlink(decodedLink.c_str(), destName.c_str());
|
|
|
|
if (res == -1) {
|
|
|
|
cerr << "unable to create symlink for " << cpath << " to " << decodedLink
|
|
|
|
<< "\n";
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int copyContents(const shared_ptr<EncFS_Root> &rootInfo,
|
|
|
|
const char *encfsName, const char *targetName) {
|
|
|
|
shared_ptr<FileNode> node = rootInfo->root->lookupNode(encfsName, "encfsctl");
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!node) {
|
2013-01-29 04:07:54 +01:00
|
|
|
cerr << "unable to open " << encfsName << "\n";
|
|
|
|
return EXIT_FAILURE;
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
2013-01-29 04:07:54 +01:00
|
|
|
struct stat st;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (node->getAttr(&st) != 0) return EXIT_FAILURE;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if ((st.st_mode & S_IFLNK) == S_IFLNK) {
|
2013-01-29 04:07:54 +01:00
|
|
|
string d = rootInfo->root->cipherPath(encfsName);
|
2013-10-20 00:35:26 +02:00
|
|
|
char linkContents[PATH_MAX + 2];
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (readlink(d.c_str(), linkContents, PATH_MAX + 1) <= 0) {
|
2013-01-29 04:07:54 +01:00
|
|
|
cerr << "unable to read link " << encfsName << "\n";
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
symlink(rootInfo->root->plainPath(linkContents).c_str(), targetName);
|
|
|
|
} else {
|
2013-01-29 04:07:54 +01:00
|
|
|
int outfd = creat(targetName, st.st_mode);
|
|
|
|
|
|
|
|
WriteOutput output(outfd);
|
2013-10-20 00:35:26 +02:00
|
|
|
processContents(rootInfo, encfsName, output);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static bool endsWith(const string &str, char ch) {
|
|
|
|
if (str.empty())
|
2013-01-29 04:07:54 +01:00
|
|
|
return false;
|
|
|
|
else
|
2013-10-20 00:35:26 +02:00
|
|
|
return str[str.length() - 1] == ch;
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int traverseDirs(const shared_ptr<EncFS_Root> &rootInfo,
|
|
|
|
string volumeDir, string destDir) {
|
|
|
|
if (!endsWith(volumeDir, '/')) volumeDir.append("/");
|
|
|
|
if (!endsWith(destDir, '/')) destDir.append("/");
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
// Lookup directory node so we can create a destination directory
|
|
|
|
// with the same permissions
|
|
|
|
{
|
|
|
|
struct stat st;
|
2013-10-20 00:35:26 +02:00
|
|
|
shared_ptr<FileNode> dirNode =
|
|
|
|
rootInfo->root->lookupNode(volumeDir.c_str(), "encfsctl");
|
|
|
|
if (dirNode->getAttr(&st)) return EXIT_FAILURE;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
mkdir(destDir.c_str(), st.st_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
// show files in directory
|
|
|
|
DirTraverse dt = rootInfo->root->openDir(volumeDir.c_str());
|
2013-10-20 00:35:26 +02:00
|
|
|
if (dt.valid()) {
|
|
|
|
for (string name = dt.nextPlaintextName(); !name.empty();
|
|
|
|
name = dt.nextPlaintextName()) {
|
2013-01-29 04:07:54 +01:00
|
|
|
// Recurse to subdirectories
|
2013-10-20 00:35:26 +02:00
|
|
|
if (name != "." && name != "..") {
|
2013-01-29 04:07:54 +01:00
|
|
|
string plainPath = volumeDir + name;
|
|
|
|
string cpath = rootInfo->root->cipherPath(plainPath.c_str());
|
|
|
|
string destName = destDir + name;
|
|
|
|
|
|
|
|
int r = EXIT_SUCCESS;
|
|
|
|
struct stat stBuf;
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!lstat(cpath.c_str(), &stBuf)) {
|
|
|
|
if (S_ISDIR(stBuf.st_mode)) {
|
|
|
|
traverseDirs(rootInfo, (plainPath + '/').c_str(), destName + '/');
|
|
|
|
} else if (S_ISLNK(stBuf.st_mode)) {
|
|
|
|
r = copyLink(stBuf, rootInfo, cpath, destName);
|
|
|
|
} else {
|
|
|
|
r = copyContents(rootInfo, plainPath.c_str(), destName.c_str());
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
2013-01-29 04:07:54 +01:00
|
|
|
r = EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (r != EXIT_SUCCESS) return r;
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int cmd_export(int argc, char **argv) {
|
2013-01-29 04:07:54 +01:00
|
|
|
(void)argc;
|
|
|
|
|
|
|
|
RootPtr rootInfo = initRootInfo(argv[1]);
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!rootInfo) return EXIT_FAILURE;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
string destDir = argv[2];
|
|
|
|
// if the dir doesn't exist, then create it (with user permission)
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!checkDir(destDir) && !userAllowMkdir(destDir.c_str(), 0700))
|
2013-01-29 04:07:54 +01:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
|
|
|
return traverseDirs(rootInfo, "/", destDir);
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int showcruft(const shared_ptr<EncFS_Root> &rootInfo, const char *dirName) {
|
2013-01-29 04:07:54 +01:00
|
|
|
int found = 0;
|
2013-10-20 00:35:26 +02:00
|
|
|
DirTraverse dt = rootInfo->root->openDir(dirName);
|
|
|
|
if (dt.valid()) {
|
2013-01-29 04:07:54 +01:00
|
|
|
bool showedDir = false;
|
2013-10-20 00:35:26 +02:00
|
|
|
for (string name = dt.nextInvalid(); !name.empty();
|
|
|
|
name = dt.nextInvalid()) {
|
|
|
|
string cpath = rootInfo->root->cipherPath(dirName);
|
2013-01-29 04:07:54 +01:00
|
|
|
cpath += '/';
|
|
|
|
cpath += name;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!showedDir) {
|
2013-01-29 04:07:54 +01:00
|
|
|
// just before showing a list of files in a directory
|
|
|
|
cout << autosprintf(_("In directory %s: \n"), dirName);
|
|
|
|
showedDir = true;
|
|
|
|
}
|
|
|
|
++found;
|
|
|
|
cout << cpath << "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
// now go back and look for directories to recurse into..
|
2013-10-20 00:35:26 +02:00
|
|
|
dt = rootInfo->root->openDir(dirName);
|
|
|
|
if (dt.valid()) {
|
|
|
|
for (string name = dt.nextPlaintextName(); !name.empty();
|
|
|
|
name = dt.nextPlaintextName()) {
|
|
|
|
if (name == "." || name == "..") continue;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
string plainPath = dirName;
|
|
|
|
plainPath += '/';
|
|
|
|
plainPath += name;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
string cpath = rootInfo->root->cipherPath(plainPath.c_str());
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (isDirectory(cpath.c_str()))
|
|
|
|
found += showcruft(rootInfo, plainPath.c_str());
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
iterate recursively through the filesystem and print out names of files
|
|
|
|
which have filenames which cannot be decoded with the given key..
|
|
|
|
*/
|
2013-10-20 00:35:26 +02:00
|
|
|
static int cmd_showcruft(int argc, char **argv) {
|
2013-01-29 04:07:54 +01:00
|
|
|
(void)argc;
|
|
|
|
|
|
|
|
RootPtr rootInfo = initRootInfo(argv[1]);
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!rootInfo) return EXIT_FAILURE;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int filesFound = showcruft(rootInfo, "/");
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-03-05 07:32:27 +01:00
|
|
|
cout << autosprintf("Found %i invalid file(s).", filesFound) << "\n";
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int do_chpasswd(bool useStdin, bool annotate, int argc, char **argv) {
|
2013-01-29 04:07:54 +01:00
|
|
|
(void)argc;
|
|
|
|
string rootDir = argv[1];
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!checkDir(rootDir)) return EXIT_FAILURE;
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
EncfsConfig config;
|
2013-10-20 00:35:26 +02:00
|
|
|
ConfigType cfgType = readConfig(rootDir, config);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (cfgType == Config_None) {
|
2013-01-29 04:07:54 +01:00
|
|
|
cout << _("Unable to load or parse config file\n");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2013-03-05 07:36:32 +01:00
|
|
|
// ask for existing password
|
|
|
|
cout << _("Enter current Encfs password\n");
|
2013-10-20 00:35:26 +02:00
|
|
|
if (annotate) cerr << "$PROMPT$ passwd" << endl;
|
|
|
|
CipherKey userKey = getUserKey(config, useStdin);
|
|
|
|
if (!userKey.valid()) return EXIT_FAILURE;
|
2013-03-05 07:36:32 +01:00
|
|
|
|
2013-01-29 04:07:54 +01:00
|
|
|
// instanciate proper cipher
|
2013-03-05 07:36:32 +01:00
|
|
|
shared_ptr<CipherV1> cipher = getCipher(config);
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!cipher) {
|
2013-01-29 04:07:54 +01:00
|
|
|
cout << autosprintf(_("Unable to find specified cipher \"%s\"\n"),
|
2013-10-20 00:35:26 +02:00
|
|
|
config.cipher().name().c_str());
|
2013-01-29 04:07:54 +01:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2013-03-05 07:36:32 +01:00
|
|
|
cipher->setKey(userKey);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
// decode volume key using user key -- at this point we detect an incorrect
|
|
|
|
// password if the key checksum does not match (causing readKey to fail).
|
2013-10-20 00:35:26 +02:00
|
|
|
CipherKey volumeKey = cipher->readKey(
|
|
|
|
(const unsigned char *)config.key().ciphertext().data(), true);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (!volumeKey.valid()) {
|
2013-01-29 04:07:54 +01:00
|
|
|
cout << _("Invalid password\n");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2013-03-05 07:36:32 +01:00
|
|
|
cipher->setKey(volumeKey);
|
|
|
|
|
2013-01-29 04:07:54 +01:00
|
|
|
// Now, get New user key..
|
|
|
|
userKey.reset();
|
|
|
|
cout << _("Enter new Encfs password\n");
|
|
|
|
|
|
|
|
// create new key
|
2013-10-20 00:35:26 +02:00
|
|
|
if (useStdin) {
|
|
|
|
if (annotate) cerr << "$PROMPT$ new_passwd" << endl;
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
userKey = getNewUserKey(config, useStdin, string(), string());
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
// re-encode the volume key using the new user key and write it out..
|
|
|
|
int result = EXIT_FAILURE;
|
2013-10-20 00:35:26 +02:00
|
|
|
if (userKey.valid()) {
|
2013-01-29 04:07:54 +01:00
|
|
|
int encodedKeySize = cipher->encodedKeySize();
|
2013-10-20 00:35:26 +02:00
|
|
|
unsigned char *keyBuf = new unsigned char[encodedKeySize];
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
// encode volume key with new user key
|
2013-03-05 07:36:32 +01:00
|
|
|
cipher->setKey(userKey);
|
2013-10-20 00:35:26 +02:00
|
|
|
cipher->writeKey(volumeKey, keyBuf);
|
2013-01-29 04:07:54 +01:00
|
|
|
userKey.reset();
|
2013-03-05 07:36:32 +01:00
|
|
|
cipher->setKey(volumeKey);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
|
|
|
EncryptedKey *key = config.mutable_key();
|
2013-10-20 00:35:26 +02:00
|
|
|
key->set_ciphertext(keyBuf, encodedKeySize);
|
2013-01-29 04:07:54 +01:00
|
|
|
delete[] keyBuf;
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (saveConfig(rootDir, config)) {
|
2013-01-29 04:07:54 +01:00
|
|
|
// password modified -- changes volume key of filesystem..
|
|
|
|
cout << _("Volume Key successfully updated.\n");
|
|
|
|
result = EXIT_SUCCESS;
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
2013-01-29 04:07:54 +01:00
|
|
|
cout << _("Error saving modified config file.\n");
|
|
|
|
}
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
2013-01-29 04:07:54 +01:00
|
|
|
cout << _("Error creating key\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
volumeKey.reset();
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int chpasswd(int argc, char **argv) {
|
|
|
|
return do_chpasswd(false, false, argc, argv);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
static int chpasswdAutomaticly(int argc, char **argv) {
|
|
|
|
return do_chpasswd(true, false, argc, argv);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
int main(int argc, char **argv) {
|
2013-01-29 04:07:54 +01:00
|
|
|
FLAGS_logtostderr = 1;
|
|
|
|
FLAGS_minloglevel = 1;
|
|
|
|
|
2013-10-19 09:08:46 +02:00
|
|
|
#ifdef DECLARE_VARIABLE
|
|
|
|
google::ParseCommandLineFlags(&argc, &argv, true);
|
|
|
|
#endif
|
|
|
|
|
2013-01-29 04:07:54 +01:00
|
|
|
google::InitGoogleLogging(argv[0]);
|
|
|
|
google::InstallFailureSignalHandler();
|
|
|
|
|
|
|
|
#ifdef LOCALEDIR
|
2013-10-20 00:35:26 +02:00
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
|
|
textdomain(PACKAGE);
|
2013-01-29 04:07:54 +01:00
|
|
|
#endif
|
|
|
|
|
2013-06-22 23:16:01 +02:00
|
|
|
bool isThreaded = false;
|
|
|
|
CipherV1::init(isThreaded);
|
2013-01-29 04:07:54 +01:00
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (argc < 2) {
|
|
|
|
usage(argv[0]);
|
2013-01-29 04:07:54 +01:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2013-10-19 09:08:46 +02:00
|
|
|
// find the specified command
|
|
|
|
int offset = 0;
|
2013-10-20 00:35:26 +02:00
|
|
|
while (commands[offset].name != 0) {
|
|
|
|
if (!strcmp(argv[1], commands[offset].name)) break;
|
2013-10-19 09:08:46 +02:00
|
|
|
++offset;
|
|
|
|
}
|
|
|
|
|
2013-10-20 00:35:26 +02:00
|
|
|
if (commands[offset].name == 0) {
|
2013-10-19 09:08:46 +02:00
|
|
|
cerr << autosprintf(_("invalid command: \"%s\""), argv[1]) << "\n";
|
2013-10-20 00:35:26 +02:00
|
|
|
} else {
|
|
|
|
if ((argc - 2 < commands[offset].minOptions) ||
|
|
|
|
(argc - 2 > commands[offset].maxOptions)) {
|
|
|
|
cerr << autosprintf(_("Incorrect number of arguments for command \"%s\""),
|
|
|
|
argv[1]) << "\n";
|
2013-01-29 04:07:54 +01:00
|
|
|
} else
|
2013-10-20 00:35:26 +02:00
|
|
|
return (*commands[offset].func)(argc - 1, argv + 1);
|
2013-01-29 04:07:54 +01:00
|
|
|
}
|
|
|
|
|
2013-06-22 23:16:01 +02:00
|
|
|
CipherV1::shutdown(isThreaded);
|
|
|
|
|
2013-01-29 04:07:54 +01:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|