Skip to content

Commit

Permalink
Big refactoring to import/export
Browse files Browse the repository at this point in the history
* decouple import/export from GTK, so that CLI can be built without it
* add possibility to import files via the CLI
  • Loading branch information
paolostivanin committed Mar 11, 2024
1 parent 73c9d21 commit 4bf9e36
Show file tree
Hide file tree
Showing 30 changed files with 404 additions and 272 deletions.
68 changes: 57 additions & 11 deletions src/cli/exec-action.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
#include <libsecret/secret.h>
#include "main.h"
#include "get-data.h"
#include "../common/exports.h"
#include "../common/import-export.h"
#include "../common/secret-schema.h"
#include "../common/db-common.h"
#include "../common/gquarks.h"

#ifndef IS_FLATPAK
static gchar *get_db_path (void);
Expand Down Expand Up @@ -79,6 +79,51 @@ gboolean exec_action (CmdlineOpts *cmdline_opts,
list_all_acc_iss (db_data);
}

if (cmdline_opts->import) {
if (!g_file_test (cmdline_opts->import_file, G_FILE_TEST_EXISTS) || !g_file_test (cmdline_opts->import_file, G_FILE_TEST_IS_REGULAR)) {
g_printerr (_("%s doesn't exist or is not a valid file.\n"), cmdline_opts->import_file);
return FALSE;
}

gchar *pwd = get_pwd (_("Type the password for the file you want to import: "));
if (pwd == NULL) {
return FALSE;
}

GSList *otps = get_data_from_provider (cmdline_opts->import_type, cmdline_opts->import_file, pwd, get_max_file_size_from_memlock (), &err);
if (otps == NULL) {
const gchar *msg = "An error occurred while importing, so nothing has been added to the database.";
gchar *msg_with_err = NULL;
if (err != NULL) {
msg_with_err = g_strconcat (msg, " The error is: ", err->message, NULL);
}
g_printerr ("%s\n", err == NULL ? msg : msg_with_err);
if (err != NULL) {
g_free (msg_with_err);
g_clear_error (&err);
}
gcry_free (pwd);

return FALSE;
}
gcry_free (pwd);

add_otps_to_db (otps, db_data);
free_otps_gslist (otps, g_slist_length (otps));

update_db (db_data, &err);
if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) {
g_printerr ("Error while updating the database: %s\n", err->message);
return FALSE;
}
reload_db (db_data, &err);
if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) {
g_printerr ("Error while reloading the database: %s\n", err->message);
return FALSE;
}
g_print ("Data successfully imported.\n");
}

if (cmdline_opts->export) {
gchar *export_directory;
#ifdef IS_FLATPAK
Expand All @@ -92,8 +137,8 @@ gboolean exec_action (CmdlineOpts *cmdline_opts,
#endif
gboolean exported = FALSE;
gchar *export_pwd = NULL, *exported_file_path = NULL, *ret_msg = NULL;
if (g_ascii_strcasecmp (cmdline_opts->export_type, "andotp_plain") == 0 || g_ascii_strcasecmp (cmdline_opts->export_type, "andotp_encrypted") == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, "andotp_encrypted") == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, ANDOTP_PLAIN_ACTION_NAME) == 0 || g_ascii_strcasecmp (cmdline_opts->export_type, ANDOTP_ENC_ACTION_NAME) == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, ANDOTP_ENC_ACTION_NAME) == 0) {
export_pwd = get_pwd (_("Type the export encryption password: "));
if (export_pwd == NULL) {
free_dbdata (db_data);
Expand All @@ -105,13 +150,13 @@ gboolean exec_action (CmdlineOpts *cmdline_opts,
gcry_free (export_pwd);
exported = TRUE;
}
if (g_ascii_strcasecmp (cmdline_opts->export_type, "freeotpplus") == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, FREEOTPPLUS_PLAIN_ACTION_NAME) == 0) {
exported_file_path = g_build_filename (export_directory, "freeotpplus-exports.txt", NULL);
ret_msg = export_freeotpplus (exported_file_path, db_data->json_data);
exported = TRUE;
}
if (g_ascii_strcasecmp (cmdline_opts->export_type, "aegis_plain") == 0 || g_ascii_strcasecmp (cmdline_opts->export_type, "aegis_encrypted") == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, "aegis_encrypted") == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, AEGIS_PLAIN_ACTION_NAME) == 0 || g_ascii_strcasecmp (cmdline_opts->export_type, AEGIS_ENC_ACTION_NAME) == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, AEGIS_ENC_ACTION_NAME) == 0) {
export_pwd = get_pwd (_("Type the export encryption password: "));
if (export_pwd == NULL) {
free_dbdata (db_data);
Expand All @@ -123,8 +168,8 @@ gboolean exec_action (CmdlineOpts *cmdline_opts,
gcry_free (export_pwd);
exported = TRUE;
}
if (g_ascii_strcasecmp (cmdline_opts->export_type, "twofas_plain") == 0 || g_ascii_strcasecmp (cmdline_opts->export_type, "twofas_encrypted") == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, "twofas_encrypted") == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, TWOFAS_PLAIN_ACTION_NAME) == 0 || g_ascii_strcasecmp (cmdline_opts->export_type, TWOFAS_ENC_ACTION_NAME) == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, TWOFAS_ENC_ACTION_NAME) == 0) {
export_pwd = get_pwd (_("Type the export encryption password: "));
if (export_pwd == NULL) {
free_dbdata (db_data);
Expand All @@ -136,8 +181,8 @@ gboolean exec_action (CmdlineOpts *cmdline_opts,
gcry_free (export_pwd);
exported = TRUE;
}
if (g_ascii_strcasecmp (cmdline_opts->export_type, "authpro_plain") == 0 || g_ascii_strcasecmp (cmdline_opts->export_type, "authpro_encrypted") == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, "authpro_encrypted") == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, AUTHPRO_PLAIN_ACTION_NAME) == 0 || g_ascii_strcasecmp (cmdline_opts->export_type, AUTHPRO_ENC_ACTION_NAME) == 0) {
if (g_ascii_strcasecmp (cmdline_opts->export_type, AUTHPRO_ENC_ACTION_NAME) == 0) {
export_pwd = get_pwd (_("Type the export encryption password: "));
if (export_pwd == NULL) {
free_dbdata (db_data);
Expand All @@ -164,6 +209,7 @@ gboolean exec_action (CmdlineOpts *cmdline_opts,
}
g_free (exported_file_path);
}

return TRUE;
}

Expand Down
67 changes: 64 additions & 3 deletions src/cli/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <gio/gio.h>
#include <gcrypt.h>
#include "version.h"
#include "../common/import-export.h"
#include "main.h"

static gint handle_local_options (GApplication *application,
Expand All @@ -15,13 +16,23 @@ static int command_line (GApplication *application,
static gboolean parse_options (GApplicationCommandLine *cmdline,
CmdlineOpts *cmdline_opts);

static gboolean is_valid_type (const gchar *type);

static void g_free_cmdline_opts (CmdlineOpts *co);


gint
main (gint argc,
gchar **argv)
{
g_autofree gchar *type_msg = g_strconcat ("The import/export type for the database (to be used with --import/--export, mandatory). Must be either one of: ",
ANDOTP_PLAIN_ACTION_NAME, ", ", ANDOTP_ENC_ACTION_NAME, ", ",
AEGIS_PLAIN_ACTION_NAME, ", ", AEGIS_ENC_ACTION_NAME, ", ",
AUTHPRO_PLAIN_ACTION_NAME, ", ", AUTHPRO_ENC_ACTION_NAME, ", ",
TWOFAS_PLAIN_ACTION_NAME, ", ", TWOFAS_ENC_ACTION_NAME, ", ",
FREEOTPPLUS_PLAIN_ACTION_NAME,
NULL);

GOptionEntry entries[] =
{
#ifndef IS_FLATPAK
Expand All @@ -33,8 +44,10 @@ main (gint argc,
{ "match-exact", 'm', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Match exactly the provided account/issuer (to be used with --show, optional)", NULL},
{ "show-next", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Show also next OTP (to be used with --show, optional)", NULL},
{ "list", 'l', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "List all accounts and issuers for a given database.", NULL },
{ "export", 'e', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Export a database.", NULL },
{ "type", 't', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, NULL, "The export type for the database. Must be either one of: andotp_plain, andotp_encrypted, freeotpplus, aegis_plain, aegis_encrypted, twofas_plain, twofas_encrypted, authpro_plain, authpro_encrypted (to be used with --export, mandatory)", NULL },
{ "import", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Import a database.", NULL },
{ "export", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Export a database.", NULL },
{ "type", 't', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, NULL, type_msg, NULL },
{ "file", 'f', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, NULL, "File to import (to be used with --import, mandatory).", NULL },
#ifndef IS_FLATPAK
{ "output-dir", 'o', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, NULL, "The output directory (defaults to the user's home. To be used with --export, optional)", NULL },
#endif
Expand Down Expand Up @@ -114,6 +127,9 @@ command_line (GApplication *application __attribute__((unused)),
cmdline_opts->match_exact = FALSE;
cmdline_opts->show_next = FALSE;
cmdline_opts->list = FALSE;
cmdline_opts->import = FALSE;
cmdline_opts->import_type = NULL;
cmdline_opts->import_file = NULL;
cmdline_opts->export = FALSE;
cmdline_opts->export_type = NULL;
cmdline_opts->export_dir = NULL;
Expand Down Expand Up @@ -156,10 +172,31 @@ parse_options (GApplicationCommandLine *cmdline,

g_variant_dict_lookup (options, "list", "b", &cmdline_opts->list);

if (g_variant_dict_lookup (options, "import", "b", &cmdline_opts->import)) {
if (!g_variant_dict_lookup (options, "type", "s", &cmdline_opts->import_type)) {
g_application_command_line_print (cmdline, "Please provide an import type.\n");
return FALSE;
} else {
if (!is_valid_type (cmdline_opts->import_type)) {
g_application_command_line_print (cmdline, "Please provide a valid import type (see --help).\n");
return FALSE;
}
}
if (!g_variant_dict_lookup (options, "file", "s", &cmdline_opts->import_file)) {
g_application_command_line_print (cmdline, "Please provide a file to import.\n");
return FALSE;
}
}

if (g_variant_dict_lookup (options, "export", "b", &cmdline_opts->export)) {
if (!g_variant_dict_lookup (options, "type", "s", &cmdline_opts->export_type)) {
g_application_command_line_print (cmdline, "Please provide at least export type.\n");
g_application_command_line_print (cmdline, "Please provide an export type (see --help).\n");
return FALSE;
} else {
if (!is_valid_type (cmdline_opts->export_type)) {
g_application_command_line_print (cmdline, "Please provide a valid export type.\n");
return FALSE;
}
}
#ifndef IS_FLATPAK
g_variant_dict_lookup (options, "output-dir", "s", &cmdline_opts->export_dir);
Expand All @@ -169,12 +206,36 @@ parse_options (GApplicationCommandLine *cmdline,
}


static gboolean
is_valid_type (const gchar *type)
{
const gchar *supported_types[] = {ANDOTP_PLAIN_ACTION_NAME, ANDOTP_ENC_ACTION_NAME,
AEGIS_PLAIN_ACTION_NAME, AEGIS_ENC_ACTION_NAME,
TWOFAS_PLAIN_ACTION_NAME, TWOFAS_ENC_ACTION_NAME,
AUTHPRO_PLAIN_ACTION_NAME, AUTHPRO_ENC_ACTION_NAME,
FREEOTPPLUS_PLAIN_ACTION_NAME};

gint array_size = sizeof(supported_types) / sizeof(supported_types[0]);

gboolean found = FALSE;
for (gint i = 0; i < array_size; i++) {
if (g_strcmp0 (type, supported_types[i]) == 0) {
found = TRUE;
break;
}
}
return found;
}


static void
g_free_cmdline_opts (CmdlineOpts *co)
{
g_free (co->database);
g_free (co->account);
g_free (co->issuer);
g_free (co->import_type);
g_free (co->import_file);
g_free (co->export_type);
g_free (co->export_dir);
g_free (co);
Expand Down
3 changes: 3 additions & 0 deletions src/cli/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ typedef struct cmdline_opts_t {
gboolean match_exact;
gboolean show_next;
gboolean list;
gboolean import;
gchar *import_type;
gchar *import_file;
gboolean export;
gchar *export_type;
gchar *export_dir;
Expand Down
47 changes: 47 additions & 0 deletions src/common/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,4 +375,51 @@ json_object_get_hash (json_t *obj)
gcry_free (tmp_string);

return hash;
}


void
free_otps_gslist (GSList *otps,
guint list_len)
{
otp_t *otp_data;
for (guint i = 0; i < list_len; i++) {
otp_data = g_slist_nth_data (otps, i);
g_free (otp_data->type);
g_free (otp_data->algo);
g_free (otp_data->account_name);
g_free (otp_data->issuer);
gcry_free (otp_data->secret);
}
g_slist_free (otps);
}


json_t *
build_json_obj (const gchar *type,
const gchar *acc_label,
const gchar *acc_iss,
const gchar *acc_key,
guint digits,
const gchar *algo,
guint period,
guint64 ctr)
{
json_t *obj = json_object ();
json_object_set (obj, "type", json_string (type));
json_object_set (obj, "label", json_string (acc_label));
json_object_set (obj, "issuer", json_string (acc_iss));
json_object_set (obj, "secret", json_string (acc_key));
json_object_set (obj, "digits", json_integer (digits));
json_object_set (obj, "algo", json_string (algo));

json_object_set (obj, "secret", json_string (acc_key));

if (g_ascii_strcasecmp (type, "TOTP") == 0) {
json_object_set (obj, "period", json_integer (period));
} else {
json_object_set (obj, "counter", json_integer (ctr));
}

return obj;
}
11 changes: 11 additions & 0 deletions src/common/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,16 @@ guchar *get_authpro_derived_key (const gchar *password,

guint32 json_object_get_hash (json_t *obj);

void free_otps_gslist (GSList *otps,
guint list_len);

json_t *build_json_obj (const gchar *type,
const gchar *acc_label,
const gchar *acc_iss,
const gchar *acc_key,
guint digits,
const gchar *algo,
guint period,
guint64 ctr);

G_END_DECLS
32 changes: 32 additions & 0 deletions src/common/db-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,38 @@ get_db_derived_key (const gchar *pwd,
}


void
add_otps_to_db (GSList *otps,
DatabaseData *db_data)
{
json_t *obj;
guint list_len = g_slist_length (otps);
for (guint i = 0; i < list_len; i++) {
otp_t *otp = g_slist_nth_data (otps, i);
obj = build_json_obj (otp->type, otp->account_name, otp->issuer, otp->secret, otp->digits, otp->algo, otp->period, otp->counter);
guint hash = json_object_get_hash (obj);
if (g_slist_find_custom (db_data->objects_hash, GUINT_TO_POINTER(hash), check_duplicate) == NULL) {
db_data->objects_hash = g_slist_append (db_data->objects_hash, g_memdup2 (&hash, sizeof (guint)));
db_data->data_to_add = g_slist_append (db_data->data_to_add, obj);
} else {
g_print ("[INFO] Duplicate element not added\n");
}
}
}


gint
check_duplicate (gconstpointer data,
gconstpointer user_data)
{
guint list_elem = *(guint *)data;
if (list_elem == GPOINTER_TO_UINT(user_data)) {
return 0;
}
return -1;
}


void
cleanup_db_gfile (GFile *file,
gpointer stream,
Expand Down
Loading

0 comments on commit 4bf9e36

Please sign in to comment.