From 94736e3fe09a605758bb29ef1cb10ba4002763bf Mon Sep 17 00:00:00 2001 From: Paolo Stivanin Date: Fri, 8 Mar 2024 14:56:49 +0100 Subject: [PATCH 1/6] Refactor CMakeLists.txt --- CMakeLists.txt | 270 +++++------------------- src/cli/CMakeLists.txt | 53 +++++ src/cli/exec-action.c | 12 +- src/cli/get-data.c | 2 +- src/cli/get-data.h | 1 - src/cli/main.c | 6 +- src/cli/main.h | 4 +- src/common/aegis.c | 4 +- src/common/andotp.c | 6 +- src/common/authpro.c | 4 +- src/common/common.c | 8 +- src/common/exports.h | 1 - src/common/freeotp.c | 6 +- src/common/twofas.c | 4 +- src/gui/CMakeLists.txt | 17 ++ src/{ => gui}/about_diag_cb.c | 0 src/{ => gui}/add-common.c | 0 src/{ => gui}/add-common.h | 0 src/{ => gui}/add-from-qr.c | 2 +- src/{ => gui}/app.c | 16 +- src/{ => gui}/change-db-cb.c | 0 src/{ => gui}/change-db-cb.h | 0 src/{ => gui}/change-file-cb.c | 0 src/{ => gui}/change-file-cb.h | 0 src/{ => gui}/change-pwd-cb.c | 2 +- src/{ => gui}/change-pwd-cb.h | 0 src/{ => gui}/data.h | 0 src/{ => gui}/db-actions.c | 2 +- src/{ => gui}/db-actions.h | 0 src/{ => gui}/db-misc.c | 9 +- src/{ => gui}/db-misc.h | 0 src/{ => gui}/dbinfo-cb.c | 2 +- src/{ => gui}/dbinfo-cb.h | 0 src/{ => gui}/edit-row-cb.c | 2 +- src/{ => gui}/edit-row-cb.h | 0 src/{ => gui}/exports.c | 4 +- src/{ => gui}/file-size.c | 0 src/{ => gui}/file-size.h | 0 src/{ => gui}/get-builder.c | 2 +- src/{ => gui}/get-builder.h | 0 src/{ => gui}/google-migration.pb-c.c | 0 src/{ => gui}/google-migration.pb-c.h | 0 src/{ => gui}/gquarks.c | 0 src/{ => gui}/gquarks.h | 0 src/{ => gui}/gui-common.c | 2 +- src/{ => gui}/gui-common.h | 0 src/{ => gui}/imports.c | 4 +- src/{ => gui}/imports.h | 0 src/{ => gui}/liststore-misc.c | 2 +- src/{ => gui}/liststore-misc.h | 0 src/{ => gui}/lock-app.c | 0 src/{ => gui}/lock-app.h | 0 src/{ => gui}/main.c | 0 src/{ => gui}/manual-add-cb.c | 0 src/{ => gui}/manual-add-cb.h | 0 src/{ => gui}/message-dialogs.c | 0 src/{ => gui}/message-dialogs.h | 0 src/{ => gui}/new-db-cb.c | 0 src/{ => gui}/new-db-cb.h | 0 src/{ => gui}/otpclient.h | 0 src/{ => gui}/parse-data.c | 2 +- src/{ => gui}/parse-uri.c | 2 +- src/{ => gui}/parse-uri.h | 0 src/{ => gui}/password-cb.c | 2 +- src/{ => gui}/password-cb.h | 0 src/{ => gui}/qrcode-parser.c | 2 +- src/{ => gui}/qrcode-parser.h | 0 src/{ => gui}/secret-schema.c | 0 src/{ => gui}/secret-schema.h | 0 src/{ => gui}/settings-cb.c | 6 +- src/{ => gui}/settings-cb.h | 0 src/{ => gui}/setup-signals-shortcuts.c | 0 src/{ => gui}/shortcuts-cb.c | 0 src/{ => gui}/shortcuts-cb.h | 0 src/{ => gui}/show-qr-cb.c | 0 src/{ => gui}/show-qr-cb.h | 0 src/{ => gui}/treeview.c | 2 +- src/{ => gui}/treeview.h | 0 src/{ => gui}/ui/add_popover.ui | 0 src/{ => gui}/ui/otpclient.ui | 0 src/{ => gui}/ui/settings_popover.ui | 0 src/{ => gui}/ui/shortcuts.ui | 0 src/{ => gui}/webcam-add-cb.c | 2 +- src/{ => gui}/webcam-add-cb.h | 0 84 files changed, 186 insertions(+), 279 deletions(-) create mode 100644 src/cli/CMakeLists.txt create mode 100644 src/gui/CMakeLists.txt rename src/{ => gui}/about_diag_cb.c (100%) rename src/{ => gui}/add-common.c (100%) rename src/{ => gui}/add-common.h (100%) rename src/{ => gui}/add-from-qr.c (99%) rename src/{ => gui}/app.c (99%) rename src/{ => gui}/change-db-cb.c (100%) rename src/{ => gui}/change-db-cb.h (100%) rename src/{ => gui}/change-file-cb.c (100%) rename src/{ => gui}/change-file-cb.h (100%) rename src/{ => gui}/change-pwd-cb.c (98%) rename src/{ => gui}/change-pwd-cb.h (100%) rename src/{ => gui}/data.h (100%) rename src/{ => gui}/db-actions.c (98%) rename src/{ => gui}/db-actions.h (100%) rename src/{ => gui}/db-misc.c (99%) rename src/{ => gui}/db-misc.h (100%) rename src/{ => gui}/dbinfo-cb.c (97%) rename src/{ => gui}/dbinfo-cb.h (100%) rename src/{ => gui}/edit-row-cb.c (99%) rename src/{ => gui}/edit-row-cb.h (100%) rename src/{ => gui}/exports.c (99%) rename src/{ => gui}/file-size.c (100%) rename src/{ => gui}/file-size.h (100%) rename src/{ => gui}/get-builder.c (93%) rename src/{ => gui}/get-builder.h (100%) rename src/{ => gui}/google-migration.pb-c.c (100%) rename src/{ => gui}/google-migration.pb-c.h (100%) rename src/{ => gui}/gquarks.c (100%) rename src/{ => gui}/gquarks.h (100%) rename src/{ => gui}/gui-common.c (99%) rename src/{ => gui}/gui-common.h (100%) rename src/{ => gui}/imports.c (98%) rename src/{ => gui}/imports.h (100%) rename src/{ => gui}/liststore-misc.c (99%) rename src/{ => gui}/liststore-misc.h (100%) rename src/{ => gui}/lock-app.c (100%) rename src/{ => gui}/lock-app.h (100%) rename src/{ => gui}/main.c (100%) rename src/{ => gui}/manual-add-cb.c (100%) rename src/{ => gui}/manual-add-cb.h (100%) rename src/{ => gui}/message-dialogs.c (100%) rename src/{ => gui}/message-dialogs.h (100%) rename src/{ => gui}/new-db-cb.c (100%) rename src/{ => gui}/new-db-cb.h (100%) rename src/{ => gui}/otpclient.h (100%) rename src/{ => gui}/parse-data.c (99%) rename src/{ => gui}/parse-uri.c (99%) rename src/{ => gui}/parse-uri.h (100%) rename src/{ => gui}/password-cb.c (99%) rename src/{ => gui}/password-cb.h (100%) rename src/{ => gui}/qrcode-parser.c (99%) rename src/{ => gui}/qrcode-parser.h (100%) rename src/{ => gui}/secret-schema.c (100%) rename src/{ => gui}/secret-schema.h (100%) rename src/{ => gui}/settings-cb.c (99%) rename src/{ => gui}/settings-cb.h (100%) rename src/{ => gui}/setup-signals-shortcuts.c (100%) rename src/{ => gui}/shortcuts-cb.c (100%) rename src/{ => gui}/shortcuts-cb.h (100%) rename src/{ => gui}/show-qr-cb.c (100%) rename src/{ => gui}/show-qr-cb.h (100%) rename src/{ => gui}/treeview.c (99%) rename src/{ => gui}/treeview.h (100%) rename src/{ => gui}/ui/add_popover.ui (100%) rename src/{ => gui}/ui/otpclient.ui (100%) rename src/{ => gui}/ui/settings_popover.ui (100%) rename src/{ => gui}/ui/shortcuts.ui (100%) rename src/{ => gui}/webcam-add-cb.c (99%) rename src/{ => gui}/webcam-add-cb.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc566f94..cccaec22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(OTPClient VERSION "3.5.2" LANGUAGES "C") +project(OTPClient VERSION "3.5.3" LANGUAGES "C") include(GNUInstallDirs) configure_file("src/common/version.h.in" "version.h") @@ -10,8 +10,7 @@ include_directories(${PROJECT_BINARY_DIR}) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -option(USE_FLATPAK_APP_FOLDER "Use flatpak app's config folder to store the database" OFF) -option(BUILD_GUI "Build the GUI" ON) +option(IS_FLATPAK "Use flatpak app's config folder to store the database" OFF) option(BUILD_CLI "Build the CLI" ON) set(CMAKE_C_STANDARD 11) @@ -23,15 +22,10 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2") if(CMAKE_COMPILER_IS_GNUCC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pie -fPIE") endif() -if(USE_FLATPAK_APP_FOLDER) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_FLATPAK_APP_FOLDER") -endif() -if(BUILD_GUI) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DBUILD_GUI") -endif() -if(BUILD_CLI) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DBUILD_CLI") +if(IS_FLATPAK) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DIS_FLATPAK") endif() + add_definitions(-DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\") @@ -57,219 +51,67 @@ pkg_check_modules(PROTOC REQUIRED libprotobuf-c>=1.3.0) pkg_check_modules(LIBSECRET REQUIRED libsecret-1>=0.20.0) pkg_check_modules(LIBQRENCODE REQUIRED libqrencode>=4.0.2) -set(GUI_HEADER_FILES - src/common/common.h - src/add-common.h - src/gui-common.h - src/data.h - src/db-misc.h - src/file-size.h - src/get-builder.h - src/gquarks.h - src/imports.h - src/liststore-misc.h - src/manual-add-cb.h - src/message-dialogs.h - src/otpclient.h - src/parse-uri.h - src/password-cb.h - src/qrcode-parser.h - src/treeview.h - src/common/exports.h - src/lock-app.h - src/common/get-providers-data.h - src/change-db-cb.h - src/new-db-cb.h - src/db-actions.h - src/google-migration.pb-c.h - src/secret-schema.h - src/change-pwd-cb.h - src/settings-cb.h - src/shortcuts-cb.h - src/webcam-add-cb.h - src/edit-row-cb.h - src/show-qr-cb.h src/dbinfo-cb.h - src/change-file-cb.h) - -set(GUI_SOURCE_FILES - src/common/common.c - src/add-common.c - src/common/andotp.c - src/app.c - src/gui-common.c - src/db-misc.c - src/edit-row-cb.c - src/file-size.c - src/get-builder.c - src/gquarks.c - src/imports.c - src/liststore-misc.c - src/main.c - src/manual-add-cb.c - src/message-dialogs.c - src/parse-data.c - src/parse-uri.c - src/password-cb.c - src/qrcode-parser.c - src/add-from-qr.c - src/settings-cb.c - src/shortcuts-cb.c - src/treeview.c - src/webcam-add-cb.c - src/exports.c - src/lock-app.c - src/common/freeotp.c - src/common/aegis.c - src/change-db-cb.c - src/new-db-cb.c - src/db-actions.c - src/change-file-cb.c - src/google-migration.pb-c.c - src/secret-schema.c - src/about_diag_cb.c - src/show-qr-cb.c - src/setup-signals-shortcuts.c - src/change-pwd-cb.c - src/dbinfo-cb.c - src/common/twofas.c - src/common/authpro.c) - -set(CLI_HEADER_FILES - src/cli/get-data.h - src/common/common.h - src/db-misc.h - src/gquarks.h - src/file-size.h - src/common/exports.h - src/parse-uri.h - src/common/get-providers-data.h - src/google-migration.pb-c.h - src/secret-schema.h - src/cli/main.h +set(COMMON_INCDIRS + ${GTK3_INCLUDE_DIRS} + ${GCRYPT_INCLUDE_DIRS} + ${COTP_INCLUDE_DIRS} + ${JANSSON_INCLUDE_DIRS} + ${UUID_INCLUDE_DIRS} + ${PROTOC_INCLUDE_DIRS} + ${LIBSECRET_INCLUDE_DIRS} + ${LIBQRENCODE_INCLUDE_DIRS} ) -set(CLI_SOURCE_FILES - src/cli/main.c - src/cli/get-data.c - src/common/common.c - src/db-misc.c - src/gquarks.c - src/cli/exec-action.c - src/file-size.c - src/parse-uri.c - src/common/andotp.c - src/common/aegis.c - src/common/freeotp.c - src/secret-schema.c - src/google-migration.pb-c.c - src/common/twofas.c - src/common/authpro.c) +set(COMMON_LIBS + ${GTK3_LIBRARIES} + ${GCRYPT_LIBRARIES} + ${COTP_LIBRARIES} + ${JANSSON_LIBRARIES} + ${UUID_LIBRARIES} + ${PROTOC_LIBRARIES} + ${LIBSECRET_LIBRARIES} + ${LIBQRENCODE_LIBRARIES} +) -if(BUILD_GUI AND BUILD_CLI) - list(APPEND CLI_SOURCE_FILES - src/treeview.c - src/liststore-misc.c - src/gui-common.c - src/add-common.c - src/imports.c - src/password-cb.c - src/get-builder.c - src/message-dialogs.c) +## GUI START +add_subdirectory(src/gui) +target_link_libraries(${PROJECT_NAME} + ${PNG_LIBRARIES} + ${ZBAR_LIBRARIES} + ${COMMON_LIBS} +) +set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "otpclient") - list(APPEND CLI_HEADER_FILES - src/treeview.h - src/liststore-misc.h - src/gui-common.h - src/add-common.h - src/imports.h - src/password-cb.h - src/get-builder.h - src/message-dialogs.h) -endif() +install(TARGETS ${PROJECT_NAME} DESTINATION bin) -if(BUILD_GUI) - include_directories(${GTK3_INCLUDE_DIRS} - ${GCRYPT_INCLUDE_DIRS} - ${COTP_INCLUDE_DIRS} - ${LIBZIP_INCLUDE_DIRS} - ${PNG_INCLUDE_DIRS} - ${JANSSON_INCLUDE_DIRS} - ${ZBAR_INCLUDE_DIRS} - ${UUID_INCLUDE_DIRS} - ${PROTOC_INCLUDE_DIRS} - ${LIBSECRET_INCLUDE_DIRS} - ${LIBQRENCODE_INCLUDE_DIRS}) +install(FILES data/com.github.paolostivanin.OTPClient.desktop DESTINATION share/applications) - add_executable(${PROJECT_NAME} ${GUI_SOURCE_FILES} ${GUI_HEADER_FILES}) - target_link_libraries(${PROJECT_NAME} - ${GTK3_LIBRARIES} - ${GCRYPT_LIBRARIES} - ${COTP_LIBRARIES} - ${LIBZIP_LIBRARIES} - ${PNG_LIBRARIES} - ${JANSSON_LIBRARIES} - ${ZBAR_LIBRARIES} - ${UUID_LIBRARIES} - ${PROTOC_LIBRARIES} - ${LIBSECRET_LIBRARIES} - ${LIBQRENCODE_LIBRARIES}) +install(FILES + src/gui/ui/otpclient.ui + src/gui/ui/add_popover.ui + src/gui/ui/settings_popover.ui + src/gui/ui/shortcuts.ui + DESTINATION share/otpclient) - set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "otpclient") - install(TARGETS ${PROJECT_NAME} DESTINATION bin) -endif() +install(FILES man/otpclient.1.gz DESTINATION share/man/man1) +install(FILES + data/icons/com.github.paolostivanin.OTPClient.svg + data/icons/com.github.paolostivanin.OTPClient-symbolic.svg + DESTINATION share/icons/hicolor/scalable/apps) +## GUI END +## CLI START if(BUILD_CLI) - include_directories(${GTK3_INCLUDE_DIRS} - ${GCRYPT_INCLUDE_DIRS} - ${COTP_INCLUDE_DIRS} - ${JANSSON_INCLUDE_DIRS} - ${UUID_INCLUDE_DIRS} - ${PROTOC_INCLUDE_DIRS} - ${LIBSECRET_INCLUDE_DIRS}) - if(BUILD_GUI) - include_directories( - ${LIBZIP_INCLUDE_DIRS} - ${PNG_INCLUDE_DIRS} - ${ZBAR_INCLUDE_DIRS} - ${LIBQRENCODE_INCLUDE_DIRS}) - endif() + add_subdirectory(src/cli) + target_link_libraries(${PROJECT_NAME}-cli + ${COMMON_LIBS} + ) + set_target_properties(${PROJECT_NAME}-cli PROPERTIES OUTPUT_NAME "otpclient-cli") - add_executable(${PROJECT_NAME}-cli ${CLI_SOURCE_FILES} ${CLI_HEADER_FILES}) - target_link_libraries(${PROJECT_NAME}-cli - ${GLIB2_LIBRARIES} - ${GIO_LIBRARIES} - ${GCRYPT_LIBRARIES} - ${COTP_LIBRARIES} - ${JANSSON_LIBRARIES} - ${UUID_LIBRARIES} - ${LIBSECRET_LIBRARIES} - ${PROTOC_LIBRARIES}) - if(BUILD_GUI) - target_link_libraries(${PROJECT_NAME}-cli - ${GTK3_LIBRARIES} - ${LIBZIP_LIBRARIES} - ${PNG_LIBRARIES} - ${ZBAR_LIBRARIES} - ${PROTOC_LIBRARIES} - ${LIBSECRET_LIBRARIES} - ${UUID_LIBRARIES} - ${LIBQRENCODE_LIBRARIES}) - endif() + install(TARGETS ${PROJECT_NAME}-cli DESTINATION bin) - set_target_properties(${PROJECT_NAME}-cli PROPERTIES OUTPUT_NAME "otpclient-cli") - install(TARGETS ${PROJECT_NAME}-cli DESTINATION bin) + install(FILES man/otpclient-cli.1.gz DESTINATION share/man/man1) endif() +## CLI END -install(FILES data/com.github.paolostivanin.OTPClient.desktop DESTINATION share/applications) -install(FILES data/com.github.paolostivanin.OTPClient.appdata.xml DESTINATION share/metainfo) - -install(FILES src/ui/otpclient.ui DESTINATION share/otpclient) -install(FILES src/ui/add_popover.ui DESTINATION share/otpclient) -install(FILES src/ui/settings_popover.ui DESTINATION share/otpclient) -install(FILES src/ui/shortcuts.ui DESTINATION share/otpclient) - -install(FILES man/otpclient.1.gz DESTINATION share/man/man1) -install(FILES man/otpclient-cli.1.gz DESTINATION share/man/man1) - -install(FILES data/icons/com.github.paolostivanin.OTPClient.svg DESTINATION share/icons/hicolor/scalable/apps) -install(FILES data/icons/com.github.paolostivanin.OTPClient-symbolic.svg DESTINATION share/icons/hicolor/scalable/apps) +install(FILES data/com.github.paolostivanin.OTPClient.appdata.xml DESTINATION share/metainfo) \ No newline at end of file diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt new file mode 100644 index 00000000..82b60f0d --- /dev/null +++ b/src/cli/CMakeLists.txt @@ -0,0 +1,53 @@ +set(CLI_HEADER_FILES + main.h + get-data.h + ../gui/db-misc.h + ../gui/gquarks.h + ../gui/file-size.h + ../gui/parse-uri.h + ../gui/google-migration.pb-c.h + ../gui/secret-schema.h + ../gui/treeview.h + ../gui/liststore-misc.h + ../gui/gui-common.h + ../gui/add-common.h + ../gui/imports.h + ../gui/password-cb.h + ../gui/get-builder.h + ../gui/message-dialogs.h + ../common/exports.h + ../common/common.h + ../common/get-providers-data.h +) + +set(CLI_SOURCE_FILES + main.c + get-data.c + exec-action.c + ../gui/db-misc.c + ../gui/gquarks.c + ../gui/file-size.c + ../gui/parse-uri.c + ../gui/secret-schema.c + ../gui/google-migration.pb-c.c + ../gui/treeview.c + ../gui/liststore-misc.c + ../gui/gui-common.c + ../gui/add-common.c + ../gui/imports.c + ../gui/password-cb.c + ../gui/get-builder.c + ../gui/message-dialogs.c + ../common/common.c + ../common/andotp.c + ../common/aegis.c + ../common/freeotp.c + ../common/twofas.c + ../common/authpro.c +) + +include_directories( + ${COMMON_INCDIRS} +) + +add_executable(${PROJECT_NAME}-cli ${CLI_SOURCE_FILES} ${CLI_HEADER_FILES}) \ No newline at end of file diff --git a/src/cli/exec-action.c b/src/cli/exec-action.c index 6fe233a4..db12bc1c 100644 --- a/src/cli/exec-action.c +++ b/src/cli/exec-action.c @@ -4,13 +4,13 @@ #include #include #include "main.h" -#include "../secret-schema.h" +#include "../gui/secret-schema.h" #include "../common/common.h" -#include "../db-misc.h" +#include "../gui/db-misc.h" #include "get-data.h" #include "../common/exports.h" -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK static gchar *get_db_path (void); #endif @@ -22,7 +22,7 @@ static gboolean get_use_secretservice (void); gboolean exec_action (CmdlineOpts *cmdline_opts, DatabaseData *db_data) { -#ifdef USE_FLATPAK_APP_FOLDER +#ifdef IS_FLATPAK db_data->db_path = g_build_filename (g_get_user_data_dir (), "otpclient-db.enc", NULL); // on the first run the cfg file is not created in the flatpak version because we use a non-changeable db path gchar *cfg_file_path = g_build_filename (g_get_user_data_dir (), "otpclient.cfg", NULL); @@ -82,7 +82,7 @@ gboolean exec_action (CmdlineOpts *cmdline_opts, if (cmdline_opts->export) { gchar *export_directory; -#ifdef USE_FLATPAK_APP_FOLDER +#ifdef IS_FLATPAK export_directory = g_get_user_data_dir (); #else export_directory = (cmdline_opts->export_dir != NULL) ? cmdline_opts->export_dir : (gchar *)g_get_home_dir (); @@ -168,7 +168,7 @@ gboolean exec_action (CmdlineOpts *cmdline_opts, return TRUE; } -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK static gchar * get_db_path (void) { diff --git a/src/cli/get-data.c b/src/cli/get-data.c index 5d4bdc9c..6b04dbbb 100644 --- a/src/cli/get-data.c +++ b/src/cli/get-data.c @@ -2,7 +2,7 @@ #include #include #include -#include "../db-misc.h" +#include "../gui/db-misc.h" #include "../common/common.h" static gint compare_strings (const gchar *s1, diff --git a/src/cli/get-data.h b/src/cli/get-data.h index 639e5887..e850645a 100644 --- a/src/cli/get-data.h +++ b/src/cli/get-data.h @@ -1,7 +1,6 @@ #pragma once #include -#include "../db-misc.h" G_BEGIN_DECLS diff --git a/src/cli/main.c b/src/cli/main.c index 719c8fe1..f060d66f 100644 --- a/src/cli/main.c +++ b/src/cli/main.c @@ -24,7 +24,7 @@ main (gint argc, { GOptionEntry entries[] = { -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK { "database", 'd', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, NULL, "(optional) path to the database. Default value is taken from otpclient.cfg", NULL }, #endif { "show", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Show a token for a given account.", NULL }, @@ -35,7 +35,7 @@ main (gint argc, { "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 }, -#ifndef USE_FLATPAK_APP_FOLDER +#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 { "version", 'v', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Show the program version.", NULL }, @@ -161,7 +161,7 @@ parse_options (GApplicationCommandLine *cmdline, g_application_command_line_print (cmdline, "Please provide at least export type.\n"); return FALSE; } -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK g_variant_dict_lookup (options, "output-dir", "s", &cmdline_opts->export_dir); #endif } diff --git a/src/cli/main.h b/src/cli/main.h index 89321fd6..47d708e1 100644 --- a/src/cli/main.h +++ b/src/cli/main.h @@ -1,13 +1,13 @@ #pragma once #include -#include "../data.h" +#include "../gui/data.h" G_BEGIN_DECLS #define MAX_ABS_PATH_LEN 256 -typedef struct _cmdline_opts { +typedef struct cmdline_opts_t { gchar *database; gboolean show; gchar *account; diff --git a/src/common/aegis.c b/src/common/aegis.c index 363c0c49..84ce24e1 100644 --- a/src/common/aegis.c +++ b/src/common/aegis.c @@ -3,8 +3,8 @@ #include #include #include -#include "../imports.h" -#include "../gquarks.h" +#include "../gui/imports.h" +#include "../gui/gquarks.h" #include "common.h" diff --git a/src/common/andotp.c b/src/common/andotp.c index a827818b..2c2f4b55 100644 --- a/src/common/andotp.c +++ b/src/common/andotp.c @@ -4,9 +4,9 @@ #include #include #include -#include "../file-size.h" -#include "../imports.h" -#include "../gquarks.h" +#include "../gui/file-size.h" +#include "../gui/imports.h" +#include "../gui/gquarks.h" #include "common.h" // salt and iv are both 12 bytes diff --git a/src/common/authpro.c b/src/common/authpro.c index 14172ecc..e012e8ef 100644 --- a/src/common/authpro.c +++ b/src/common/authpro.c @@ -2,8 +2,8 @@ #include #include #include "common.h" -#include "../gquarks.h" -#include "../imports.h" +#include "../gui/gquarks.h" +#include "../gui/imports.h" static GSList *get_otps_from_encrypted_backup (const gchar *path, const gchar *password, diff --git a/src/common/common.c b/src/common/common.c index 61d948a3..5ffd5293 100644 --- a/src/common/common.c +++ b/src/common/common.c @@ -6,9 +6,9 @@ #include "gcrypt.h" #include "jansson.h" #include "common.h" -#include "../google-migration.pb-c.h" -#include "../file-size.h" -#include "../gquarks.h" +#include "../gui/google-migration.pb-c.h" +#include "../gui/file-size.h" +#include "../gui/gquarks.h" gint32 get_max_file_size_from_memlock (void) @@ -391,7 +391,7 @@ get_kf_ptr (void) GError *err = NULL; GKeyFile *kf = g_key_file_new (); gchar *cfg_file_path; -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK cfg_file_path = g_build_filename (g_get_user_config_dir (), "otpclient.cfg", NULL); #else cfg_file_path = g_build_filename (g_get_user_data_dir (), "otpclient.cfg", NULL); diff --git a/src/common/exports.h b/src/common/exports.h index dca6beb6..6bf79dc7 100644 --- a/src/common/exports.h +++ b/src/common/exports.h @@ -1,6 +1,5 @@ #pragma once -#include #include G_BEGIN_DECLS diff --git a/src/common/freeotp.c b/src/common/freeotp.c index 2cc671ba..53e19d72 100644 --- a/src/common/freeotp.c +++ b/src/common/freeotp.c @@ -2,9 +2,9 @@ #include #include #include -#include "../file-size.h" -#include "../parse-uri.h" -#include "../gquarks.h" +#include "../gui/file-size.h" +#include "../gui/parse-uri.h" +#include "../gui/gquarks.h" GSList * diff --git a/src/common/twofas.c b/src/common/twofas.c index f65645d3..747d0c85 100644 --- a/src/common/twofas.c +++ b/src/common/twofas.c @@ -3,8 +3,8 @@ #include #include #include "common.h" -#include "../gquarks.h" -#include "../imports.h" +#include "../gui/gquarks.h" +#include "../gui/imports.h" #define TWOFAS_KDF_ITERS 10000 #define TWOFAS_SALT 256 diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt new file mode 100644 index 00000000..1dc3418b --- /dev/null +++ b/src/gui/CMakeLists.txt @@ -0,0 +1,17 @@ +file(GLOB GUI_HEADER_FILES + *.h + ../common/*.h +) + +file(GLOB GUI_SOURCE_FILES + *.c + ../common/*.c +) + +include_directories( + ${PNG_INCLUDE_DIRS} + ${ZBAR_INCLUDE_DIRS} + ${COMMON_INCDIRS} +) + +add_executable(${PROJECT_NAME} ${GUI_SOURCE_FILES} ${GUI_HEADER_FILES}) \ No newline at end of file diff --git a/src/about_diag_cb.c b/src/gui/about_diag_cb.c similarity index 100% rename from src/about_diag_cb.c rename to src/gui/about_diag_cb.c diff --git a/src/add-common.c b/src/gui/add-common.c similarity index 100% rename from src/add-common.c rename to src/gui/add-common.c diff --git a/src/add-common.h b/src/gui/add-common.h similarity index 100% rename from src/add-common.h rename to src/gui/add-common.h diff --git a/src/add-from-qr.c b/src/gui/add-from-qr.c similarity index 99% rename from src/add-from-qr.c rename to src/gui/add-from-qr.c index 867ddffe..485ccf15 100644 --- a/src/add-from-qr.c +++ b/src/gui/add-from-qr.c @@ -7,7 +7,7 @@ #include "message-dialogs.h" #include "add-common.h" #include "get-builder.h" -#include "common/common.h" +#include "../common/common.h" #include "gui-common.h" diff --git a/src/app.c b/src/gui/app.c similarity index 99% rename from src/app.c rename to src/gui/app.c index e304fa87..88e4cd47 100644 --- a/src/app.c +++ b/src/gui/app.c @@ -6,7 +6,7 @@ #include "otpclient.h" #include "gquarks.h" #include "imports.h" -#include "common/exports.h" +#include "../common/exports.h" #include "message-dialogs.h" #include "password-cb.h" #include "get-builder.h" @@ -14,7 +14,7 @@ #include "lock-app.h" #include "change-db-cb.h" #include "new-db-cb.h" -#include "common/common.h" +#include "../common/common.h" #include "secret-schema.h" #include "change-pwd-cb.h" #include "settings-cb.h" @@ -26,7 +26,7 @@ #include "dbinfo-cb.h" #include "change-file-cb.h" -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK static gchar *get_db_path (AppData *app_data); #endif @@ -131,7 +131,7 @@ activate (GtkApplication *app, return; } -#ifdef USE_FLATPAK_APP_FOLDER +#ifdef IS_FLATPAK app_data->db_data->db_path = g_build_filename (g_get_user_data_dir (), "otpclient-db.enc", NULL); // on the first run the cfg file is not created in the flatpak version because we use a non-changeable db path gchar *cfg_file_path = g_build_filename (g_get_user_data_dir (), "otpclient.cfg", NULL); @@ -371,7 +371,7 @@ migrate_secretservice_kf (AppData *app_data, g_key_file_set_boolean (kf, "config", "use_secret_service", app_data->use_secret_service); g_key_file_remove_key (kf, "config", "disable_secret_service", NULL); gchar *cfg_file_path; -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK cfg_file_path = g_build_filename (g_get_user_config_dir (), "otpclient.cfg", NULL); #else cfg_file_path = g_build_filename (g_get_user_data_dir (), "otpclient.cfg", NULL); @@ -411,7 +411,7 @@ set_warn_data (gboolean show_warning) if (kf != NULL) { g_key_file_set_boolean (kf, "config", "show_memlock_warning", show_warning); gchar *cfg_file_path; -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK cfg_file_path = g_build_filename (g_get_user_config_dir (), "otpclient.cfg", NULL); #else cfg_file_path = g_build_filename (g_get_user_data_dir (), "otpclient.cfg", NULL); @@ -499,7 +499,7 @@ create_main_window (gint width, } -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK static gchar * get_db_path (AppData *app_data) { @@ -715,7 +715,7 @@ store_data (const gchar *param1_name, GError *err = NULL; GKeyFile *kf = g_key_file_new (); gchar *cfg_file_path; -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK cfg_file_path = g_build_filename (g_get_user_config_dir (), "otpclient.cfg", NULL); #else cfg_file_path = g_build_filename (g_get_user_data_dir (), "otpclient.cfg", NULL); diff --git a/src/change-db-cb.c b/src/gui/change-db-cb.c similarity index 100% rename from src/change-db-cb.c rename to src/gui/change-db-cb.c diff --git a/src/change-db-cb.h b/src/gui/change-db-cb.h similarity index 100% rename from src/change-db-cb.h rename to src/gui/change-db-cb.h diff --git a/src/change-file-cb.c b/src/gui/change-file-cb.c similarity index 100% rename from src/change-file-cb.c rename to src/gui/change-file-cb.c diff --git a/src/change-file-cb.h b/src/gui/change-file-cb.h similarity index 100% rename from src/change-file-cb.h rename to src/gui/change-file-cb.h diff --git a/src/change-pwd-cb.c b/src/gui/change-pwd-cb.c similarity index 98% rename from src/change-pwd-cb.c rename to src/gui/change-pwd-cb.c index a4d7ab98..9e57c9e0 100644 --- a/src/change-pwd-cb.c +++ b/src/gui/change-pwd-cb.c @@ -5,7 +5,7 @@ #include "message-dialogs.h" #include "db-misc.h" #include "password-cb.h" -#include "common/common.h" +#include "../common/common.h" #include "secret-schema.h" #include "otpclient.h" diff --git a/src/change-pwd-cb.h b/src/gui/change-pwd-cb.h similarity index 100% rename from src/change-pwd-cb.h rename to src/gui/change-pwd-cb.h diff --git a/src/data.h b/src/gui/data.h similarity index 100% rename from src/data.h rename to src/gui/data.h diff --git a/src/db-actions.c b/src/gui/db-actions.c similarity index 98% rename from src/db-actions.c rename to src/gui/db-actions.c index 01c70e76..0c0d87c1 100644 --- a/src/db-actions.c +++ b/src/gui/db-actions.c @@ -45,7 +45,7 @@ update_cfg_file (AppData *app_data) { GKeyFile *kf = g_key_file_new (); gchar *cfg_file_path; -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK cfg_file_path = g_build_filename (g_get_user_config_dir (), "otpclient.cfg", NULL); #else cfg_file_path = g_build_filename (g_get_user_data_dir (), "otpclient.cfg", NULL); diff --git a/src/db-actions.h b/src/gui/db-actions.h similarity index 100% rename from src/db-actions.h rename to src/gui/db-actions.h diff --git a/src/db-misc.c b/src/gui/db-misc.c similarity index 99% rename from src/db-misc.c rename to src/gui/db-misc.c index c40bce19..bebadbc4 100644 --- a/src/db-misc.c +++ b/src/gui/db-misc.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -7,7 +6,7 @@ #include "otpclient.h" #include "file-size.h" #include "gquarks.h" -#include "common/common.h" +#include "../common/common.h" typedef struct header_data_t { guint8 iv[IV_SIZE]; @@ -107,13 +106,12 @@ update_and_reload_db (AppData *app_data, g_printerr ("%s\n", (*err)->message); return; } -#ifdef BUILD_GUI + if (regenerate_model) { update_model (app_data); g_slist_free_full (app_data->db_data->data_to_add, json_free); app_data->db_data->data_to_add = NULL; } -#endif } @@ -125,11 +123,10 @@ load_new_db (AppData *app_data, if (*err != NULL) { return; } -#ifdef BUILD_GUI + update_model (app_data); g_slist_free_full (app_data->db_data->data_to_add, json_free); app_data->db_data->data_to_add = NULL; -#endif } diff --git a/src/db-misc.h b/src/gui/db-misc.h similarity index 100% rename from src/db-misc.h rename to src/gui/db-misc.h diff --git a/src/dbinfo-cb.c b/src/gui/dbinfo-cb.c similarity index 97% rename from src/dbinfo-cb.c rename to src/gui/dbinfo-cb.c index 22c152e1..6adc42a9 100644 --- a/src/dbinfo-cb.c +++ b/src/gui/dbinfo-cb.c @@ -15,7 +15,7 @@ dbinfo_cb (GSimpleAction *simple __attribute__((unused)), gtk_entry_set_text (GTK_ENTRY(db_location_entry), app_data->db_data->db_path); gchar *cfg_file_path = NULL; -#ifdef USE_FLATPAK_APP_FOLDER +#ifdef IS_FLATPAK cfg_file_path = g_build_filename (g_get_user_data_dir (), "otpclient.cfg", NULL); #else cfg_file_path = g_build_filename (g_get_user_config_dir (), "otpclient.cfg", NULL); diff --git a/src/dbinfo-cb.h b/src/gui/dbinfo-cb.h similarity index 100% rename from src/dbinfo-cb.h rename to src/gui/dbinfo-cb.h diff --git a/src/edit-row-cb.c b/src/gui/edit-row-cb.c similarity index 99% rename from src/edit-row-cb.c rename to src/gui/edit-row-cb.c index 8d7e3542..3d4fbe1f 100644 --- a/src/edit-row-cb.c +++ b/src/gui/edit-row-cb.c @@ -7,7 +7,7 @@ #include "message-dialogs.h" #include "gui-common.h" #include "gquarks.h" -#include "common/common.h" +#include "../common/common.h" typedef struct edit_data_t { GtkListStore *list_store; diff --git a/src/edit-row-cb.h b/src/gui/edit-row-cb.h similarity index 100% rename from src/edit-row-cb.h rename to src/gui/edit-row-cb.h diff --git a/src/exports.c b/src/gui/exports.c similarity index 99% rename from src/exports.c rename to src/gui/exports.c index 4fa68e55..e795be95 100644 --- a/src/exports.c +++ b/src/gui/exports.c @@ -3,7 +3,7 @@ #include #include "password-cb.h" #include "message-dialogs.h" -#include "common/exports.h" +#include "../common/exports.h" static void show_ret_msg_dialog (GtkWidget *mainwin, const gchar *fpath, @@ -19,7 +19,7 @@ export_data_cb (GSimpleAction *simple, AppData *app_data = (AppData *)user_data; const gchar *base_dir = NULL; -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK base_dir = g_get_home_dir (); #else base_dir = g_get_user_data_dir (); diff --git a/src/file-size.c b/src/gui/file-size.c similarity index 100% rename from src/file-size.c rename to src/gui/file-size.c diff --git a/src/file-size.h b/src/gui/file-size.h similarity index 100% rename from src/file-size.h rename to src/gui/file-size.h diff --git a/src/get-builder.c b/src/gui/get-builder.c similarity index 93% rename from src/get-builder.c rename to src/gui/get-builder.c index b8f45955..d9541c75 100644 --- a/src/get-builder.c +++ b/src/gui/get-builder.c @@ -5,7 +5,7 @@ GtkBuilder * get_builder_from_partial_path (const gchar *partial_path) { const gchar *prefix; -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK // cmake trims the last '/', so we have to manually add it later on prefix = INSTALL_PREFIX; #else diff --git a/src/get-builder.h b/src/gui/get-builder.h similarity index 100% rename from src/get-builder.h rename to src/gui/get-builder.h diff --git a/src/google-migration.pb-c.c b/src/gui/google-migration.pb-c.c similarity index 100% rename from src/google-migration.pb-c.c rename to src/gui/google-migration.pb-c.c diff --git a/src/google-migration.pb-c.h b/src/gui/google-migration.pb-c.h similarity index 100% rename from src/google-migration.pb-c.h rename to src/gui/google-migration.pb-c.h diff --git a/src/gquarks.c b/src/gui/gquarks.c similarity index 100% rename from src/gquarks.c rename to src/gui/gquarks.c diff --git a/src/gquarks.h b/src/gui/gquarks.h similarity index 100% rename from src/gquarks.h rename to src/gui/gquarks.h diff --git a/src/gui-common.c b/src/gui/gui-common.c similarity index 99% rename from src/gui-common.c rename to src/gui/gui-common.c index e4d36a57..3b8b8ac4 100644 --- a/src/gui-common.c +++ b/src/gui/gui-common.c @@ -2,7 +2,7 @@ #include #include "message-dialogs.h" #include "add-common.h" -#include "common/common.h" +#include "../common/common.h" void diff --git a/src/gui-common.h b/src/gui/gui-common.h similarity index 100% rename from src/gui-common.h rename to src/gui/gui-common.h diff --git a/src/imports.c b/src/gui/imports.c similarity index 98% rename from src/imports.c rename to src/gui/imports.c index 881be297..b0b27a00 100644 --- a/src/imports.c +++ b/src/gui/imports.c @@ -5,10 +5,10 @@ #include "password-cb.h" #include "message-dialogs.h" #include "gquarks.h" -#include "common/common.h" +#include "../common/common.h" #include "gui-common.h" #include "db-misc.h" -#include "common/get-providers-data.h" +#include "../common/get-providers-data.h" static gboolean parse_data_and_update_db (AppData *app_data, diff --git a/src/imports.h b/src/gui/imports.h similarity index 100% rename from src/imports.h rename to src/gui/imports.h diff --git a/src/liststore-misc.c b/src/gui/liststore-misc.c similarity index 99% rename from src/liststore-misc.c rename to src/gui/liststore-misc.c index a354e88e..f8d02cc0 100644 --- a/src/liststore-misc.c +++ b/src/gui/liststore-misc.c @@ -4,7 +4,7 @@ #include "treeview.h" #include "liststore-misc.h" #include "gquarks.h" -#include "common/common.h" +#include "../common/common.h" typedef struct otp_data_t { diff --git a/src/liststore-misc.h b/src/gui/liststore-misc.h similarity index 100% rename from src/liststore-misc.h rename to src/gui/liststore-misc.h diff --git a/src/lock-app.c b/src/gui/lock-app.c similarity index 100% rename from src/lock-app.c rename to src/gui/lock-app.c diff --git a/src/lock-app.h b/src/gui/lock-app.h similarity index 100% rename from src/lock-app.h rename to src/gui/lock-app.h diff --git a/src/main.c b/src/gui/main.c similarity index 100% rename from src/main.c rename to src/gui/main.c diff --git a/src/manual-add-cb.c b/src/gui/manual-add-cb.c similarity index 100% rename from src/manual-add-cb.c rename to src/gui/manual-add-cb.c diff --git a/src/manual-add-cb.h b/src/gui/manual-add-cb.h similarity index 100% rename from src/manual-add-cb.h rename to src/gui/manual-add-cb.h diff --git a/src/message-dialogs.c b/src/gui/message-dialogs.c similarity index 100% rename from src/message-dialogs.c rename to src/gui/message-dialogs.c diff --git a/src/message-dialogs.h b/src/gui/message-dialogs.h similarity index 100% rename from src/message-dialogs.h rename to src/gui/message-dialogs.h diff --git a/src/new-db-cb.c b/src/gui/new-db-cb.c similarity index 100% rename from src/new-db-cb.c rename to src/gui/new-db-cb.c diff --git a/src/new-db-cb.h b/src/gui/new-db-cb.h similarity index 100% rename from src/new-db-cb.h rename to src/gui/new-db-cb.h diff --git a/src/otpclient.h b/src/gui/otpclient.h similarity index 100% rename from src/otpclient.h rename to src/gui/otpclient.h diff --git a/src/parse-data.c b/src/gui/parse-data.c similarity index 99% rename from src/parse-data.c rename to src/gui/parse-data.c index a23decf1..c2a0aa0e 100644 --- a/src/parse-data.c +++ b/src/gui/parse-data.c @@ -8,7 +8,7 @@ #include "gquarks.h" #include "message-dialogs.h" #include "gui-common.h" -#include "common/common.h" +#include "../common/common.h" static gboolean is_input_valid (GtkWidget *dialog, diff --git a/src/parse-uri.c b/src/gui/parse-uri.c similarity index 99% rename from src/parse-uri.c rename to src/gui/parse-uri.c index 6ec597cf..cd7e3076 100644 --- a/src/parse-uri.c +++ b/src/gui/parse-uri.c @@ -1,6 +1,6 @@ #include #include "imports.h" -#include "common/common.h" +#include "../common/common.h" #include "gui-common.h" diff --git a/src/parse-uri.h b/src/gui/parse-uri.h similarity index 100% rename from src/parse-uri.h rename to src/gui/parse-uri.h diff --git a/src/password-cb.c b/src/gui/password-cb.c similarity index 99% rename from src/password-cb.c rename to src/gui/password-cb.c index 55d8235d..f492224a 100644 --- a/src/password-cb.c +++ b/src/gui/password-cb.c @@ -3,7 +3,7 @@ #include "gui-common.h" #include "message-dialogs.h" #include "get-builder.h" -#include "common/common.h" +#include "../common/common.h" typedef struct entrywidgets_t { GtkWidget *entry_old; diff --git a/src/password-cb.h b/src/gui/password-cb.h similarity index 100% rename from src/password-cb.h rename to src/gui/password-cb.h diff --git a/src/qrcode-parser.c b/src/gui/qrcode-parser.c similarity index 99% rename from src/qrcode-parser.c rename to src/gui/qrcode-parser.c index 4c4a987c..1906b21f 100644 --- a/src/qrcode-parser.c +++ b/src/gui/qrcode-parser.c @@ -3,7 +3,7 @@ #include #include #include -#include "common/common.h" +#include "../common/common.h" typedef struct image_data_t { gulong width; diff --git a/src/qrcode-parser.h b/src/gui/qrcode-parser.h similarity index 100% rename from src/qrcode-parser.h rename to src/gui/qrcode-parser.h diff --git a/src/secret-schema.c b/src/gui/secret-schema.c similarity index 100% rename from src/secret-schema.c rename to src/gui/secret-schema.c diff --git a/src/secret-schema.h b/src/gui/secret-schema.h similarity index 100% rename from src/secret-schema.h rename to src/gui/secret-schema.h diff --git a/src/settings-cb.c b/src/gui/settings-cb.c similarity index 99% rename from src/settings-cb.c rename to src/gui/settings-cb.c index 4762edec..c964acf2 100644 --- a/src/settings-cb.c +++ b/src/gui/settings-cb.c @@ -5,7 +5,7 @@ #include "message-dialogs.h" #include "get-builder.h" #include "secret-schema.h" -#include "common/common.h" +#include "../common/common.h" typedef struct settings_data_t { GtkWidget *dss_switch; @@ -41,7 +41,7 @@ settings_dialog_cb (GSimpleAction *simple __attribute__((unused)), settings_data->app_data = app_data; gchar *cfg_file_path; -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK cfg_file_path = g_build_filename (g_get_user_config_dir (), "otpclient.cfg", NULL); #else cfg_file_path = g_build_filename (g_get_user_data_dir (), "otpclient.cfg", NULL); @@ -180,7 +180,7 @@ handle_al_ss (AppData *app_data, g_key_file_set_boolean (kf, "config", "use_secret_service", app_data->use_secret_service); g_key_file_set_integer (kf, "config", "inactivity_timeout", app_data->inactivity_timeout); gchar *cfg_file_path; -#ifndef USE_FLATPAK_APP_FOLDER +#ifndef IS_FLATPAK cfg_file_path = g_build_filename (g_get_user_config_dir (), "otpclient.cfg", NULL); #else cfg_file_path = g_build_filename (g_get_user_data_dir (), "otpclient.cfg", NULL); diff --git a/src/settings-cb.h b/src/gui/settings-cb.h similarity index 100% rename from src/settings-cb.h rename to src/gui/settings-cb.h diff --git a/src/setup-signals-shortcuts.c b/src/gui/setup-signals-shortcuts.c similarity index 100% rename from src/setup-signals-shortcuts.c rename to src/gui/setup-signals-shortcuts.c diff --git a/src/shortcuts-cb.c b/src/gui/shortcuts-cb.c similarity index 100% rename from src/shortcuts-cb.c rename to src/gui/shortcuts-cb.c diff --git a/src/shortcuts-cb.h b/src/gui/shortcuts-cb.h similarity index 100% rename from src/shortcuts-cb.h rename to src/gui/shortcuts-cb.h diff --git a/src/show-qr-cb.c b/src/gui/show-qr-cb.c similarity index 100% rename from src/show-qr-cb.c rename to src/gui/show-qr-cb.c diff --git a/src/show-qr-cb.h b/src/gui/show-qr-cb.h similarity index 100% rename from src/show-qr-cb.h rename to src/gui/show-qr-cb.h diff --git a/src/treeview.c b/src/gui/treeview.c similarity index 99% rename from src/treeview.c rename to src/gui/treeview.c index 967d8da4..340726f9 100644 --- a/src/treeview.c +++ b/src/gui/treeview.c @@ -3,7 +3,7 @@ #include "otpclient.h" #include "liststore-misc.h" #include "message-dialogs.h" -#include "common/common.h" +#include "../common/common.h" typedef struct parsed_json_data_t { diff --git a/src/treeview.h b/src/gui/treeview.h similarity index 100% rename from src/treeview.h rename to src/gui/treeview.h diff --git a/src/ui/add_popover.ui b/src/gui/ui/add_popover.ui similarity index 100% rename from src/ui/add_popover.ui rename to src/gui/ui/add_popover.ui diff --git a/src/ui/otpclient.ui b/src/gui/ui/otpclient.ui similarity index 100% rename from src/ui/otpclient.ui rename to src/gui/ui/otpclient.ui diff --git a/src/ui/settings_popover.ui b/src/gui/ui/settings_popover.ui similarity index 100% rename from src/ui/settings_popover.ui rename to src/gui/ui/settings_popover.ui diff --git a/src/ui/shortcuts.ui b/src/gui/ui/shortcuts.ui similarity index 100% rename from src/ui/shortcuts.ui rename to src/gui/ui/shortcuts.ui diff --git a/src/webcam-add-cb.c b/src/gui/webcam-add-cb.c similarity index 99% rename from src/webcam-add-cb.c rename to src/gui/webcam-add-cb.c index 9e099b33..5d9c964a 100644 --- a/src/webcam-add-cb.c +++ b/src/gui/webcam-add-cb.c @@ -4,7 +4,7 @@ #include "imports.h" #include "message-dialogs.h" #include "get-builder.h" -#include "common/common.h" +#include "../common/common.h" #include "gui-common.h" diff --git a/src/webcam-add-cb.h b/src/gui/webcam-add-cb.h similarity index 100% rename from src/webcam-add-cb.h rename to src/gui/webcam-add-cb.h From c5d8ada8c39b2ec8b1d3b9d4e7a6b771d8c62843 Mon Sep 17 00:00:00 2001 From: Paolo Stivanin Date: Fri, 8 Mar 2024 15:59:37 +0100 Subject: [PATCH 2/6] Make GUI and CLI independent from each other --- CMakeLists.txt | 55 ++-- src/cli/CMakeLists.txt | 50 +-- src/cli/exec-action.c | 5 +- src/cli/get-data.c | 10 +- src/cli/main.c | 2 +- src/cli/main.h | 2 +- src/common/aegis.c | 4 +- src/common/andotp.c | 4 +- src/common/authpro.c | 3 +- src/common/common.c | 314 +++---------------- src/common/common.h | 74 +++-- src/common/db-common.c | 408 ++++++++++++++++++++++++ src/common/db-common.h | 67 ++++ src/{gui => common}/file-size.c | 1 - src/{gui => common}/file-size.h | 0 src/common/freeotp.c | 9 +- src/common/get-providers-data.h | 2 +- src/{gui => common}/gquarks.c | 0 src/{gui => common}/gquarks.h | 0 src/{gui => common}/parse-uri.c | 50 +-- src/{gui => common}/parse-uri.h | 4 +- src/{gui => common}/secret-schema.c | 0 src/{gui => common}/secret-schema.h | 0 src/common/twofas.c | 3 +- src/gui/CMakeLists.txt | 1 + src/gui/add-common.c | 2 +- src/gui/add-from-qr.c | 2 - src/gui/app.c | 8 +- src/gui/change-db-cb.c | 2 +- src/gui/change-pwd-cb.c | 13 +- src/gui/data.h | 22 +- src/gui/db-misc.c | 462 +--------------------------- src/gui/db-misc.h | 31 +- src/gui/edit-row-cb.c | 13 +- src/gui/gui-common.c | 225 ++++++++++++++ src/gui/gui-common.h | 58 ++-- src/gui/imports.c | 10 +- src/gui/imports.h | 19 -- src/gui/liststore-misc.c | 9 +- src/gui/manual-add-cb.c | 9 +- src/gui/new-db-cb.c | 2 +- src/gui/parse-data.c | 3 +- src/gui/password-cb.c | 1 - src/gui/qrcode-parser.c | 1 + src/gui/settings-cb.c | 5 +- src/gui/show-qr-cb.c | 14 +- src/gui/treeview.c | 25 +- src/gui/webcam-add-cb.c | 1 - 48 files changed, 996 insertions(+), 1009 deletions(-) create mode 100644 src/common/db-common.c create mode 100644 src/common/db-common.h rename src/{gui => common}/file-size.c (99%) rename src/{gui => common}/file-size.h (100%) rename src/{gui => common}/gquarks.c (100%) rename src/{gui => common}/gquarks.h (100%) rename src/{gui => common}/parse-uri.c (82%) rename src/{gui => common}/parse-uri.h (54%) rename src/{gui => common}/secret-schema.c (100%) rename src/{gui => common}/secret-schema.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index cccaec22..a2a0c940 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(OTPClient VERSION "3.5.3" LANGUAGES "C") +project(OTPClient VERSION "3.6.0" LANGUAGES "C") include(GNUInstallDirs) configure_file("src/common/version.h.in" "version.h") @@ -11,6 +11,7 @@ include_directories(${PROJECT_BINARY_DIR}) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) option(IS_FLATPAK "Use flatpak app's config folder to store the database" OFF) +option(BUILD_GUI "Build the GTK UI" ON) option(BUILD_CLI "Build the CLI" ON) set(CMAKE_C_STANDARD 11) @@ -52,7 +53,6 @@ pkg_check_modules(LIBSECRET REQUIRED libsecret-1>=0.20.0) pkg_check_modules(LIBQRENCODE REQUIRED libqrencode>=4.0.2) set(COMMON_INCDIRS - ${GTK3_INCLUDE_DIRS} ${GCRYPT_INCLUDE_DIRS} ${COTP_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS} @@ -63,7 +63,6 @@ set(COMMON_INCDIRS ) set(COMMON_LIBS - ${GTK3_LIBRARIES} ${GCRYPT_LIBRARIES} ${COTP_LIBRARIES} ${JANSSON_LIBRARIES} @@ -73,34 +72,35 @@ set(COMMON_LIBS ${LIBQRENCODE_LIBRARIES} ) -## GUI START -add_subdirectory(src/gui) -target_link_libraries(${PROJECT_NAME} - ${PNG_LIBRARIES} - ${ZBAR_LIBRARIES} - ${COMMON_LIBS} -) -set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "otpclient") +if(BUILD_GUI) + add_subdirectory(src/gui) + target_link_libraries(${PROJECT_NAME} + ${GTK3_LIBRARIES} + ${PNG_LIBRARIES} + ${ZBAR_LIBRARIES} + ${COMMON_LIBS} + ) + set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "otpclient") + + install(TARGETS ${PROJECT_NAME} DESTINATION bin) -install(TARGETS ${PROJECT_NAME} DESTINATION bin) + install(FILES data/com.github.paolostivanin.OTPClient.desktop DESTINATION share/applications) -install(FILES data/com.github.paolostivanin.OTPClient.desktop DESTINATION share/applications) + install(FILES + src/gui/ui/otpclient.ui + src/gui/ui/add_popover.ui + src/gui/ui/settings_popover.ui + src/gui/ui/shortcuts.ui + DESTINATION share/otpclient) -install(FILES - src/gui/ui/otpclient.ui - src/gui/ui/add_popover.ui - src/gui/ui/settings_popover.ui - src/gui/ui/shortcuts.ui - DESTINATION share/otpclient) + install(FILES man/otpclient.1.gz DESTINATION share/man/man1) -install(FILES man/otpclient.1.gz DESTINATION share/man/man1) + install(FILES + data/icons/com.github.paolostivanin.OTPClient.svg + data/icons/com.github.paolostivanin.OTPClient-symbolic.svg + DESTINATION share/icons/hicolor/scalable/apps) +endif () -install(FILES - data/icons/com.github.paolostivanin.OTPClient.svg - data/icons/com.github.paolostivanin.OTPClient-symbolic.svg - DESTINATION share/icons/hicolor/scalable/apps) -## GUI END -## CLI START if(BUILD_CLI) add_subdirectory(src/cli) target_link_libraries(${PROJECT_NAME}-cli @@ -112,6 +112,5 @@ if(BUILD_CLI) install(FILES man/otpclient-cli.1.gz DESTINATION share/man/man1) endif() -## CLI END -install(FILES data/com.github.paolostivanin.OTPClient.appdata.xml DESTINATION share/metainfo) \ No newline at end of file +install(FILES data/com.github.paolostivanin.OTPClient.appdata.xml DESTINATION share/metainfo) diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 82b60f0d..be99646d 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -1,49 +1,11 @@ -set(CLI_HEADER_FILES - main.h - get-data.h - ../gui/db-misc.h - ../gui/gquarks.h - ../gui/file-size.h - ../gui/parse-uri.h - ../gui/google-migration.pb-c.h - ../gui/secret-schema.h - ../gui/treeview.h - ../gui/liststore-misc.h - ../gui/gui-common.h - ../gui/add-common.h - ../gui/imports.h - ../gui/password-cb.h - ../gui/get-builder.h - ../gui/message-dialogs.h - ../common/exports.h - ../common/common.h - ../common/get-providers-data.h +file(GLOB CLI_HEADER_FILES + *.h + ../common/*.h ) -set(CLI_SOURCE_FILES - main.c - get-data.c - exec-action.c - ../gui/db-misc.c - ../gui/gquarks.c - ../gui/file-size.c - ../gui/parse-uri.c - ../gui/secret-schema.c - ../gui/google-migration.pb-c.c - ../gui/treeview.c - ../gui/liststore-misc.c - ../gui/gui-common.c - ../gui/add-common.c - ../gui/imports.c - ../gui/password-cb.c - ../gui/get-builder.c - ../gui/message-dialogs.c - ../common/common.c - ../common/andotp.c - ../common/aegis.c - ../common/freeotp.c - ../common/twofas.c - ../common/authpro.c +file(GLOB CLI_SOURCE_FILES + *.c + ../common/*.c ) include_directories( diff --git a/src/cli/exec-action.c b/src/cli/exec-action.c index db12bc1c..9878d946 100644 --- a/src/cli/exec-action.c +++ b/src/cli/exec-action.c @@ -4,11 +4,10 @@ #include #include #include "main.h" -#include "../gui/secret-schema.h" -#include "../common/common.h" -#include "../gui/db-misc.h" #include "get-data.h" #include "../common/exports.h" +#include "../common/secret-schema.h" +#include "../common/db-common.h" #ifndef IS_FLATPAK static gchar *get_db_path (void); diff --git a/src/cli/get-data.c b/src/cli/get-data.c index 6b04dbbb..e4519a0b 100644 --- a/src/cli/get-data.c +++ b/src/cli/get-data.c @@ -2,8 +2,7 @@ #include #include #include -#include "../gui/db-misc.h" -#include "../common/common.h" +#include "../common/db-common.h" static gint compare_strings (const gchar *s1, const gchar *s2, @@ -132,9 +131,14 @@ get_token (json_t *obj, // counter must be updated every time it is accessed json_object_set (obj, "counter", json_integer (counter + 1)); GError *err = NULL; - update_and_reload_db (NULL, db_data, FALSE, &err); + update_db (db_data, &err); if (err != NULL) { g_printerr ("[ERROR] %s\n", err->message); + } else { + reload_db (db_data, &err); + if (err != NULL) { + g_printerr ("[ERROR] %s\n", err->message); + } } } } diff --git a/src/cli/main.c b/src/cli/main.c index f060d66f..3849991c 100644 --- a/src/cli/main.c +++ b/src/cli/main.c @@ -1,8 +1,8 @@ #include #include +#include #include "version.h" #include "main.h" -#include "../common/common.h" static gint handle_local_options (GApplication *application, GVariantDict *options, diff --git a/src/cli/main.h b/src/cli/main.h index 47d708e1..cf16dca6 100644 --- a/src/cli/main.h +++ b/src/cli/main.h @@ -1,7 +1,7 @@ #pragma once #include -#include "../gui/data.h" +#include "../common/db-common.h" G_BEGIN_DECLS diff --git a/src/common/aegis.c b/src/common/aegis.c index 84ce24e1..95506a9f 100644 --- a/src/common/aegis.c +++ b/src/common/aegis.c @@ -1,10 +1,10 @@ #include +#include #include #include #include #include -#include "../gui/imports.h" -#include "../gui/gquarks.h" +#include "gquarks.h" #include "common.h" diff --git a/src/common/andotp.c b/src/common/andotp.c index 2c2f4b55..2b3d3dee 100644 --- a/src/common/andotp.c +++ b/src/common/andotp.c @@ -4,9 +4,7 @@ #include #include #include -#include "../gui/file-size.h" -#include "../gui/imports.h" -#include "../gui/gquarks.h" +#include "gquarks.h" #include "common.h" // salt and iv are both 12 bytes diff --git a/src/common/authpro.c b/src/common/authpro.c index e012e8ef..50b80fe4 100644 --- a/src/common/authpro.c +++ b/src/common/authpro.c @@ -1,9 +1,8 @@ #include #include #include +#include "gquarks.h" #include "common.h" -#include "../gui/gquarks.h" -#include "../gui/imports.h" static GSList *get_otps_from_encrypted_backup (const gchar *path, const gchar *password, diff --git a/src/common/common.c b/src/common/common.c index 5ffd5293..b3ec3d86 100644 --- a/src/common/common.c +++ b/src/common/common.c @@ -1,14 +1,13 @@ #include +#include #include #include #include -#include #include "gcrypt.h" #include "jansson.h" #include "common.h" -#include "../gui/google-migration.pb-c.h" -#include "../gui/file-size.h" -#include "../gui/gquarks.h" +#include "file-size.h" +#include "gquarks.h" gint32 get_max_file_size_from_memlock (void) @@ -69,47 +68,6 @@ get_algo_int_from_str (const gchar *algo) } -guint32 -jenkins_one_at_a_time_hash (const gchar *key, gsize len) -{ - guint32 hash, i; - for (hash = i = 0; i < len; ++i) { - hash += key[i]; - hash += (hash << 10); - hash ^= (hash >> 6); - } - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); - - return hash; -} - - -guint32 -json_object_get_hash (json_t *obj) -{ - const gchar *key; - json_t *value; - gchar *tmp_string = gcry_calloc_secure (256, 1); - json_object_foreach (obj, key, value) { - if (g_strcmp0 (key, "period") == 0 || g_strcmp0 (key, "counter") == 0 || g_strcmp0 (key, "digits") == 0) { - json_int_t v = json_integer_value (value); - g_snprintf (tmp_string + g_utf8_strlen (tmp_string, -1), 256, "%ld", (gint64) v); - } else { - if (g_strlcat (tmp_string, json_string_value (value), 256) > 256) { - g_printerr ("%s\n", _("Truncation occurred.")); - } - } - } - - guint32 hash = jenkins_one_at_a_time_hash (tmp_string, strlen (tmp_string) + 1); - - gcry_free (tmp_string); - - return hash; -} - gchar * secure_strdup (const gchar *src) { @@ -120,26 +78,6 @@ secure_strdup (const gchar *src) } -gchar * -g_trim_whitespace (const gchar *str) -{ - if (g_utf8_strlen (str, -1) == 0) { - return NULL; - } - gchar *sec_buf = gcry_calloc_secure (strlen (str) + 1, 1); - int pos = 0; - for (int i = 0; str[i]; i++) { - if (str[i] != ' ') { - sec_buf[pos++] = str[i]; - } - } - sec_buf[pos] = '\0'; - gchar *secubf_newpos = (gchar *)gcry_realloc (sec_buf, strlen (sec_buf) + 1); - - return secubf_newpos; -} - - guchar * hexstr_to_bytes (const gchar *hexstr) { @@ -176,185 +114,6 @@ bytes_to_hexstr (const guchar *data, size_t datalen) } -// Backported from Glib (needed by below function) -static int -unescape_character (const char *scanner) -{ - int first_digit; - int second_digit; - - first_digit = g_ascii_xdigit_value (*scanner++); - if (first_digit < 0) - return -1; - - second_digit = g_ascii_xdigit_value (*scanner++); - if (second_digit < 0) - return -1; - - return (first_digit << 4) | second_digit; -} - - -// Backported from Glib. The only difference is that it's using gcrypt to allocate a secure buffer. -gchar * -g_uri_unescape_string_secure (const gchar *escaped_string, - const gchar *illegal_characters) -{ - if (escaped_string == NULL) - return NULL; - - const gchar *escaped_string_end = escaped_string + g_utf8_strlen (escaped_string, -1); - - gchar *result = gcry_calloc_secure (escaped_string_end - escaped_string + 1, 1); - gchar *out = result; - - const gchar *in; - gint character; - for (in = escaped_string; in < escaped_string_end; in++) { - character = *in; - - if (*in == '%') { - in++; - if (escaped_string_end - in < 2) { - // Invalid escaped char (to short) - gcry_free (result); - return NULL; - } - - character = unescape_character (in); - - // Check for an illegal character. We consider '\0' illegal here. - if (character <= 0 || - (illegal_characters != NULL && - strchr (illegal_characters, (char)character) != NULL)) { - gcry_free (result); - return NULL; - } - - in++; // The other char will be eaten in the loop header - } - *out++ = (char)character; - } - - *out = '\0'; - - return result; -} - - -guchar * -g_base64_decode_secure (const gchar *text, - gsize *out_len) -{ - guchar *ret; - gsize input_length; - gint state = 0; - guint save = 0; - - g_return_val_if_fail (text != NULL, NULL); - g_return_val_if_fail (out_len != NULL, NULL); - - input_length = g_utf8_strlen (text, -1); - - /* We can use a smaller limit here, since we know the saved state is 0, - +1 used to avoid calling g_malloc0(0), and hence returning NULL */ - ret = gcry_calloc_secure ((input_length / 4) * 3 + 1, 1); - - *out_len = g_base64_decode_step (text, input_length, ret, &state, &save); - - return ret; -} - - -GSList * -decode_migration_data (const gchar *encoded_uri) -{ - const gchar *encoded_uri_copy = encoded_uri; - if (g_ascii_strncasecmp (encoded_uri_copy, "otpauth-migration://offline?data=", 33) != 0) { - return NULL; - } - encoded_uri_copy += 33; - gsize out_len; - gchar *unesc_str = g_uri_unescape_string_secure (encoded_uri_copy, NULL); - guchar *data = g_base64_decode_secure (unesc_str, &out_len); - gcry_free (unesc_str); - - GSList *uris = NULL; - GString *uri = NULL; - MigrationPayload *msg = migration_payload__unpack (NULL, out_len, data); - gcry_free (data); - for (gint i = 0; i < msg->n_otp_parameters; i++) { - uri = g_string_new ("otpauth://"); - if (msg->otp_parameters[i]->type == 1) { - g_string_append (uri, "hotp/"); - } else if (msg->otp_parameters[i]->type == 2) { - g_string_append (uri, "totp/"); - } else { - g_printerr ("OTP type not recognized, skipping %s\n", msg->otp_parameters[i]->name); - goto end; - } - - g_string_append (uri, msg->otp_parameters[i]->name); - g_string_append (uri, "?"); - - if (msg->otp_parameters[i]->algorithm == 1) { - g_string_append (uri, "algorithm=SHA1&"); - } else if (msg->otp_parameters[i]->algorithm == 2) { - g_string_append (uri, "algorithm=SHA256&"); - } else if (msg->otp_parameters[i]->algorithm == 3) { - g_string_append (uri, "algorithm=SHA512&"); - } else { - g_printerr ("Algorithm type not supported, skipping %s\n", msg->otp_parameters[i]->name); - goto end; - } - - if (msg->otp_parameters[i]->digits == 1) { - g_string_append (uri, "digits=6&"); - } else if (msg->otp_parameters[i]->digits == 2) { - g_string_append (uri, "digits=8&"); - } else { - g_printerr ("Algorithm type not supported, skipping %s\n", msg->otp_parameters[i]->name); - goto end; - } - - if (msg->otp_parameters[i]->issuer != NULL) { - g_string_append (uri, "issuer="); - g_string_append (uri, msg->otp_parameters[i]->issuer); - g_string_append (uri, "&"); - } - - if (msg->otp_parameters[i]->type == 1) { - g_string_append (uri, "counter="); - g_string_append_printf(uri, "%ld", msg->otp_parameters[i]->counter); - g_string_append (uri, "&"); - } - -#ifdef COTP_OLD_LIB - baseencode_error_t b_err; -#else - cotp_error_t b_err; -#endif - gchar *b32_encoded_secret = base32_encode (msg->otp_parameters[i]->secret.data, msg->otp_parameters[i]->secret.len, &b_err); - if (b32_encoded_secret == NULL) { - g_printerr ("Error while encoding the secret (error code %d)\n", b_err); - goto end; - } - - g_string_append (uri, "secret="); - g_string_append (uri, b32_encoded_secret); - - uris = g_slist_append (uris, g_strdup (uri->str)); - - end: - g_string_free (uri, TRUE); - } - - migration_payload__free_unpacked (msg, NULL); - - return uris; -} - - gcry_cipher_hd_t open_cipher_and_set_data (guchar *derived_key, guchar *iv, @@ -385,31 +144,6 @@ open_cipher_and_set_data (guchar *derived_key, } -GKeyFile * -get_kf_ptr (void) -{ - GError *err = NULL; - GKeyFile *kf = g_key_file_new (); - gchar *cfg_file_path; -#ifndef IS_FLATPAK - cfg_file_path = g_build_filename (g_get_user_config_dir (), "otpclient.cfg", NULL); -#else - cfg_file_path = g_build_filename (g_get_user_data_dir (), "otpclient.cfg", NULL); -#endif - if (g_file_test (cfg_file_path, G_FILE_TEST_EXISTS)) { - if (g_key_file_load_from_file (kf, cfg_file_path, G_KEY_FILE_NONE, &err)) { - g_free (cfg_file_path); - return kf; - } - g_printerr ("%s\n", err->message); - g_clear_error (&err); - } - g_free (cfg_file_path); - g_key_file_free (kf); - return NULL; -} - - guchar * get_authpro_derived_key (const gchar *password, const guchar *salt) @@ -599,4 +333,46 @@ get_data_from_encrypted_backup (const gchar *path, g_free (enc_buf); return decrypted_data; +} + + +static guint32 +jenkins_one_at_a_time_hash (const gchar *key, gsize len) +{ + guint32 hash, i; + for (hash = i = 0; i < len; ++i) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; +} + + +guint32 +json_object_get_hash (json_t *obj) +{ + const gchar *key; + json_t *value; + gchar *tmp_string = gcry_calloc_secure (256, 1); + json_object_foreach (obj, key, value) { + if (g_strcmp0 (key, "period") == 0 || g_strcmp0 (key, "counter") == 0 || g_strcmp0 (key, "digits") == 0) { + json_int_t v = json_integer_value (value); + g_snprintf (tmp_string + g_utf8_strlen (tmp_string, -1), 256, "%ld", (gint64) v); + } else { + if (g_strlcat (tmp_string, json_string_value (value), 256) > 256) { + g_printerr ("%s\n", _("Truncation occurred.")); + } + } + } + + guint32 hash = jenkins_one_at_a_time_hash (tmp_string, strlen (tmp_string) + 1); + + gcry_free (tmp_string); + + return hash; } \ No newline at end of file diff --git a/src/common/common.h b/src/common/common.h index 9b931e03..0edcd09c 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -1,9 +1,9 @@ #pragma once #include +#include #include #include -#include G_BEGIN_DECLS @@ -19,54 +19,60 @@ G_BEGIN_DECLS #define ANDOTP_IV_SALT 12 #define ANDOTP_TAG 16 -gint32 get_max_file_size_from_memlock (void); +typedef struct otp_object_t { + gchar *type; + + gchar *algo; + + guint32 digits; + + union { + guint32 period; + guint64 counter; + }; -gchar *init_libs (gint32 max_file_size); + gchar *account_name; -gint get_algo_int_from_str (const gchar *algo); + gchar *issuer; -guint32 jenkins_one_at_a_time_hash (const gchar *key, - gsize len); + gchar *secret; +} otp_t; -guint32 json_object_get_hash (json_t *obj); -gchar *secure_strdup (const gchar *src); +gint32 get_max_file_size_from_memlock (void); -gchar *g_trim_whitespace (const gchar *str); +gchar *init_libs (gint32 max_file_size); -guchar *hexstr_to_bytes (const gchar *hexstr); +gint get_algo_int_from_str (const gchar *algo); -gchar *bytes_to_hexstr (const guchar *data, - size_t datalen); +guchar *hexstr_to_bytes (const gchar *hexstr); -GSList *decode_migration_data (const gchar *encoded_uri); +gchar *bytes_to_hexstr (const guchar *data, + size_t datalen); -gchar *g_uri_unescape_string_secure (const gchar *escaped_string, - const gchar *illegal_characters); +gcry_cipher_hd_t open_cipher_and_set_data (guchar *derived_key, + guchar *iv, + gsize iv_len); -guchar *g_base64_decode_secure (const gchar *text, - gsize *out_len); +gchar *secure_strdup (const gchar *src); -gcry_cipher_hd_t open_cipher_and_set_data (guchar *derived_key, - guchar *iv, - gsize iv_len); +gchar *get_data_from_encrypted_backup (const gchar *path, + const gchar *password, + gint32 max_file_size, + gint32 provider, + guint32 andotp_be_iterations, + GFile *in_file, + GFileInputStream *in_stream, + GError **err); -GKeyFile *get_kf_ptr (void); +guchar *get_andotp_derived_key (const gchar *password, + const guchar *salt, + guint32 iterations); -guchar *get_andotp_derived_key (const gchar *password, - const guchar *salt, - guint32 iterations); +guchar *get_authpro_derived_key (const gchar *password, + const guchar *salt); -guchar *get_authpro_derived_key (const gchar *password, - const guchar *salt); +guint32 json_object_get_hash (json_t *obj); -gchar *get_data_from_encrypted_backup (const gchar *path, - const gchar *password, - gint32 max_file_size, - gint32 provider, - guint32 andotp_be_iterations, - GFile *in_file, - GFileInputStream *in_stream, - GError **err); G_END_DECLS diff --git a/src/common/db-common.c b/src/common/db-common.c new file mode 100644 index 00000000..1f42a727 --- /dev/null +++ b/src/common/db-common.c @@ -0,0 +1,408 @@ +#include +#include +#include +#include +#include +#include +#include "gquarks.h" +#include "db-common.h" +#include "file-size.h" + +static gchar *decrypt_db (const gchar *db_path, + const gchar *password); + +static gpointer encrypt_db (const gchar *db_path, + const gchar *in_memory_json, + const gchar *password, + GError **err); + +static void add_to_json (gpointer list_elem, + gpointer json_array); + +static void backup_db (const gchar *path); + +static void restore_db (const gchar *path); + + +void +load_db (DatabaseData *db_data, + GError **err) +{ + if (!g_file_test (db_data->db_path, G_FILE_TEST_EXISTS)) { + g_set_error (err, missing_file_gquark (), MISSING_FILE_CODE, "Missing database file"); + db_data->json_data = NULL; + return; + } + + gchar *in_memory_json = decrypt_db (db_data->db_path, db_data->key); + if (in_memory_json == TAG_MISMATCH) { + g_set_error (err, bad_tag_gquark (), BAD_TAG_ERRCODE, "Either the file is corrupted or the password is wrong"); + return; + } else if (in_memory_json == KEY_DERIV_ERR) { + g_set_error (err, key_deriv_gquark (), KEY_DERIVATION_ERRCODE, "Error during key derivation"); + return; + } else if (in_memory_json == GENERIC_ERROR) { + g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "An error occurred, please check stderr"); + return; + } + + json_error_t jerr; + db_data->json_data = json_loads (in_memory_json, 0, &jerr); + gcry_free (in_memory_json); + if (db_data->json_data == NULL) { + gchar *msg = g_strconcat ("Error while loading json data: ", jerr.text, NULL); + g_set_error (err, memlock_error_gquark(), MEMLOCK_ERRCODE, "%s", msg); + return; + } + + gsize index; + json_t *obj; + json_array_foreach (db_data->json_data, index, obj) { + guint32 hash = json_object_get_hash (obj); + db_data->objects_hash = g_slist_append (db_data->objects_hash, g_memdup2 (&hash, sizeof (guint32))); + } +} + + +void +update_db (DatabaseData *db_data, + GError **err) +{ + gboolean first_run = (db_data->json_data == NULL) ? TRUE : FALSE; + if (first_run == TRUE) { + db_data->json_data = json_array (); + } else { + // database is backed-up only if this is not the first run + backup_db (db_data->db_path); + } + + g_slist_foreach (db_data->data_to_add, add_to_json, db_data->json_data); + + gchar *plain_data = json_dumps (db_data->json_data, JSON_COMPACT); + + if (encrypt_db (db_data->db_path, plain_data, db_data->key, err) != NULL) { + if (!first_run) { + g_printerr ("Encrypting the new data failed, restoring original copy...\n"); + restore_db (db_data->db_path); + } else { + g_printerr ("Couldn't update the database (encrypt_db failed)\n"); + if (g_file_test (db_data->db_path, G_FILE_TEST_EXISTS)) { + if (g_unlink (db_data->db_path) == -1) { + g_printerr ("%s\n", _("Error while unlinking the file.")); + } + } + } + } else { + // database must be backed-up both before and after the update + backup_db (db_data->db_path); + } + + gcry_free (plain_data); +} + + +void +reload_db (DatabaseData *db_data, + GError **err) +{ + if (db_data->json_data != NULL) { + json_decref (db_data->json_data); + } + + g_slist_free_full (db_data->objects_hash, g_free); + db_data->objects_hash = NULL; + + load_db (db_data, err); +} + + +guchar * +get_db_derived_key (const gchar *pwd, + DbHeaderData *header_data) +{ + gsize key_len = gcry_cipher_get_algo_keylen (GCRY_CIPHER_AES256); + gsize pwd_len = g_utf8_strlen (pwd, -1) + 1; + + guchar *derived_key = gcry_malloc_secure (key_len); + if (derived_key == NULL) { + g_printerr ("%s\n", _("Couldn't allocate the needed secure memory.")); + return SECURE_MEMORY_ALLOC_ERR; + } + + gpg_error_t ret = gcry_kdf_derive (pwd, pwd_len, GCRY_KDF_PBKDF2, GCRY_MD_SHA512, header_data->salt, KDF_SALT_SIZE, KDF_ITERATIONS, key_len, derived_key); + if (ret != 0) { + gcry_free (derived_key); + g_printerr ("%s\n", _("Error during key derivation.")); + return KEY_DERIV_ERR; + } + return derived_key; +} + + +void +cleanup_db_gfile (GFile *file, + gpointer stream, + GError *err) +{ + g_object_unref (file); + g_clear_error (&err); + + if (stream != NULL) + g_object_unref (stream); +} + + +void +free_db_resources (gcry_cipher_hd_t hd, + guchar *derived_key, + guchar *enc_buf, + gchar *dec_buf, + DbHeaderData *header_data) +{ + g_free (enc_buf); + g_free (header_data); + + if (hd != NULL) + gcry_cipher_close (hd); + if (derived_key != NULL) + gcry_free (derived_key); + if (dec_buf != NULL) + gcry_free (dec_buf); +} + + +static gchar * +decrypt_db (const gchar *db_path, + const gchar *password) +{ + GError *err = NULL; + DbHeaderData *header_data = g_new0 (DbHeaderData, 1); + + goffset input_file_size = get_file_size (db_path); + + GFile *in_file = g_file_new_for_path (db_path); + GFileInputStream *in_stream = g_file_read (in_file, NULL, &err); + if (err != NULL) { + g_printerr ("%s\n", err->message); + cleanup_db_gfile (in_file, NULL, err); + g_free (header_data); + return GENERIC_ERROR; + } + if (g_input_stream_read (G_INPUT_STREAM (in_stream), header_data, sizeof (DbHeaderData), NULL, &err) == -1) { + g_printerr ("%s\n", err->message); + cleanup_db_gfile (in_file, in_stream, err); + g_free (header_data); + return GENERIC_ERROR; + } + + guchar tag[TAG_SIZE]; + if (!g_seekable_seek (G_SEEKABLE (in_stream), input_file_size - TAG_SIZE, G_SEEK_SET, NULL, &err)) { + g_printerr ("%s\n", err->message); + cleanup_db_gfile (in_file, in_stream, err); + g_free (header_data); + return GENERIC_ERROR; + } + if (g_input_stream_read (G_INPUT_STREAM (in_stream), tag, TAG_SIZE, NULL, &err) == -1) { + g_printerr ("%s\n", err->message); + cleanup_db_gfile (in_file, in_stream, err); + g_free (header_data); + return GENERIC_ERROR; + } + + gsize enc_buf_size = input_file_size - sizeof (DbHeaderData) - TAG_SIZE; + guchar *enc_buf = g_malloc0 (enc_buf_size); + + if (!g_seekable_seek (G_SEEKABLE (in_stream), sizeof (DbHeaderData), G_SEEK_SET, NULL, &err)) { + g_printerr ("%s\n", err->message); + cleanup_db_gfile (in_file, in_stream, err); + + return GENERIC_ERROR; + } + if (g_input_stream_read (G_INPUT_STREAM (in_stream), enc_buf, enc_buf_size, NULL, &err) == -1) { + g_printerr ("%s\n", err->message); + cleanup_db_gfile (in_file, in_stream, err); + free_db_resources (NULL, NULL, enc_buf, NULL, header_data); + return GENERIC_ERROR; + } + g_object_unref (in_stream); + g_object_unref (in_file); + + guchar *derived_key = get_db_derived_key (password, header_data); + if (derived_key == SECURE_MEMORY_ALLOC_ERR || derived_key == KEY_DERIV_ERR) { + free_db_resources (NULL, NULL, enc_buf, NULL, header_data); + return (gpointer)derived_key; + } + + gcry_cipher_hd_t hd = open_cipher_and_set_data (derived_key, header_data->iv, IV_SIZE); + if (hd == NULL) { + free_db_resources (NULL, derived_key, enc_buf, NULL, header_data); + return GENERIC_ERROR; + } + + gpg_error_t gpg_err = gcry_cipher_authenticate (hd, header_data, sizeof (DbHeaderData)); + if (gpg_err) { + g_printerr ("%s\n", _("Error while processing the authenticated data.")); + free_db_resources (hd, derived_key, enc_buf, NULL, header_data); + return GENERIC_ERROR; + } + + gchar *dec_buf = gcry_calloc_secure (enc_buf_size, 1); + if (dec_buf == NULL) { + g_printerr ("%s\n", _("Error while allocating secure memory.")); + free_db_resources (hd, derived_key, enc_buf, NULL, header_data); + return GENERIC_ERROR; + } + gpg_err = gcry_cipher_decrypt (hd, dec_buf, enc_buf_size, enc_buf, enc_buf_size); + if (gpg_err) { + g_printerr ("%s\n", _("Error while decrypting the data.")); + free_db_resources (hd, derived_key, enc_buf, dec_buf, header_data); + return GENERIC_ERROR; + } + if (gcry_err_code (gcry_cipher_checktag (hd, tag, TAG_SIZE)) == GPG_ERR_CHECKSUM) { + free_db_resources (hd, derived_key, enc_buf, dec_buf, header_data); + return TAG_MISMATCH; + } + + free_db_resources (hd, derived_key, enc_buf, NULL, header_data); + + return dec_buf; +} + + +static void +add_to_json (gpointer list_elem, + gpointer json_array) +{ + json_array_append (json_array, json_deep_copy (list_elem)); +} + + +static gpointer +encrypt_db (const gchar *db_path, + const gchar *in_memory_json, + const gchar *password, + GError **err) +{ + GError *local_err = NULL; + DbHeaderData *header_data = g_new0 (DbHeaderData, 1); + + gcry_create_nonce (header_data->iv, IV_SIZE); + gcry_create_nonce (header_data->salt, KDF_SALT_SIZE); + + GFile *out_file = g_file_new_for_path (db_path); + GFileOutputStream *out_stream = g_file_replace (out_file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, &local_err); + if (local_err != NULL) { + g_printerr ("%s\n", local_err->message); + g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "Failed to replace existing file"); + cleanup_db_gfile (out_file, NULL, local_err); + g_free (header_data); + return GENERIC_ERROR; + } + if (g_output_stream_write (G_OUTPUT_STREAM (out_stream), header_data, sizeof (DbHeaderData), NULL, &local_err) == -1) { + g_printerr ("%s\n", local_err->message); + g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "Failed while writing header data to file"); + cleanup_db_gfile (out_file, out_stream, local_err); + g_free (header_data); + return GENERIC_ERROR; + } + + guchar *derived_key = get_db_derived_key (password, header_data); + if (derived_key == SECURE_MEMORY_ALLOC_ERR || derived_key == KEY_DERIV_ERR) { + g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "Failed to derive key.\nPlease check Secure Memory wiki page"); + cleanup_db_gfile (out_file, out_stream, local_err); + g_free (header_data); + return (gpointer)derived_key; + } + + gsize input_data_len = strlen (in_memory_json) + 1; + guchar *enc_buffer = g_malloc0 (input_data_len); + + gcry_cipher_hd_t hd = open_cipher_and_set_data (derived_key, header_data->iv, IV_SIZE); + if (hd == NULL) { + cleanup_db_gfile (out_file, out_stream, local_err); + free_db_resources (NULL, derived_key, enc_buffer, NULL, header_data); + return NULL; + } + + gpg_error_t gpg_err = gcry_cipher_authenticate (hd, header_data, sizeof (DbHeaderData)); + if (gpg_err) { + g_printerr ("%s\n", _("Error while processing the authenticated data.")); + cleanup_db_gfile (out_file, out_stream, local_err); + free_db_resources (hd, derived_key, enc_buffer, NULL, header_data); + return GENERIC_ERROR; + } + gpg_err = gcry_cipher_encrypt (hd, enc_buffer, input_data_len, in_memory_json, input_data_len); + if (gpg_err) { + g_printerr ("%s\n", _("Error while encrypting the data.")); + cleanup_db_gfile (out_file, out_stream, local_err); + free_db_resources (hd, derived_key, enc_buffer, NULL, header_data); + return GENERIC_ERROR; + } + + guchar tag[TAG_SIZE]; + gpg_err = gcry_cipher_gettag (hd, tag, TAG_SIZE); //append tag to outfile + if (gpg_err) { + g_printerr ("%s\n", _("Error while getting the tag.")); + cleanup_db_gfile (out_file, out_stream, local_err); + free_db_resources (hd, derived_key, enc_buffer, NULL, header_data); + return GENERIC_ERROR; + } + + if (g_output_stream_write (G_OUTPUT_STREAM(out_stream), enc_buffer, input_data_len, NULL, &local_err) == -1) { + g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "Failed while writing encrypted buffer to file"); + cleanup_db_gfile (out_file, out_stream, local_err); + free_db_resources (hd, derived_key, enc_buffer, NULL, header_data); + return GENERIC_ERROR; + } + if (g_output_stream_write (G_OUTPUT_STREAM(out_stream), tag, TAG_SIZE, NULL, &local_err) == -1) { + g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "Failed while writing tag data to file"); + cleanup_db_gfile (out_file, out_stream, local_err); + free_db_resources (hd, derived_key, enc_buffer, NULL, header_data); + return GENERIC_ERROR; + } + + free_db_resources (hd, derived_key, enc_buffer, NULL, header_data); + cleanup_db_gfile (out_file, out_stream, NULL); + + return NULL; +} + + +static void +perform_backup_restore (const gchar *path, + gboolean is_backup) +{ + GError *err = NULL; + gchar *src_path = is_backup ? g_strdup (path) : g_strconcat (path, ".bak", NULL); + gchar *dst_path = is_backup ? g_strconcat (path, ".bak", NULL) : g_strdup (path); + + GFile *src = g_file_new_for_path (src_path); + GFile *dst = g_file_new_for_path (dst_path); + + g_free (src_path); + g_free (dst_path); + + if (!g_file_copy (src, dst, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS, NULL, NULL, NULL, &err)) { + g_printerr ("Couldn't %s: %s\n", is_backup ? "create the backup" : "restore the backup", err->message); + g_clear_error (&err); + } else { + g_print("%s\n", is_backup ? _("Backup copy successfully created.") : _("Backup copy successfully restored.")); + } + + g_object_unref (src); + g_object_unref (dst); +} + + +static void +backup_db (const gchar *path) +{ + perform_backup_restore (path, TRUE); +} + + +static void +restore_db (const gchar *path) +{ + perform_backup_restore (path, FALSE); +} \ No newline at end of file diff --git a/src/common/db-common.h b/src/common/db-common.h new file mode 100644 index 00000000..c4b3b7c5 --- /dev/null +++ b/src/common/db-common.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include "common.h" + +G_BEGIN_DECLS + +#define GENERIC_ERROR (gpointer)1 +#define TAG_MISMATCH (gpointer)2 +#define SECURE_MEMORY_ALLOC_ERR (gpointer)3 +#define KEY_DERIV_ERR (gpointer)4 + +#define IV_SIZE 16 +#define KDF_ITERATIONS 100000 +#define KDF_SALT_SIZE 32 +#define TAG_SIZE 16 + + +typedef struct db_header_data_t { + guint8 iv[IV_SIZE]; + guint8 salt[KDF_SALT_SIZE]; +} DbHeaderData; + + +typedef struct db_data_t { + gchar *db_path; + + gchar *key; + + json_t *json_data; + + GSList *objects_hash; + + GSList *data_to_add; + + gint32 max_file_size_from_memlock; + + gchar *last_hotp; + GDateTime *last_hotp_update; + + gboolean key_stored; +} DatabaseData; + + +void load_db (DatabaseData *db_data, + GError **error); + +void update_db (DatabaseData *db_data, + GError **err); + +void reload_db (DatabaseData *db_data, + GError **err); + +guchar *get_db_derived_key (const gchar *pwd, + DbHeaderData *header_data); + +void cleanup_db_gfile (GFile *file, + gpointer stream, + GError *err); + +void free_db_resources (gcry_cipher_hd_t hd, + guchar *derived_key, + guchar *enc_buf, + gchar *dec_buf, + DbHeaderData *header_data); + +G_END_DECLS \ No newline at end of file diff --git a/src/gui/file-size.c b/src/common/file-size.c similarity index 99% rename from src/gui/file-size.c rename to src/common/file-size.c index ea8d2ee3..03f82c69 100644 --- a/src/gui/file-size.c +++ b/src/common/file-size.c @@ -1,6 +1,5 @@ #include - goffset get_file_size (const gchar *file_path) { diff --git a/src/gui/file-size.h b/src/common/file-size.h similarity index 100% rename from src/gui/file-size.h rename to src/common/file-size.h diff --git a/src/common/freeotp.c b/src/common/freeotp.c index 53e19d72..b2d8b907 100644 --- a/src/common/freeotp.c +++ b/src/common/freeotp.c @@ -1,10 +1,11 @@ #include +#include #include #include #include -#include "../gui/file-size.h" -#include "../gui/parse-uri.h" -#include "../gui/gquarks.h" +#include "file-size.h" +#include "gquarks.h" +#include "parse-uri.h" GSList * @@ -44,7 +45,7 @@ export_freeotpplus (const gchar *export_path, GFileOutputStream *out_stream = g_file_replace (out_gfile, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION | G_FILE_CREATE_PRIVATE, NULL, &err); if (err == NULL) { json_array_foreach (json_db_data, index, db_obj) { - gchar *uri = get_otpauth_uri (NULL, db_obj); + gchar *uri = get_otpauth_uri (db_obj); if (g_output_stream_write (G_OUTPUT_STREAM(out_stream), uri, g_utf8_strlen (uri, -1), NULL, &err) == -1) { g_set_error (&err, generic_error_gquark (), GENERIC_ERRCODE, "couldn't dump json data to file"); } diff --git a/src/common/get-providers-data.h b/src/common/get-providers-data.h index 7ff9160b..0e8379c9 100644 --- a/src/common/get-providers-data.h +++ b/src/common/get-providers-data.h @@ -1,6 +1,6 @@ #pragma once -#include +#include G_BEGIN_DECLS diff --git a/src/gui/gquarks.c b/src/common/gquarks.c similarity index 100% rename from src/gui/gquarks.c rename to src/common/gquarks.c diff --git a/src/gui/gquarks.h b/src/common/gquarks.h similarity index 100% rename from src/gui/gquarks.h rename to src/common/gquarks.h diff --git a/src/gui/parse-uri.c b/src/common/parse-uri.c similarity index 82% rename from src/gui/parse-uri.c rename to src/common/parse-uri.c index cd7e3076..6eeaa348 100644 --- a/src/gui/parse-uri.c +++ b/src/common/parse-uri.c @@ -1,8 +1,5 @@ #include -#include "imports.h" -#include "../common/common.h" -#include "gui-common.h" - +#include "common.h" static void parse_uri (const gchar *uri, GSList **otps); @@ -31,74 +28,57 @@ set_otps_from_uris (const gchar *otpauth_uris, gchar * -get_otpauth_uri (AppData *app_data, - json_t *obj) +get_otpauth_uri (json_t *obj) { gchar *constructed_label = NULL; - json_t *db_obj = NULL; - - if (app_data == NULL) { - db_obj = obj; - } else { - GtkTreeModel *model = gtk_tree_view_get_model (app_data->tree_view); - GtkListStore *list_store = GTK_LIST_STORE(model); - GtkTreeIter iter; - - if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (app_data->tree_view), &model, &iter) == FALSE) { - return NULL; - } - - guint row_number = get_row_number_from_iter (list_store, iter); - db_obj = json_array_get (app_data->db_data->json_data, row_number); - } GString *uri = g_string_new (NULL); g_string_append (uri, "otpauth://"); - const gchar *issuer = json_string_value (json_object_get (db_obj, "issuer")); + const gchar *issuer = json_string_value (json_object_get (obj, "issuer")); if (issuer != NULL && g_ascii_strcasecmp (issuer, "steam") == 0) { g_string_append (uri, "totp/"); - constructed_label = g_strconcat ("Steam:", json_string_value (json_object_get (db_obj, "label")), NULL); + constructed_label = g_strconcat ("Steam:", json_string_value (json_object_get (obj, "label")), NULL); } else { - g_string_append (uri, g_utf8_strdown (json_string_value (json_object_get (db_obj, "type")), -1)); + g_string_append (uri, g_utf8_strdown (json_string_value (json_object_get (obj, "type")), -1)); g_string_append (uri, "/"); if (issuer != NULL && g_utf8_strlen (issuer, -1) > 0) { - constructed_label = g_strconcat (json_string_value (json_object_get (db_obj, "issuer")), + constructed_label = g_strconcat (json_string_value (json_object_get (obj, "issuer")), ":", - json_string_value (json_object_get (db_obj, "label")), + json_string_value (json_object_get (obj, "label")), NULL); } else { - constructed_label = g_strdup (json_string_value (json_object_get (db_obj, "label"))); + constructed_label = g_strdup (json_string_value (json_object_get (obj, "label"))); } } gchar *escaped_label = g_uri_escape_string (constructed_label, NULL, FALSE); g_string_append (uri, escaped_label); g_string_append (uri, "?secret="); - g_string_append (uri, json_string_value (json_object_get (db_obj, "secret"))); + g_string_append (uri, json_string_value (json_object_get (obj, "secret"))); if (issuer != NULL && g_ascii_strcasecmp (issuer, "steam") == 0) { g_string_append (uri, "&issuer=Steam"); } if (issuer != NULL && g_utf8_strlen (issuer, -1) > 0) { g_string_append (uri, "&issuer="); - g_string_append (uri, json_string_value (json_object_get (db_obj, "issuer"))); + g_string_append (uri, json_string_value (json_object_get (obj, "issuer"))); } gchar *str_to_append = NULL; g_string_append (uri, "&digits="); - str_to_append = g_strdup_printf ("%lld", json_integer_value ( json_object_get (db_obj, "digits"))); + str_to_append = g_strdup_printf ("%lld", json_integer_value ( json_object_get (obj, "digits"))); g_string_append (uri,str_to_append); g_free (str_to_append); g_string_append (uri, "&algorithm="); - g_string_append (uri, json_string_value ( json_object_get (db_obj, "algo"))); + g_string_append (uri, json_string_value ( json_object_get (obj, "algo"))); - if (g_ascii_strcasecmp (json_string_value (json_object_get (db_obj, "type")), "TOTP") == 0) { + if (g_ascii_strcasecmp (json_string_value (json_object_get (obj, "type")), "TOTP") == 0) { g_string_append (uri, "&period="); - str_to_append = g_strdup_printf ("%lld", json_integer_value ( json_object_get (db_obj, "period"))); + str_to_append = g_strdup_printf ("%lld", json_integer_value ( json_object_get (obj, "period"))); g_string_append (uri, str_to_append); g_free (str_to_append); } else { g_string_append (uri, "&counter="); - str_to_append = g_strdup_printf ("%lld", json_integer_value ( json_object_get (db_obj, "counter"))); + str_to_append = g_strdup_printf ("%lld", json_integer_value ( json_object_get (obj, "counter"))); g_string_append (uri, str_to_append); g_free (str_to_append); } diff --git a/src/gui/parse-uri.h b/src/common/parse-uri.h similarity index 54% rename from src/gui/parse-uri.h rename to src/common/parse-uri.h index e18253f5..73ec6177 100644 --- a/src/gui/parse-uri.h +++ b/src/common/parse-uri.h @@ -1,14 +1,12 @@ #pragma once #include -#include "data.h" G_BEGIN_DECLS void set_otps_from_uris (const gchar *otpauth_uris, GSList **otps); -gchar *get_otpauth_uri (AppData *app_data, - json_t *obj); +gchar *get_otpauth_uri (json_t *obj); G_END_DECLS \ No newline at end of file diff --git a/src/gui/secret-schema.c b/src/common/secret-schema.c similarity index 100% rename from src/gui/secret-schema.c rename to src/common/secret-schema.c diff --git a/src/gui/secret-schema.h b/src/common/secret-schema.h similarity index 100% rename from src/gui/secret-schema.h rename to src/common/secret-schema.h diff --git a/src/common/twofas.c b/src/common/twofas.c index 747d0c85..e65364d1 100644 --- a/src/common/twofas.c +++ b/src/common/twofas.c @@ -2,9 +2,8 @@ #include #include #include +#include "gquarks.h" #include "common.h" -#include "../gui/gquarks.h" -#include "../gui/imports.h" #define TWOFAS_KDF_ITERS 10000 #define TWOFAS_SALT 256 diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 1dc3418b..5cf6539e 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -9,6 +9,7 @@ file(GLOB GUI_SOURCE_FILES ) include_directories( + ${GTK3_INCLUDE_DIRS} ${PNG_INCLUDE_DIRS} ${ZBAR_INCLUDE_DIRS} ${COMMON_INCDIRS} diff --git a/src/gui/add-common.c b/src/gui/add-common.c index 31ac5564..7f77dfb0 100644 --- a/src/gui/add-common.c +++ b/src/gui/add-common.c @@ -1,7 +1,7 @@ #include #include "data.h" #include "imports.h" -#include "parse-uri.h" +#include "../common/parse-uri.h" static gchar *check_params (GSList *otps); diff --git a/src/gui/add-from-qr.c b/src/gui/add-from-qr.c index 485ccf15..4d04333a 100644 --- a/src/gui/add-from-qr.c +++ b/src/gui/add-from-qr.c @@ -5,9 +5,7 @@ #include "imports.h" #include "qrcode-parser.h" #include "message-dialogs.h" -#include "add-common.h" #include "get-builder.h" -#include "../common/common.h" #include "gui-common.h" diff --git a/src/gui/app.c b/src/gui/app.c index 88e4cd47..73e3cd24 100644 --- a/src/gui/app.c +++ b/src/gui/app.c @@ -4,7 +4,7 @@ #include #include #include "otpclient.h" -#include "gquarks.h" +#include "../common/gquarks.h" #include "imports.h" #include "../common/exports.h" #include "message-dialogs.h" @@ -15,7 +15,7 @@ #include "change-db-cb.h" #include "new-db-cb.h" #include "../common/common.h" -#include "secret-schema.h" +#include "../common/secret-schema.h" #include "change-pwd-cb.h" #include "settings-cb.h" #include "shortcuts-cb.h" @@ -25,6 +25,7 @@ #include "show-qr-cb.h" #include "dbinfo-cb.h" #include "change-file-cb.h" +#include "gui-common.h" #ifndef IS_FLATPAK static gchar *get_db_path (AppData *app_data); @@ -238,7 +239,8 @@ activate (GtkApplication *app, "\nIf you need more info, please visit the project's wiki"); show_message_dialog (app_data->main_window, msg, GTK_MESSAGE_INFO); GError *tmp_err = NULL; - update_and_reload_db (app_data, app_data->db_data, FALSE, &tmp_err); + update_db (app_data->db_data, &tmp_err); + reload_db (app_data->db_data, &tmp_err); g_clear_error (&tmp_err); } diff --git a/src/gui/change-db-cb.c b/src/gui/change-db-cb.c index d2edee83..4cdfb0d5 100644 --- a/src/gui/change-db-cb.c +++ b/src/gui/change-db-cb.c @@ -6,7 +6,7 @@ #include "db-misc.h" #include "password-cb.h" #include "db-actions.h" -#include "secret-schema.h" +#include "../common/secret-schema.h" #include "change-file-cb.h" int diff --git a/src/gui/change-pwd-cb.c b/src/gui/change-pwd-cb.c index 9e57c9e0..6039960b 100644 --- a/src/gui/change-pwd-cb.c +++ b/src/gui/change-pwd-cb.c @@ -5,8 +5,7 @@ #include "message-dialogs.h" #include "db-misc.h" #include "password-cb.h" -#include "../common/common.h" -#include "secret-schema.h" +#include "../common/secret-schema.h" #include "otpclient.h" @@ -21,7 +20,15 @@ change_password_cb (GSimpleAction *simple __attribute__((unused)), if (pwd != NULL) { app_data->db_data->key = pwd; GError *err = NULL; - update_and_reload_db (app_data, app_data->db_data, FALSE, &err); + update_db (app_data->db_data, &err); + if (err != NULL) { + show_message_dialog (app_data->main_window, err->message, GTK_MESSAGE_ERROR); + GtkApplication *app = gtk_window_get_application (GTK_WINDOW(app_data->main_window)); + destroy_cb (app_data->main_window, app_data); + g_application_quit (G_APPLICATION(app)); + return; + } + reload_db (app_data->db_data, &err); if (err != NULL) { show_message_dialog (app_data->main_window, err->message, GTK_MESSAGE_ERROR); GtkApplication *app = gtk_window_get_application (GTK_WINDOW(app_data->main_window)); diff --git a/src/gui/data.h b/src/gui/data.h index 5aa60aa8..16cc52b7 100644 --- a/src/gui/data.h +++ b/src/gui/data.h @@ -2,38 +2,18 @@ #include #include +#include "../common/db-common.h" #define DBUS_SERVICES 4 G_BEGIN_DECLS -typedef struct db_data_t { - gchar *db_path; - - gchar *key; - - json_t *json_data; - - GSList *objects_hash; - - GSList *data_to_add; - - gint32 max_file_size_from_memlock; - - gchar *last_hotp; - GDateTime *last_hotp_update; - - gboolean key_stored; -} DatabaseData; - - typedef struct app_data_t { GtkBuilder *builder; GtkBuilder *add_popover_builder; GtkBuilder *settings_popover_builder; GtkWidget *main_window; - GtkWidget *info_bar; GtkTreeView *tree_view; GtkClipboard *clipboard; diff --git a/src/gui/db-misc.c b/src/gui/db-misc.c index bebadbc4..ad0101bb 100644 --- a/src/gui/db-misc.c +++ b/src/gui/db-misc.c @@ -1,117 +1,18 @@ -#include #include -#include -#include #include "db-misc.h" #include "otpclient.h" -#include "file-size.h" -#include "gquarks.h" -#include "../common/common.h" +#include "../common/gquarks.h" +#include "gui-common.h" -typedef struct header_data_t { - guint8 iv[IV_SIZE]; - guint8 salt[KDF_SALT_SIZE]; -} HeaderData; - -static void reload_db (DatabaseData *db_data, - GError **err); - -static void update_db (DatabaseData *db_data, - GError **err); - -static gpointer encrypt_db (const gchar *db_path, - const gchar *in_memory_json, - const gchar *password, - GError **err); - -static inline void add_to_json (gpointer list_elem, - gpointer json_array); - -static gchar *decrypt_db (const gchar *db_path, - const gchar *password); - -static guchar *get_derived_key (const gchar *pwd, - HeaderData *header_data); - -static void backup_db (const gchar *path); - -static void restore_db (const gchar *path); - -static inline void json_free (gpointer data); - -static void cleanup_gfile (GFile *file, - gpointer stream, - GError *err); - -static void free_resources (gcry_cipher_hd_t hd, - guchar *derived_key, - guchar *enc_buf, - gchar *dec_buf, - HeaderData *header_data); - - -void -load_db (DatabaseData *db_data, - GError **err) -{ - if (!g_file_test (db_data->db_path, G_FILE_TEST_EXISTS)) { - g_set_error (err, missing_file_gquark (), MISSING_FILE_CODE, "Missing database file"); - db_data->json_data = NULL; - return; - } - - gchar *in_memory_json = decrypt_db (db_data->db_path, db_data->key); - if (in_memory_json == TAG_MISMATCH) { - g_set_error (err, bad_tag_gquark (), BAD_TAG_ERRCODE, "Either the file is corrupted or the password is wrong"); - return; - } else if (in_memory_json == KEY_DERIV_ERR) { - g_set_error (err, key_deriv_gquark (), KEY_DERIVATION_ERRCODE, "Error during key derivation"); - return; - } else if (in_memory_json == GENERIC_ERROR) { - g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "An error occurred, please check stderr"); - return; - } - - json_error_t jerr; - db_data->json_data = json_loads (in_memory_json, 0, &jerr); - gcry_free (in_memory_json); - if (db_data->json_data == NULL) { - gchar *msg = g_strconcat ("Error while loading json data: ", jerr.text, NULL); - g_set_error (err, memlock_error_gquark(), MEMLOCK_ERRCODE, "%s", msg); - return; - } - - gsize index; - json_t *obj; - json_array_foreach (db_data->json_data, index, obj) { - guint32 hash = json_object_get_hash (obj); - db_data->objects_hash = g_slist_append (db_data->objects_hash, g_memdup2 (&hash, sizeof (guint32))); - } -} +static void json_free (gpointer data); void -update_and_reload_db (AppData *app_data, - DatabaseData *db_data, - gboolean regenerate_model, - GError **err) +regenerate_model (AppData *app_data) { - update_db (db_data, err); - if (*err != NULL && !g_error_matches (*err, missing_file_gquark (), MISSING_FILE_CODE)) { - g_printerr ("%s\n", (*err)->message); - return; - } - reload_db (db_data, err); - if (*err != NULL && !g_error_matches (*err, missing_file_gquark (), MISSING_FILE_CODE)) { - g_printerr ("%s\n", (*err)->message); - return; - } - - if (regenerate_model) { - update_model (app_data); - g_slist_free_full (app_data->db_data->data_to_add, json_free); - app_data->db_data->data_to_add = NULL; - } + update_model (app_data); + g_slist_free_full (app_data->db_data->data_to_add, json_free); + app_data->db_data->data_to_add = NULL; } @@ -142,359 +43,16 @@ check_duplicate (gconstpointer data, } -void write_db_to_disk (DatabaseData *db_data, - GError **err) +void +write_db_to_disk (DatabaseData *db_data, + GError **err) { update_db (db_data, err); } static void -reload_db (DatabaseData *db_data, - GError **err) -{ - if (db_data->json_data != NULL) { - json_decref (db_data->json_data); - } - - g_slist_free_full (db_data->objects_hash, g_free); - db_data->objects_hash = NULL; - - load_db (db_data, err); -} - - -static void -update_db (DatabaseData *db_data, - GError **err) -{ - gboolean first_run = (db_data->json_data == NULL) ? TRUE : FALSE; - if (first_run == TRUE) { - db_data->json_data = json_array (); - } else { - // database is backed-up only if this is not the first run - backup_db (db_data->db_path); - } - - g_slist_foreach (db_data->data_to_add, add_to_json, db_data->json_data); - - gchar *plain_data = json_dumps (db_data->json_data, JSON_COMPACT); - - if (encrypt_db (db_data->db_path, plain_data, db_data->key, err) != NULL) { - if (!first_run) { - g_printerr ("Encrypting the new data failed, restoring original copy...\n"); - restore_db (db_data->db_path); - } else { - g_printerr ("Couldn't update the database (encrypt_db failed)\n"); - if (g_file_test (db_data->db_path, G_FILE_TEST_EXISTS)) { - if (g_unlink (db_data->db_path) == -1) { - g_printerr ("%s\n", _("Error while unlinking the file.")); - } - } - } - } else { - // database must be backed-up both before and after the update - backup_db (db_data->db_path); - } - - gcry_free (plain_data); -} - - -static inline void -add_to_json (gpointer list_elem, - gpointer json_array) -{ - json_array_append (json_array, json_deep_copy (list_elem)); -} - - -static gpointer -encrypt_db (const gchar *db_path, - const gchar *in_memory_json, - const gchar *password, - GError **err) -{ - GError *local_err = NULL; - HeaderData *header_data = g_new0 (HeaderData, 1); - - gcry_create_nonce (header_data->iv, IV_SIZE); - gcry_create_nonce (header_data->salt, KDF_SALT_SIZE); - - GFile *out_file = g_file_new_for_path (db_path); - GFileOutputStream *out_stream = g_file_replace (out_file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, &local_err); - if (local_err != NULL) { - g_printerr ("%s\n", local_err->message); - g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "Failed to replace existing file"); - cleanup_gfile (out_file, NULL, local_err); - g_free (header_data); - return GENERIC_ERROR; - } - if (g_output_stream_write (G_OUTPUT_STREAM (out_stream), header_data, sizeof (HeaderData), NULL, &local_err) == -1) { - g_printerr ("%s\n", local_err->message); - g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "Failed while writing header data to file"); - cleanup_gfile (out_file, out_stream, local_err); - g_free (header_data); - return GENERIC_ERROR; - } - - guchar *derived_key = get_derived_key (password, header_data); - if (derived_key == SECURE_MEMORY_ALLOC_ERR || derived_key == KEY_DERIV_ERR) { - g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "Failed to derive key.\nPlease check Secure Memory wiki page"); - cleanup_gfile (out_file, out_stream, local_err); - g_free (header_data); - return (gpointer)derived_key; - } - - gsize input_data_len = strlen (in_memory_json) + 1; - guchar *enc_buffer = g_malloc0 (input_data_len); - - gcry_cipher_hd_t hd = open_cipher_and_set_data (derived_key, header_data->iv, IV_SIZE); - if (hd == NULL) { - cleanup_gfile (out_file, out_stream, local_err); - free_resources (NULL, derived_key, enc_buffer, NULL, header_data); - return NULL; - } - - gpg_error_t gpg_err = gcry_cipher_authenticate (hd, header_data, sizeof (HeaderData)); - if (gpg_err) { - g_printerr ("%s\n", _("Error while processing the authenticated data.")); - cleanup_gfile (out_file, out_stream, local_err); - free_resources (hd, derived_key, enc_buffer, NULL, header_data); - return GENERIC_ERROR; - } - gpg_err = gcry_cipher_encrypt (hd, enc_buffer, input_data_len, in_memory_json, input_data_len); - if (gpg_err) { - g_printerr ("%s\n", _("Error while encrypting the data.")); - cleanup_gfile (out_file, out_stream, local_err); - free_resources (hd, derived_key, enc_buffer, NULL, header_data); - return GENERIC_ERROR; - } - - guchar tag[TAG_SIZE]; - gpg_err = gcry_cipher_gettag (hd, tag, TAG_SIZE); //append tag to outfile - if (gpg_err) { - g_printerr ("%s\n", _("Error while getting the tag.")); - cleanup_gfile (out_file, out_stream, local_err); - free_resources (hd, derived_key, enc_buffer, NULL, header_data); - return GENERIC_ERROR; - } - - if (g_output_stream_write (G_OUTPUT_STREAM(out_stream), enc_buffer, input_data_len, NULL, &local_err) == -1) { - g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "Failed while writing encrypted buffer to file"); - cleanup_gfile (out_file, out_stream, local_err); - free_resources (hd, derived_key, enc_buffer, NULL, header_data); - return GENERIC_ERROR; - } - if (g_output_stream_write (G_OUTPUT_STREAM(out_stream), tag, TAG_SIZE, NULL, &local_err) == -1) { - g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "Failed while writing tag data to file"); - cleanup_gfile (out_file, out_stream, local_err); - free_resources (hd, derived_key, enc_buffer, NULL, header_data); - return GENERIC_ERROR; - } - - free_resources (hd, derived_key, enc_buffer, NULL, header_data); - cleanup_gfile (out_file, out_stream, NULL); - - return NULL; -} - - -static gchar * -decrypt_db (const gchar *db_path, - const gchar *password) -{ - GError *err = NULL; - HeaderData *header_data = g_new0 (HeaderData, 1); - - goffset input_file_size = get_file_size (db_path); - - GFile *in_file = g_file_new_for_path (db_path); - GFileInputStream *in_stream = g_file_read (in_file, NULL, &err); - if (err != NULL) { - g_printerr ("%s\n", err->message); - cleanup_gfile (in_file, NULL, err); - g_free (header_data); - return GENERIC_ERROR; - } - if (g_input_stream_read (G_INPUT_STREAM (in_stream), header_data, sizeof (HeaderData), NULL, &err) == -1) { - g_printerr ("%s\n", err->message); - cleanup_gfile (in_file, in_stream, err); - g_free (header_data); - return GENERIC_ERROR; - } - - guchar tag[TAG_SIZE]; - if (!g_seekable_seek (G_SEEKABLE (in_stream), input_file_size - TAG_SIZE, G_SEEK_SET, NULL, &err)) { - g_printerr ("%s\n", err->message); - cleanup_gfile (in_file, in_stream, err); - g_free (header_data); - return GENERIC_ERROR; - } - if (g_input_stream_read (G_INPUT_STREAM (in_stream), tag, TAG_SIZE, NULL, &err) == -1) { - g_printerr ("%s\n", err->message); - cleanup_gfile (in_file, in_stream, err); - g_free (header_data); - return GENERIC_ERROR; - } - - gsize enc_buf_size = input_file_size - sizeof (HeaderData) - TAG_SIZE; - guchar *enc_buf = g_malloc0 (enc_buf_size); - - if (!g_seekable_seek (G_SEEKABLE (in_stream), sizeof (HeaderData), G_SEEK_SET, NULL, &err)) { - g_printerr ("%s\n", err->message); - cleanup_gfile (in_file, in_stream, err); - - return GENERIC_ERROR; - } - if (g_input_stream_read (G_INPUT_STREAM (in_stream), enc_buf, enc_buf_size, NULL, &err) == -1) { - g_printerr ("%s\n", err->message); - cleanup_gfile (in_file, in_stream, err); - free_resources (NULL, NULL, enc_buf, NULL, header_data); - return GENERIC_ERROR; - } - g_object_unref (in_stream); - g_object_unref (in_file); - - guchar *derived_key = get_derived_key (password, header_data); - if (derived_key == SECURE_MEMORY_ALLOC_ERR || derived_key == KEY_DERIV_ERR) { - free_resources (NULL, NULL, enc_buf, NULL, header_data); - return (gpointer)derived_key; - } - - gcry_cipher_hd_t hd = open_cipher_and_set_data (derived_key, header_data->iv, IV_SIZE); - if (hd == NULL) { - free_resources (NULL, derived_key, enc_buf, NULL, header_data); - return GENERIC_ERROR; - } - - gpg_error_t gpg_err = gcry_cipher_authenticate (hd, header_data, sizeof (HeaderData)); - if (gpg_err) { - g_printerr ("%s\n", _("Error while processing the authenticated data.")); - free_resources (hd, derived_key, enc_buf, NULL, header_data); - return GENERIC_ERROR; - } - - gchar *dec_buf = gcry_calloc_secure (enc_buf_size, 1); - if (dec_buf == NULL) { - g_printerr ("%s\n", _("Error while allocating secure memory.")); - free_resources (hd, derived_key, enc_buf, NULL, header_data); - return GENERIC_ERROR; - } - gpg_err = gcry_cipher_decrypt (hd, dec_buf, enc_buf_size, enc_buf, enc_buf_size); - if (gpg_err) { - g_printerr ("%s\n", _("Error while decrypting the data.")); - free_resources (hd, derived_key, enc_buf, dec_buf, header_data); - return GENERIC_ERROR; - } - if (gcry_err_code (gcry_cipher_checktag (hd, tag, TAG_SIZE)) == GPG_ERR_CHECKSUM) { - free_resources (hd, derived_key, enc_buf, dec_buf, header_data); - return TAG_MISMATCH; - } - - free_resources (hd, derived_key, enc_buf, NULL, header_data); - - return dec_buf; -} - - -static guchar * -get_derived_key (const gchar *pwd, - HeaderData *header_data) -{ - gsize key_len = gcry_cipher_get_algo_keylen (GCRY_CIPHER_AES256); - gsize pwd_len = g_utf8_strlen (pwd, -1) + 1; - - guchar *derived_key = gcry_malloc_secure (key_len); - if (derived_key == NULL) { - g_printerr ("%s\n", _("Couldn't allocate the needed secure memory.")); - return SECURE_MEMORY_ALLOC_ERR; - } - - gpg_error_t ret = gcry_kdf_derive (pwd, pwd_len, GCRY_KDF_PBKDF2, GCRY_MD_SHA512, header_data->salt, KDF_SALT_SIZE, KDF_ITERATIONS, key_len, derived_key); - if (ret != 0) { - gcry_free (derived_key); - g_printerr ("%s\n", _("Error during key derivation.")); - return KEY_DERIV_ERR; - } - return derived_key; -} - - -static void -perform_backup_restore (const gchar *path, - gboolean is_backup) -{ - GError *err = NULL; - gchar *src_path = is_backup ? g_strdup (path) : g_strconcat (path, ".bak", NULL); - gchar *dst_path = is_backup ? g_strconcat (path, ".bak", NULL) : g_strdup (path); - - GFile *src = g_file_new_for_path (src_path); - GFile *dst = g_file_new_for_path (dst_path); - - g_free (src_path); - g_free (dst_path); - - if (!g_file_copy (src, dst, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS, NULL, NULL, NULL, &err)) { - g_printerr ("Couldn't %s: %s\n", is_backup ? "create the backup" : "restore the backup", err->message); - g_clear_error (&err); - } else { - g_print("%s\n", is_backup ? _("Backup copy successfully created.") : _("Backup copy successfully restored.")); - } - - g_object_unref (src); - g_object_unref (dst); -} - - -static void -backup_db (const gchar *path) -{ - perform_backup_restore (path, TRUE); -} - - -static void -restore_db (const gchar *path) -{ - perform_backup_restore (path, FALSE); -} - - -static inline void json_free (gpointer data) { json_decref (data); -} - - -static void -cleanup_gfile (GFile *file, - gpointer stream, - GError *err) -{ - g_object_unref (file); - g_clear_error (&err); - - if (stream != NULL) - g_object_unref (stream); -} - - -static void -free_resources (gcry_cipher_hd_t hd, - guchar *derived_key, - guchar *enc_buf, - gchar *dec_buf, - HeaderData *header_data) -{ - g_free (enc_buf); - g_free (header_data); - - if (hd != NULL) - gcry_cipher_close (hd); - if (derived_key != NULL) - gcry_free (derived_key); - if (dec_buf != NULL) - gcry_free (dec_buf); } \ No newline at end of file diff --git a/src/gui/db-misc.h b/src/gui/db-misc.h index 1e074279..6bc3deef 100644 --- a/src/gui/db-misc.h +++ b/src/gui/db-misc.h @@ -1,35 +1,20 @@ #pragma once #include +#include "../common/db-common.h" #include "data.h" G_BEGIN_DECLS -#define GENERIC_ERROR (gpointer)1 -#define TAG_MISMATCH (gpointer)2 -#define SECURE_MEMORY_ALLOC_ERR (gpointer)3 -#define KEY_DERIV_ERR (gpointer)4 +void load_new_db (AppData *app_data, + GError **err); -#define IV_SIZE 16 -#define KDF_ITERATIONS 100000 -#define KDF_SALT_SIZE 32 -#define TAG_SIZE 16 +void regenerate_model (AppData *app_data); -void load_db (DatabaseData *db_data, - GError **error); +void write_db_to_disk (DatabaseData *db_data, + GError **err); -void update_and_reload_db (AppData *app_data, - DatabaseData *db_data, - gboolean regenerate_model, - GError **err); - -void load_new_db (AppData *app_data, - GError **err); - -void write_db_to_disk (DatabaseData *db_data, - GError **err); - -gint check_duplicate (gconstpointer data, - gconstpointer user_data); +gint check_duplicate (gconstpointer data, + gconstpointer user_data); G_END_DECLS diff --git a/src/gui/edit-row-cb.c b/src/gui/edit-row-cb.c index 3d4fbe1f..42f5cbc9 100644 --- a/src/gui/edit-row-cb.c +++ b/src/gui/edit-row-cb.c @@ -6,8 +6,7 @@ #include "get-builder.h" #include "message-dialogs.h" #include "gui-common.h" -#include "gquarks.h" -#include "../common/common.h" +#include "../common/gquarks.h" typedef struct edit_data_t { GtkListStore *list_store; @@ -60,11 +59,19 @@ edit_row_cb (GSimpleAction *simple __attribute__((unused)), } GError *err = NULL; - update_and_reload_db (app_data, app_data->db_data, TRUE, &err); + update_db (app_data->db_data, &err); if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) { show_message_dialog (app_data->main_window, err->message, GTK_MESSAGE_ERROR); + goto end; } + reload_db (app_data->db_data, &err); + if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) { + show_message_dialog (app_data->main_window, err->message, GTK_MESSAGE_ERROR); + goto end; + } + regenerate_model (app_data); + end: g_free (edit_data->new_label); g_free (edit_data->new_issuer); if (edit_data->new_secret != NULL) { diff --git a/src/gui/gui-common.c b/src/gui/gui-common.c index 3b8b8ac4..e480eda3 100644 --- a/src/gui/gui-common.c +++ b/src/gui/gui-common.c @@ -1,8 +1,11 @@ #include #include +#include #include "message-dialogs.h" #include "add-common.h" +#include "gui-common.h" #include "../common/common.h" +#include "google-migration.pb-c.h" void @@ -97,4 +100,226 @@ parse_uris_migration (AppData *app_data, } return return_err_msg; +} + + +gchar * +g_trim_whitespace (const gchar *str) +{ + if (g_utf8_strlen (str, -1) == 0) { + return NULL; + } + gchar *sec_buf = gcry_calloc_secure (strlen (str) + 1, 1); + int pos = 0; + for (int i = 0; str[i]; i++) { + if (str[i] != ' ') { + sec_buf[pos++] = str[i]; + } + } + sec_buf[pos] = '\0'; + gchar *secubf_newpos = (gchar *)gcry_realloc (sec_buf, strlen (sec_buf) + 1); + + return secubf_newpos; +} + + +// Backported from Glib (needed by below function) +static int +unescape_character (const char *scanner) +{ + int first_digit; + int second_digit; + + first_digit = g_ascii_xdigit_value (*scanner++); + if (first_digit < 0) + return -1; + + second_digit = g_ascii_xdigit_value (*scanner++); + if (second_digit < 0) + return -1; + + return (first_digit << 4) | second_digit; +} + + +// Backported from Glib. The only difference is that it's using gcrypt to allocate a secure buffer. +gchar * +g_uri_unescape_string_secure (const gchar *escaped_string, + const gchar *illegal_characters) +{ + if (escaped_string == NULL) + return NULL; + + const gchar *escaped_string_end = escaped_string + g_utf8_strlen (escaped_string, -1); + + gchar *result = gcry_calloc_secure (escaped_string_end - escaped_string + 1, 1); + gchar *out = result; + + const gchar *in; + gint character; + for (in = escaped_string; in < escaped_string_end; in++) { + character = *in; + + if (*in == '%') { + in++; + if (escaped_string_end - in < 2) { + // Invalid escaped char (to short) + gcry_free (result); + return NULL; + } + + character = unescape_character (in); + + // Check for an illegal character. We consider '\0' illegal here. + if (character <= 0 || + (illegal_characters != NULL && + strchr (illegal_characters, (char)character) != NULL)) { + gcry_free (result); + return NULL; + } + + in++; // The other char will be eaten in the loop header + } + *out++ = (char)character; + } + + *out = '\0'; + + return result; +} + + +guchar * +g_base64_decode_secure (const gchar *text, + gsize *out_len) +{ + guchar *ret; + gsize input_length; + gint state = 0; + guint save = 0; + + g_return_val_if_fail (text != NULL, NULL); + g_return_val_if_fail (out_len != NULL, NULL); + + input_length = g_utf8_strlen (text, -1); + + /* We can use a smaller limit here, since we know the saved state is 0, + +1 used to avoid calling g_malloc0(0), and hence returning NULL */ + ret = gcry_calloc_secure ((input_length / 4) * 3 + 1, 1); + + *out_len = g_base64_decode_step (text, input_length, ret, &state, &save); + + return ret; +} + + +GSList * +decode_migration_data (const gchar *encoded_uri) +{ + const gchar *encoded_uri_copy = encoded_uri; + if (g_ascii_strncasecmp (encoded_uri_copy, "otpauth-migration://offline?data=", 33) != 0) { + return NULL; + } + encoded_uri_copy += 33; + gsize out_len; + gchar *unesc_str = g_uri_unescape_string_secure (encoded_uri_copy, NULL); + guchar *data = g_base64_decode_secure (unesc_str, &out_len); + gcry_free (unesc_str); + + GSList *uris = NULL; + GString *uri = NULL; + MigrationPayload *msg = migration_payload__unpack (NULL, out_len, data); + gcry_free (data); + for (gint i = 0; i < msg->n_otp_parameters; i++) { + uri = g_string_new ("otpauth://"); + if (msg->otp_parameters[i]->type == 1) { + g_string_append (uri, "hotp/"); + } else if (msg->otp_parameters[i]->type == 2) { + g_string_append (uri, "totp/"); + } else { + g_printerr ("OTP type not recognized, skipping %s\n", msg->otp_parameters[i]->name); + goto end; + } + + g_string_append (uri, msg->otp_parameters[i]->name); + g_string_append (uri, "?"); + + if (msg->otp_parameters[i]->algorithm == 1) { + g_string_append (uri, "algorithm=SHA1&"); + } else if (msg->otp_parameters[i]->algorithm == 2) { + g_string_append (uri, "algorithm=SHA256&"); + } else if (msg->otp_parameters[i]->algorithm == 3) { + g_string_append (uri, "algorithm=SHA512&"); + } else { + g_printerr ("Algorithm type not supported, skipping %s\n", msg->otp_parameters[i]->name); + goto end; + } + + if (msg->otp_parameters[i]->digits == 1) { + g_string_append (uri, "digits=6&"); + } else if (msg->otp_parameters[i]->digits == 2) { + g_string_append (uri, "digits=8&"); + } else { + g_printerr ("Algorithm type not supported, skipping %s\n", msg->otp_parameters[i]->name); + goto end; + } + + if (msg->otp_parameters[i]->issuer != NULL) { + g_string_append (uri, "issuer="); + g_string_append (uri, msg->otp_parameters[i]->issuer); + g_string_append (uri, "&"); + } + + if (msg->otp_parameters[i]->type == 1) { + g_string_append (uri, "counter="); + g_string_append_printf(uri, "%ld", msg->otp_parameters[i]->counter); + g_string_append (uri, "&"); + } + + cotp_error_t b_err; + gchar *b32_encoded_secret = base32_encode (msg->otp_parameters[i]->secret.data, msg->otp_parameters[i]->secret.len, &b_err); + if (b32_encoded_secret == NULL) { + g_printerr ("Error while encoding the secret (error code %d)\n", b_err); + goto end; + } + + g_string_append (uri, "secret="); + g_string_append (uri, b32_encoded_secret); + + uris = g_slist_append (uris, g_strdup (uri->str)); + + end: + g_string_free (uri, TRUE); + } + + migration_payload__free_unpacked (msg, NULL); + + return uris; +} + + + + +GKeyFile * +get_kf_ptr (void) +{ + GError *err = NULL; + GKeyFile *kf = g_key_file_new (); + gchar *cfg_file_path; +#ifndef IS_FLATPAK + cfg_file_path = g_build_filename (g_get_user_config_dir (), "otpclient.cfg", NULL); +#else + cfg_file_path = g_build_filename (g_get_user_data_dir (), "otpclient.cfg", NULL); +#endif + if (g_file_test (cfg_file_path, G_FILE_TEST_EXISTS)) { + if (g_key_file_load_from_file (kf, cfg_file_path, G_KEY_FILE_NONE, &err)) { + g_free (cfg_file_path); + return kf; + } + g_printerr ("%s\n", err->message); + g_clear_error (&err); + } + g_free (cfg_file_path); + g_key_file_free (kf); + return NULL; } \ No newline at end of file diff --git a/src/gui/gui-common.h b/src/gui/gui-common.h index 977057f6..b7414869 100644 --- a/src/gui/gui-common.h +++ b/src/gui/gui-common.h @@ -6,28 +6,40 @@ G_BEGIN_DECLS -void icon_press_cb (GtkEntry *entry, - gint position, - GdkEventButton *event, - gpointer data); - -guint get_row_number_from_iter (GtkListStore *list_store, - GtkTreeIter iter); - -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); - -void send_ok_cb (GtkWidget *entry, - gpointer user_data); - -gchar *parse_uris_migration (AppData *app_data, - const gchar *user_uri, - gboolean google_migration); +void icon_press_cb (GtkEntry *entry, + gint position, + GdkEventButton *event, + gpointer data); + +guint get_row_number_from_iter (GtkListStore *list_store, + GtkTreeIter iter); + +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); + +void send_ok_cb (GtkWidget *entry, + gpointer user_data); + +gchar *parse_uris_migration (AppData *app_data, + const gchar *user_uri, + gboolean google_migration); + +gchar *g_trim_whitespace (const gchar *str); + +GSList *decode_migration_data (const gchar *encoded_uri); + +gchar *g_uri_unescape_string_secure (const gchar *escaped_string, + const gchar *illegal_characters); + +guchar *g_base64_decode_secure (const gchar *text, + gsize *out_len); + +GKeyFile *get_kf_ptr (void); G_END_DECLS diff --git a/src/gui/imports.c b/src/gui/imports.c index b0b27a00..08eed4d4 100644 --- a/src/gui/imports.c +++ b/src/gui/imports.c @@ -4,8 +4,7 @@ #include "imports.h" #include "password-cb.h" #include "message-dialogs.h" -#include "gquarks.h" -#include "../common/common.h" +#include "../common/gquarks.h" #include "gui-common.h" #include "db-misc.h" #include "../common/get-providers-data.h" @@ -61,10 +60,15 @@ update_db_from_otps (GSList *otps, AppData *app_data) } GError *err = NULL; - update_and_reload_db (app_data, app_data->db_data, TRUE, &err); + update_db (app_data->db_data, &err); if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) { return g_strdup (err->message); } + reload_db (app_data->db_data, &err); + if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) { + return g_strdup (err->message); + } + regenerate_model (app_data); return NULL; } diff --git a/src/gui/imports.h b/src/gui/imports.h index f1e666ca..40b02def 100644 --- a/src/gui/imports.h +++ b/src/gui/imports.h @@ -17,25 +17,6 @@ G_BEGIN_DECLS #define GOOGLE_MIGRATION_FILE_ACTION_NAME "import_google_qr_file" #define GOOGLE_MIGRATION_WEBCAM_ACTION_NAME "import_google_qr_webcam" -typedef struct otp_object_t { - gchar *type; - - gchar *algo; - - guint32 digits; - - union { - guint32 period; - guint64 counter; - }; - - gchar *account_name; - - gchar *issuer; - - gchar *secret; -} otp_t; - void select_file_cb (GSimpleAction *simple, GVariant *parameter, gpointer user_data); diff --git a/src/gui/liststore-misc.c b/src/gui/liststore-misc.c index f8d02cc0..d073091e 100644 --- a/src/gui/liststore-misc.c +++ b/src/gui/liststore-misc.c @@ -3,7 +3,7 @@ #include #include "treeview.h" #include "liststore-misc.h" -#include "gquarks.h" +#include "../common/gquarks.h" #include "../common/common.h" @@ -150,9 +150,14 @@ set_otp_data (OtpData *otp_data, otp_data->counter = json_integer_value (json_object_get (obj, "counter")); // every time HOTP is accessed, counter must be increased json_object_set (obj, "counter", json_integer (otp_data->counter + 1)); - update_and_reload_db (app_data, app_data->db_data, FALSE, &err); + update_db (app_data->db_data, &err); if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) { g_printerr ("%s\n", err->message); + } else { + reload_db (app_data->db_data, &err); + if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) { + g_printerr ("%s\n", err->message); + } } } else { otp_data->period = (gint)json_integer_value (json_object_get (obj, "period")); diff --git a/src/gui/manual-add-cb.c b/src/gui/manual-add-cb.c index c5aafc79..a363f20b 100644 --- a/src/gui/manual-add-cb.c +++ b/src/gui/manual-add-cb.c @@ -2,7 +2,7 @@ #include "db-misc.h" #include "gui-common.h" #include "manual-add-cb.h" -#include "gquarks.h" +#include "../common/gquarks.h" #include "message-dialogs.h" #include "get-builder.h" @@ -47,10 +47,15 @@ manual_add_cb (GSimpleAction *simple __attribute__((unused)), result = gtk_dialog_run (GTK_DIALOG(widgets->dialog)); if (result == GTK_RESPONSE_OK) { if (parse_user_data (widgets, app_data->db_data)) { - update_and_reload_db (app_data, app_data->db_data, TRUE, &err); + update_db (app_data->db_data, &err); if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) { show_message_dialog (app_data->main_window, err->message, GTK_MESSAGE_ERROR); } + reload_db (app_data->db_data, &err); + if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) { + show_message_dialog (app_data->main_window, err->message, GTK_MESSAGE_ERROR); + } + regenerate_model (app_data); retry = FALSE; } } diff --git a/src/gui/new-db-cb.c b/src/gui/new-db-cb.c index 8019828c..28a181bd 100644 --- a/src/gui/new-db-cb.c +++ b/src/gui/new-db-cb.c @@ -6,7 +6,7 @@ #include "message-dialogs.h" #include "password-cb.h" #include "db-actions.h" -#include "secret-schema.h" +#include "../common/secret-schema.h" #include "change-file-cb.h" int diff --git a/src/gui/parse-data.c b/src/gui/parse-data.c index c2a0aa0e..b0c23180 100644 --- a/src/gui/parse-data.c +++ b/src/gui/parse-data.c @@ -5,10 +5,9 @@ #include #include "db-misc.h" #include "manual-add-cb.h" -#include "gquarks.h" +#include "../common/gquarks.h" #include "message-dialogs.h" #include "gui-common.h" -#include "../common/common.h" static gboolean is_input_valid (GtkWidget *dialog, diff --git a/src/gui/password-cb.c b/src/gui/password-cb.c index f492224a..66ddc388 100644 --- a/src/gui/password-cb.c +++ b/src/gui/password-cb.c @@ -3,7 +3,6 @@ #include "gui-common.h" #include "message-dialogs.h" #include "get-builder.h" -#include "../common/common.h" typedef struct entrywidgets_t { GtkWidget *entry_old; diff --git a/src/gui/qrcode-parser.c b/src/gui/qrcode-parser.c index 1906b21f..f2dcc826 100644 --- a/src/gui/qrcode-parser.c +++ b/src/gui/qrcode-parser.c @@ -4,6 +4,7 @@ #include #include #include "../common/common.h" +#include "gui-common.h" typedef struct image_data_t { gulong width; diff --git a/src/gui/settings-cb.c b/src/gui/settings-cb.c index c964acf2..9729baf7 100644 --- a/src/gui/settings-cb.c +++ b/src/gui/settings-cb.c @@ -1,11 +1,10 @@ #include #include #include -#include "otpclient.h" #include "message-dialogs.h" #include "get-builder.h" -#include "secret-schema.h" -#include "../common/common.h" +#include "../common/secret-schema.h" +#include "gui-common.h" typedef struct settings_data_t { GtkWidget *dss_switch; diff --git a/src/gui/show-qr-cb.c b/src/gui/show-qr-cb.c index c96bd180..3ff7c665 100644 --- a/src/gui/show-qr-cb.c +++ b/src/gui/show-qr-cb.c @@ -4,9 +4,10 @@ #include #include #include "data.h" -#include "parse-uri.h" +#include "../common/parse-uri.h" #include "get-builder.h" #include "message-dialogs.h" +#include "gui-common.h" #define INCHES_PER_METER (100.0/2.54) #define SIZE 3 @@ -24,11 +25,18 @@ show_qr_cb (GSimpleAction *simple __attribute__((unused)), { AppData *app_data = (AppData *)user_data; - gchar *otpauth_uri = get_otpauth_uri (app_data, NULL); - if (otpauth_uri == NULL) { + GtkTreeModel *model = gtk_tree_view_get_model (app_data->tree_view); + GtkListStore *list_store = GTK_LIST_STORE(model); + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (app_data->tree_view), &model, &iter) == FALSE) { show_message_dialog (app_data->main_window, "Error: a row must be selected in order to get the QR Code.", GTK_MESSAGE_ERROR); return; } + + guint row_number = get_row_number_from_iter (list_store, iter); + json_t *db_obj = json_array_get (app_data->db_data->json_data, row_number); + gchar *otpauth_uri = get_otpauth_uri (db_obj); QRcode *qr = QRcode_encodeString8bit ((const gchar *)otpauth_uri, 0, QR_ECLEVEL_H); write_png (qr); g_free (otpauth_uri); diff --git a/src/gui/treeview.c b/src/gui/treeview.c index 340726f9..a1539de5 100644 --- a/src/gui/treeview.c +++ b/src/gui/treeview.c @@ -3,7 +3,6 @@ #include "otpclient.h" #include "liststore-misc.h" #include "message-dialogs.h" -#include "../common/common.h" typedef struct parsed_json_data_t { @@ -128,11 +127,18 @@ delete_rows_cb (GtkTreeView *tree_view, } GError *err = NULL; - update_and_reload_db (app_data, app_data->db_data, FALSE, &err); + update_db (app_data->db_data, &err); if (err != NULL) { gchar *msg = g_strconcat ("The database update FAILED. The error message is:\n", err->message, NULL); show_message_dialog (app_data->main_window, msg, GTK_MESSAGE_ERROR); g_free (msg); + } else { + reload_db (app_data->db_data, &err); + if (err != NULL) { + gchar *msg = g_strconcat ("The database update FAILED. The error message is:\n", err->message, NULL); + show_message_dialog (app_data->main_window, msg, GTK_MESSAGE_ERROR); + g_free (msg); + } } } @@ -233,13 +239,24 @@ reorder_db (AppData *app_data) // update the database and reload the changes GError *err = NULL; - update_and_reload_db (app_data, app_data->db_data, TRUE, &err); + update_db (app_data->db_data, &err); if (err != NULL) { - gchar *msg = g_strconcat ("[ERROR] Failed to update_and_reload_db: ", err->message, NULL); + gchar *msg = g_strconcat ("[ERROR] Failed to update the db: ", err->message, NULL); show_message_dialog (app_data->main_window, msg, GTK_MESSAGE_ERROR); g_free (msg); g_clear_error (&err); + return; } + reload_db (app_data->db_data, &err); + if (err != NULL) { + gchar *msg = g_strconcat ("[ERROR] Failed to reload the db: ", err->message, NULL); + show_message_dialog (app_data->main_window, msg, GTK_MESSAGE_ERROR); + g_free (msg); + g_clear_error (&err); + return; + } + regenerate_model (app_data); + g_slist_free (nodes_order_slist); } diff --git a/src/gui/webcam-add-cb.c b/src/gui/webcam-add-cb.c index 5d9c964a..b4ea0d20 100644 --- a/src/gui/webcam-add-cb.c +++ b/src/gui/webcam-add-cb.c @@ -4,7 +4,6 @@ #include "imports.h" #include "message-dialogs.h" #include "get-builder.h" -#include "../common/common.h" #include "gui-common.h" From 73c9d212c2d77d9b5985e978c9cd0ef5c001aa52 Mon Sep 17 00:00:00 2001 From: Paolo Stivanin Date: Sat, 9 Mar 2024 15:47:35 +0100 Subject: [PATCH 3/6] Check file size against memlock --- src/common/aegis.c | 2 +- src/common/common.c | 2 +- src/common/freeotp.c | 13 +++++++++---- src/common/get-providers-data.h | 2 ++ src/common/gquarks.h | 1 + src/common/twofas.c | 6 ++++++ src/gui/imports.c | 4 ++-- 7 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/common/aegis.c b/src/common/aegis.c index 95506a9f..fc386408 100644 --- a/src/common/aegis.c +++ b/src/common/aegis.c @@ -161,7 +161,7 @@ get_otps_from_encrypted_backup (const gchar *path, gsize out_len; guchar *b64decoded_db = g_base64_decode (json_string_value (json_object_get (json, "db")), &out_len); if (out_len > max_file_size) { - g_set_error (err, file_too_big_gquark (), FILE_TOO_BIG, "File is too big"); + g_set_error (err, file_too_big_gquark (), FILE_TOO_BIG, FILE_SIZE_SECMEM_MSG); g_free (tag); g_free (nonce); gcry_free (master_key); diff --git a/src/common/common.c b/src/common/common.c index b3ec3d86..303cc580 100644 --- a/src/common/common.c +++ b/src/common/common.c @@ -268,7 +268,7 @@ get_data_from_encrypted_backup (const gchar *path, } else if (enc_buf_size > max_file_size) { g_object_unref (in_stream); g_object_unref (in_file); - g_set_error (err, file_too_big_gquark (), FILE_TOO_BIG, "File is too big"); + g_set_error (err, file_too_big_gquark (), FILE_TOO_BIG, FILE_SIZE_SECMEM_MSG); return NULL; } diff --git a/src/common/freeotp.c b/src/common/freeotp.c index b2d8b907..c1904f68 100644 --- a/src/common/freeotp.c +++ b/src/common/freeotp.c @@ -9,18 +9,23 @@ GSList * -get_freeotpplus_data (const gchar *path, - GError **err) +get_freeotpplus_data (const gchar *path, + gint32 max_file_size, + GError **err) { GSList *otps = NULL; goffset fs = get_file_size (path); if (fs < 10) { - g_printerr ("Couldn't get the file size (file doesn't exit or wrong file selected\n"); + g_set_error (err, file_too_big_gquark (), GENERIC_ERRCODE, "Couldn't get the file size (file doesn't exit or wrong file selected."); + return NULL; + } + if (fs > max_file_size) { + g_set_error (err, file_too_big_gquark (), FILE_TOO_BIG, FILE_SIZE_SECMEM_MSG); return NULL; } gchar *sec_buf = gcry_calloc_secure (fs, 1); if (!g_file_get_contents (path, &sec_buf, NULL, err)) { - g_printerr("Couldn't read into memory the freeotp txt file\n"); + g_printerr("Couldn't read into memory the freeotp txt file.\n"); gcry_free (sec_buf); return NULL; } diff --git a/src/common/get-providers-data.h b/src/common/get-providers-data.h index 0e8379c9..9c971201 100644 --- a/src/common/get-providers-data.h +++ b/src/common/get-providers-data.h @@ -10,6 +10,7 @@ GSList *get_andotp_data (const gchar *path, GError **err); GSList *get_freeotpplus_data (const gchar *path, + gint32 max_file_size, GError **err); GSList *get_aegis_data (const gchar *path, @@ -24,6 +25,7 @@ GSList *get_authpro_data (const gchar *path, GSList *get_twofas_data (const gchar *path, const gchar *password, + gint32 max_file_size, GError **err); G_END_DECLS diff --git a/src/common/gquarks.h b/src/common/gquarks.h index 84d958a4..13ce9354 100644 --- a/src/common/gquarks.h +++ b/src/common/gquarks.h @@ -10,6 +10,7 @@ G_BEGIN_DECLS #define FILE_TOO_BIG 13 #define GENERIC_ERRCODE 14 #define MEMLOCK_ERRCODE 15 +#define FILE_SIZE_SECMEM_MSG "Selected file is too big. Please increase the secure memory size." GQuark missing_file_gquark (void); diff --git a/src/common/twofas.c b/src/common/twofas.c index e65364d1..f81a0da8 100644 --- a/src/common/twofas.c +++ b/src/common/twofas.c @@ -4,6 +4,7 @@ #include #include "gquarks.h" #include "common.h" +#include "file-size.h" #define TWOFAS_KDF_ITERS 10000 #define TWOFAS_SALT 256 @@ -46,8 +47,13 @@ static GSList *parse_twofas_json_data (const gchar *data, GSList * get_twofas_data (const gchar *path, const gchar *password, + gint32 max_file_size, GError **err) { + if (get_file_size (path) > max_file_size) { + g_set_error (err, file_too_big_gquark (), FILE_TOO_BIG, FILE_SIZE_SECMEM_MSG); + return NULL; + } return (password != NULL) ? get_otps_from_encrypted_backup (path, password, err) : get_otps_from_plain_backup (path, err); } diff --git a/src/gui/imports.c b/src/gui/imports.c index 08eed4d4..d8c9dc00 100644 --- a/src/gui/imports.c +++ b/src/gui/imports.c @@ -111,13 +111,13 @@ parse_data_and_update_db (AppData *app_data, if (g_strcmp0 (action_name, ANDOTP_IMPORT_ACTION_NAME) == 0 || g_strcmp0 (action_name, ANDOTP_IMPORT_PLAIN_ACTION_NAME) == 0) { content = get_andotp_data (filename, pwd, app_data->db_data->max_file_size_from_memlock, &err); } else if (g_strcmp0 (action_name, FREEOTPPLUS_IMPORT_ACTION_NAME) == 0) { - content = get_freeotpplus_data (filename, &err); + content = get_freeotpplus_data (filename, app_data->db_data->max_file_size_from_memlock, &err); } else if (g_strcmp0 (action_name, AEGIS_IMPORT_ACTION_NAME) == 0 || g_strcmp0 (action_name, AEGIS_IMPORT_ENC_ACTION_NAME) == 0) { content = get_aegis_data (filename, pwd, app_data->db_data->max_file_size_from_memlock, &err); } else if (g_strcmp0 (action_name, AUTHPRO_IMPORT_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, AUTHPRO_IMPORT_PLAIN_ACTION_NAME) == 0) { content = get_authpro_data (filename, pwd, app_data->db_data->max_file_size_from_memlock, &err); } else if (g_strcmp0 (action_name, TWOFAS_IMPORT_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, TWOFAS_IMPORT_PLAIN_ACTION_NAME) == 0) { - content = get_twofas_data (filename, pwd, &err); + content = get_twofas_data (filename, pwd, app_data->db_data->max_file_size_from_memlock, &err); } if (content == NULL) { From 4bf9e36380f63f7d86242b5f6ba85f7789e30b97 Mon Sep 17 00:00:00 2001 From: Paolo Stivanin Date: Mon, 11 Mar 2024 11:22:41 +0100 Subject: [PATCH 4/6] Big refactoring to import/export * decouple import/export from GTK, so that CLI can be built without it * add possibility to import files via the CLI --- src/cli/exec-action.c | 68 ++++++++++++++++++++---- src/cli/main.c | 67 +++++++++++++++++++++-- src/cli/main.h | 3 ++ src/common/common.c | 47 ++++++++++++++++ src/common/common.h | 11 ++++ src/common/db-common.c | 32 +++++++++++ src/common/db-common.h | 16 ++++-- src/common/exports.h | 41 -------------- src/common/import-export.c | 26 +++++++++ src/common/import-export.h | 44 +++++++++++++++ src/gui/add-common.c | 2 +- src/gui/add-from-qr.c | 6 +-- src/gui/app.c | 61 ++++++++++++--------- src/gui/db-misc.c | 14 +---- src/gui/db-misc.h | 3 -- src/gui/edit-row-cb.c | 3 +- src/gui/{exports.c => export-from-gui.c} | 32 +++++------ src/gui/{gui-common.c => gui-misc.c} | 32 +---------- src/gui/{gui-common.h => gui-misc.h} | 20 +++---- src/gui/{imports.c => import-from-gui.c} | 58 +++----------------- src/gui/imports.h | 30 ----------- src/gui/lock-app.c | 2 +- src/gui/manual-add-cb.c | 2 +- src/gui/parse-data.c | 2 +- src/gui/password-cb.c | 2 +- src/gui/qrcode-parser.c | 2 +- src/gui/settings-cb.c | 2 +- src/gui/show-qr-cb.c | 2 +- src/gui/ui/settings_popover.ui | 40 +++++++------- src/gui/webcam-add-cb.c | 6 +-- 30 files changed, 404 insertions(+), 272 deletions(-) delete mode 100644 src/common/exports.h create mode 100644 src/common/import-export.c create mode 100644 src/common/import-export.h rename src/gui/{exports.c => export-from-gui.c} (71%) rename src/gui/{gui-common.c => gui-misc.c} (89%) rename src/gui/{gui-common.h => gui-misc.h} (71%) rename src/gui/{imports.c => import-from-gui.c} (54%) delete mode 100644 src/gui/imports.h diff --git a/src/cli/exec-action.c b/src/cli/exec-action.c index 9878d946..549774d6 100644 --- a/src/cli/exec-action.c +++ b/src/cli/exec-action.c @@ -5,9 +5,9 @@ #include #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); @@ -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 @@ -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); @@ -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); @@ -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); @@ -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); @@ -164,6 +209,7 @@ gboolean exec_action (CmdlineOpts *cmdline_opts, } g_free (exported_file_path); } + return TRUE; } diff --git a/src/cli/main.c b/src/cli/main.c index 3849991c..5ff72bd1 100644 --- a/src/cli/main.c +++ b/src/cli/main.c @@ -2,6 +2,7 @@ #include #include #include "version.h" +#include "../common/import-export.h" #include "main.h" static gint handle_local_options (GApplication *application, @@ -15,6 +16,8 @@ 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); @@ -22,6 +25,14 @@ 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 @@ -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 @@ -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; @@ -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); @@ -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); diff --git a/src/cli/main.h b/src/cli/main.h index cf16dca6..4b7138c3 100644 --- a/src/cli/main.h +++ b/src/cli/main.h @@ -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; diff --git a/src/common/common.c b/src/common/common.c index 303cc580..bc4ca4a7 100644 --- a/src/common/common.c +++ b/src/common/common.c @@ -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; } \ No newline at end of file diff --git a/src/common/common.h b/src/common/common.h index 0edcd09c..d6a7ab24 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -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 diff --git a/src/common/db-common.c b/src/common/db-common.c index 1f42a727..ed4e5813 100644 --- a/src/common/db-common.c +++ b/src/common/db-common.c @@ -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, diff --git a/src/common/db-common.h b/src/common/db-common.h index c4b3b7c5..f973b57b 100644 --- a/src/common/db-common.h +++ b/src/common/db-common.h @@ -42,23 +42,29 @@ typedef struct db_data_t { } DatabaseData; -void load_db (DatabaseData *db_data, +void load_db (DatabaseData *db_data, GError **error); -void update_db (DatabaseData *db_data, +void update_db (DatabaseData *db_data, GError **err); -void reload_db (DatabaseData *db_data, +void reload_db (DatabaseData *db_data, GError **err); guchar *get_db_derived_key (const gchar *pwd, DbHeaderData *header_data); -void cleanup_db_gfile (GFile *file, +void add_otps_to_db (GSList *otps, + DatabaseData *db_data); + +gint check_duplicate (gconstpointer data, + gconstpointer user_data); + +void cleanup_db_gfile (GFile *file, gpointer stream, GError *err); -void free_db_resources (gcry_cipher_hd_t hd, +void free_db_resources (gcry_cipher_hd_t hd, guchar *derived_key, guchar *enc_buf, gchar *dec_buf, diff --git a/src/common/exports.h b/src/common/exports.h deleted file mode 100644 index 6bf79dc7..00000000 --- a/src/common/exports.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include - -G_BEGIN_DECLS - -#define ANDOTP_EXPORT_ACTION_NAME "export_andotp" -#define ANDOTP_EXPORT_PLAIN_ACTION_NAME "export_andotp_plain" -#define FREEOTPPLUS_EXPORT_ACTION_NAME "export_freeotpplus" -#define AEGIS_EXPORT_ACTION_NAME "export_aegis" -#define AEGIS_EXPORT_PLAIN_ACTION_NAME "export_aegis_plain" -#define AUTHPRO_EXPORT_ENC_ACTION_NAME "export_authpro_enc" -#define AUTHPRO_EXPORT_PLAIN_ACTION_NAME "export_authpro_plain" -#define TWOFAS_EXPORT_ENC_ACTION_NAME "export_twofas_enc" -#define TWOFAS_EXPORT_PLAIN_ACTION_NAME "export_twofas_plain" - - -void export_data_cb (GSimpleAction *simple, - GVariant *parameter, - gpointer user_data); - -gchar *export_andotp (const gchar *export_path, - const gchar *password, - json_t *json_db_data); - -gchar *export_freeotpplus (const gchar *export_path, - json_t *json_db_data); - -gchar *export_aegis (const gchar *export_path, - const gchar *password, - json_t *json_db_data); - -gchar *export_authpro (const gchar *export_path, - const gchar *password, - json_t *json_db_data); - -gchar *export_twofas (const gchar *export_path, - const gchar *password, - json_t *json_db_data); - -G_END_DECLS diff --git a/src/common/import-export.c b/src/common/import-export.c new file mode 100644 index 00000000..93b52c00 --- /dev/null +++ b/src/common/import-export.c @@ -0,0 +1,26 @@ +#include +#include "import-export.h" +#include "get-providers-data.h" + +GSList * +get_data_from_provider (const gchar *action_name, + const gchar *filename, + const gchar *pwd, + gint32 max_file_size_from_memlock, + GError **err) +{ + GSList *content = NULL; + if (g_strcmp0 (action_name, ANDOTP_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, ANDOTP_ENC_ACTION_NAME) == 0) { + content = get_andotp_data (filename, pwd, max_file_size_from_memlock, err); + } else if (g_strcmp0 (action_name, FREEOTPPLUS_PLAIN_ACTION_NAME) == 0) { + content = get_freeotpplus_data (filename, max_file_size_from_memlock, err); + } else if (g_strcmp0 (action_name, AEGIS_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, AEGIS_ENC_ACTION_NAME) == 0) { + content = get_aegis_data (filename, pwd, max_file_size_from_memlock, err); + } else if (g_strcmp0 (action_name, AUTHPRO_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, AUTHPRO_ENC_ACTION_NAME) == 0) { + content = get_authpro_data (filename, pwd, max_file_size_from_memlock, err); + } else if (g_strcmp0 (action_name, TWOFAS_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, TWOFAS_ENC_ACTION_NAME) == 0) { + content = get_twofas_data (filename, pwd, max_file_size_from_memlock, err); + } + + return content; +} \ No newline at end of file diff --git a/src/common/import-export.h b/src/common/import-export.h new file mode 100644 index 00000000..cad73cfb --- /dev/null +++ b/src/common/import-export.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +G_BEGIN_DECLS + +#define ANDOTP_PLAIN_ACTION_NAME "andotp_plain" +#define ANDOTP_ENC_ACTION_NAME "andotp_encrypted" +#define FREEOTPPLUS_PLAIN_ACTION_NAME "freeotpplus_plain" +#define AEGIS_PLAIN_ACTION_NAME "aegis_plain" +#define AEGIS_ENC_ACTION_NAME "aegis_encrypted" +#define AUTHPRO_PLAIN_ACTION_NAME "authpro_plain" +#define AUTHPRO_ENC_ACTION_NAME "authpro_encrypted" +#define TWOFAS_PLAIN_ACTION_NAME "twofas_plain" +#define TWOFAS_ENC_ACTION_NAME "twofas_encrypted" +#define GOOGLE_FILE_ACTION_NAME "import_google_qr_file" +#define GOOGLE_WEBCAM_ACTION_NAME "import_google_qr_webcam" + +GSList *get_data_from_provider (const gchar *action_name, + const gchar *filename, + const gchar *pwd, + gint32 max_file_size_from_memlock, + GError **err); + +gchar *export_andotp (const gchar *export_path, + const gchar *password, + json_t *json_db_data); + +gchar *export_freeotpplus (const gchar *export_path, + json_t *json_db_data); + +gchar *export_aegis (const gchar *export_path, + const gchar *password, + json_t *json_db_data); + +gchar *export_authpro (const gchar *export_path, + const gchar *password, + json_t *json_db_data); + +gchar *export_twofas (const gchar *export_path, + const gchar *password, + json_t *json_db_data); + +G_END_DECLS diff --git a/src/gui/add-common.c b/src/gui/add-common.c index 7f77dfb0..ed878ee4 100644 --- a/src/gui/add-common.c +++ b/src/gui/add-common.c @@ -1,6 +1,6 @@ #include #include "data.h" -#include "imports.h" +#include "gui-misc.h" #include "../common/parse-uri.h" static gchar *check_params (GSList *otps); diff --git a/src/gui/add-from-qr.c b/src/gui/add-from-qr.c index 4d04333a..fdf39626 100644 --- a/src/gui/add-from-qr.c +++ b/src/gui/add-from-qr.c @@ -2,11 +2,11 @@ #include #include #include -#include "imports.h" +#include "../common/import-export.h" #include "qrcode-parser.h" #include "message-dialogs.h" #include "get-builder.h" -#include "gui-common.h" +#include "gui-misc.h" typedef struct gtimeout_data_t { @@ -39,7 +39,7 @@ add_qr_from_file (GSimpleAction *simple, gpointer user_data) { const gchar *action_name = g_action_get_name (G_ACTION(simple)); - gboolean google_migration = (g_strcmp0 (action_name, GOOGLE_MIGRATION_FILE_ACTION_NAME) == 0) ? TRUE : FALSE; + gboolean google_migration = (g_strcmp0 (action_name, GOOGLE_FILE_ACTION_NAME) == 0) ? TRUE : FALSE; AppData *app_data = (AppData *)user_data; diff --git a/src/gui/app.c b/src/gui/app.c index 73e3cd24..4c01231d 100644 --- a/src/gui/app.c +++ b/src/gui/app.c @@ -5,8 +5,8 @@ #include #include "otpclient.h" #include "../common/gquarks.h" -#include "imports.h" -#include "../common/exports.h" +#include "gui-misc.h" +#include "../common/import-export.h" #include "message-dialogs.h" #include "password-cb.h" #include "get-builder.h" @@ -14,7 +14,6 @@ #include "lock-app.h" #include "change-db-cb.h" #include "new-db-cb.h" -#include "../common/common.h" #include "../common/secret-schema.h" #include "change-pwd-cb.h" #include "settings-cb.h" @@ -25,7 +24,6 @@ #include "show-qr-cb.h" #include "dbinfo-cb.h" #include "change-file-cb.h" -#include "gui-common.h" #ifndef IS_FLATPAK static gchar *get_db_path (AppData *app_data); @@ -445,27 +443,33 @@ create_main_window (gint width, gtk_widget_set_sensitive (lock_btn, FALSE); } + static GActionEntry import_menu_entries[] = { + { .name = ANDOTP_PLAIN_ACTION_NAME, .activate = import_data_cb }, + { .name = ANDOTP_ENC_ACTION_NAME, .activate = import_data_cb }, + { .name = FREEOTPPLUS_PLAIN_ACTION_NAME, .activate = import_data_cb }, + { .name = AEGIS_PLAIN_ACTION_NAME, .activate = import_data_cb }, + { .name = AEGIS_ENC_ACTION_NAME, .activate = import_data_cb }, + { .name = AUTHPRO_PLAIN_ACTION_NAME, .activate = import_data_cb }, + { .name = AUTHPRO_ENC_ACTION_NAME, .activate = import_data_cb }, + { .name = TWOFAS_PLAIN_ACTION_NAME, .activate = import_data_cb }, + { .name = TWOFAS_ENC_ACTION_NAME, .activate = import_data_cb }, + { .name = GOOGLE_FILE_ACTION_NAME, .activate = add_qr_from_file }, + { .name = GOOGLE_WEBCAM_ACTION_NAME, .activate = webcam_add_cb } + }; + + static GActionEntry export_menu_entries[] = { + { .name = ANDOTP_PLAIN_ACTION_NAME, .activate = export_data_cb }, + { .name = ANDOTP_ENC_ACTION_NAME, .activate = export_data_cb }, + { .name = FREEOTPPLUS_PLAIN_ACTION_NAME, .activate = export_data_cb }, + { .name = AEGIS_PLAIN_ACTION_NAME, .activate = export_data_cb }, + { .name = AEGIS_ENC_ACTION_NAME, .activate = export_data_cb }, + { .name = AUTHPRO_PLAIN_ACTION_NAME, .activate = export_data_cb }, + { .name = AUTHPRO_ENC_ACTION_NAME, .activate = export_data_cb }, + { .name = TWOFAS_PLAIN_ACTION_NAME, .activate = export_data_cb }, + { .name = TWOFAS_ENC_ACTION_NAME, .activate = export_data_cb } + }; + static GActionEntry settings_menu_entries[] = { - { .name = ANDOTP_IMPORT_ACTION_NAME, .activate = select_file_cb }, - { .name = ANDOTP_IMPORT_PLAIN_ACTION_NAME, .activate = select_file_cb }, - { .name = FREEOTPPLUS_IMPORT_ACTION_NAME, .activate = select_file_cb }, - { .name = AEGIS_IMPORT_ACTION_NAME, .activate = select_file_cb }, - { .name = AEGIS_IMPORT_ENC_ACTION_NAME, .activate = select_file_cb }, - { .name = AUTHPRO_IMPORT_ENC_ACTION_NAME, .activate = select_file_cb }, - { .name = AUTHPRO_IMPORT_PLAIN_ACTION_NAME, .activate = select_file_cb }, - { .name = TWOFAS_IMPORT_ENC_ACTION_NAME, .activate = select_file_cb }, - { .name = TWOFAS_IMPORT_PLAIN_ACTION_NAME, .activate = select_file_cb }, - { .name = ANDOTP_EXPORT_ACTION_NAME, .activate = export_data_cb }, - { .name = ANDOTP_EXPORT_PLAIN_ACTION_NAME, .activate = export_data_cb }, - { .name = FREEOTPPLUS_EXPORT_ACTION_NAME, .activate = export_data_cb }, - { .name = AEGIS_EXPORT_ACTION_NAME, .activate = export_data_cb }, - { .name = AEGIS_EXPORT_PLAIN_ACTION_NAME, .activate = export_data_cb }, - { .name = AUTHPRO_EXPORT_ENC_ACTION_NAME, .activate = export_data_cb }, - { .name = AUTHPRO_EXPORT_PLAIN_ACTION_NAME, .activate = export_data_cb }, - { .name = TWOFAS_EXPORT_ENC_ACTION_NAME, .activate = export_data_cb }, - { .name = TWOFAS_EXPORT_PLAIN_ACTION_NAME, .activate = export_data_cb }, - { .name = GOOGLE_MIGRATION_FILE_ACTION_NAME, .activate = add_qr_from_file }, - { .name = GOOGLE_MIGRATION_WEBCAM_ACTION_NAME, .activate = webcam_add_cb }, { .name = "create_newdb", .activate = new_db_cb }, { .name = "change_db", .activate = change_db_cb }, { .name = "change_pwd", .activate = change_password_cb }, @@ -486,10 +490,19 @@ create_main_window (gint width, GtkWidget *settings_popover = GTK_WIDGET(gtk_builder_get_object (app_data->settings_popover_builder, "settings_pop_id")); gtk_menu_button_set_popover (GTK_MENU_BUTTON(gtk_builder_get_object (app_data->builder, "settings_btn_id")), settings_popover); + GActionGroup *settings_actions = (GActionGroup *)g_simple_action_group_new (); g_action_map_add_action_entries (G_ACTION_MAP(settings_actions), settings_menu_entries, G_N_ELEMENTS (settings_menu_entries), app_data); gtk_widget_insert_action_group (settings_popover, "settings_menu", settings_actions); + GActionGroup *import_actions = (GActionGroup *)g_simple_action_group_new (); + g_action_map_add_action_entries (G_ACTION_MAP(import_actions), import_menu_entries, G_N_ELEMENTS (import_menu_entries), app_data); + gtk_widget_insert_action_group (settings_popover, "import_menu", import_actions); + + GActionGroup *export_actions = (GActionGroup *)g_simple_action_group_new (); + g_action_map_add_action_entries (G_ACTION_MAP(export_actions), export_menu_entries, G_N_ELEMENTS (export_menu_entries), app_data); + gtk_widget_insert_action_group (settings_popover, "export_menu", export_actions); + GtkWidget *add_popover = GTK_WIDGET(gtk_builder_get_object (app_data->add_popover_builder, "add_pop_id")); gtk_menu_button_set_popover (GTK_MENU_BUTTON(gtk_builder_get_object (app_data->builder, "add_btn_main_id")), add_popover); GActionGroup *add_actions = (GActionGroup *)g_simple_action_group_new (); diff --git a/src/gui/db-misc.c b/src/gui/db-misc.c index ad0101bb..076aba95 100644 --- a/src/gui/db-misc.c +++ b/src/gui/db-misc.c @@ -2,7 +2,7 @@ #include "db-misc.h" #include "otpclient.h" #include "../common/gquarks.h" -#include "gui-common.h" +#include "gui-misc.h" static void json_free (gpointer data); @@ -31,18 +31,6 @@ load_new_db (AppData *app_data, } -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 write_db_to_disk (DatabaseData *db_data, GError **err) diff --git a/src/gui/db-misc.h b/src/gui/db-misc.h index 6bc3deef..8b419d09 100644 --- a/src/gui/db-misc.h +++ b/src/gui/db-misc.h @@ -14,7 +14,4 @@ void regenerate_model (AppData *app_data); void write_db_to_disk (DatabaseData *db_data, GError **err); -gint check_duplicate (gconstpointer data, - gconstpointer user_data); - G_END_DECLS diff --git a/src/gui/edit-row-cb.c b/src/gui/edit-row-cb.c index 42f5cbc9..609d2d32 100644 --- a/src/gui/edit-row-cb.c +++ b/src/gui/edit-row-cb.c @@ -1,11 +1,10 @@ #include #include -#include "imports.h" #include "treeview.h" #include "db-misc.h" #include "get-builder.h" #include "message-dialogs.h" -#include "gui-common.h" +#include "gui-misc.h" #include "../common/gquarks.h" typedef struct edit_data_t { diff --git a/src/gui/exports.c b/src/gui/export-from-gui.c similarity index 71% rename from src/gui/exports.c rename to src/gui/export-from-gui.c index e795be95..c5bb38ff 100644 --- a/src/gui/exports.c +++ b/src/gui/export-from-gui.c @@ -3,11 +3,11 @@ #include #include "password-cb.h" #include "message-dialogs.h" -#include "../common/exports.h" +#include "../common/import-export.h" -static void show_ret_msg_dialog (GtkWidget *mainwin, - const gchar *fpath, - const gchar *ret_msg); +static void show_ret_msg_dialog (GtkWidget *mainwin, + const gchar *fpath, + const gchar *ret_msg); void @@ -27,8 +27,8 @@ export_data_cb (GSimpleAction *simple, gboolean encrypted = FALSE; gchar *password = NULL; - if (g_strcmp0 (action_name, "export_andotp") == 0 || g_strcmp0 (action_name, "export_aegis") == 0 || - g_strcmp0 (action_name, "export_authpro_enc") == 0 || g_strcmp0 (action_name, "export_twofas_enc") == 0) { + if (g_strcmp0 (action_name, ANDOTP_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, AEGIS_ENC_ACTION_NAME) == 0 || + g_strcmp0 (action_name, AUTHPRO_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, TWOFAS_ENC_ACTION_NAME) == 0) { password = prompt_for_password (app_data, NULL, NULL, TRUE); if (password == NULL) { return; @@ -52,15 +52,15 @@ export_data_cb (GSimpleAction *simple, gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(fl_diag), FALSE); const gchar *filename = NULL; - if (g_strcmp0 (action_name, ANDOTP_EXPORT_ACTION_NAME) == 0 || g_strcmp0 (action_name, ANDOTP_EXPORT_PLAIN_ACTION_NAME) == 0) { + if (g_strcmp0 (action_name, ANDOTP_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, ANDOTP_ENC_ACTION_NAME) == 0) { filename = (encrypted == TRUE) ? "andotp_exports.json.aes" : "andotp_exports.json"; - } else if (g_strcmp0 (action_name, FREEOTPPLUS_EXPORT_ACTION_NAME) == 0) { + } else if (g_strcmp0 (action_name, FREEOTPPLUS_PLAIN_ACTION_NAME) == 0) { filename = "freeotpplus-exports.txt"; - } else if (g_strcmp0 (action_name, AEGIS_EXPORT_ACTION_NAME) == 0 || g_strcmp0 (action_name, AEGIS_EXPORT_PLAIN_ACTION_NAME) == 0) { + } else if (g_strcmp0 (action_name, AEGIS_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, AEGIS_ENC_ACTION_NAME) == 0) { filename = (encrypted == TRUE) ? "aegis_encrypted.json" : "aegis_export_plain.json"; - } else if (g_strcmp0 (action_name, AUTHPRO_EXPORT_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, AUTHPRO_EXPORT_PLAIN_ACTION_NAME) == 0) { + } else if (g_strcmp0 (action_name, AUTHPRO_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, AUTHPRO_ENC_ACTION_NAME) == 0) { filename = (encrypted == TRUE) ? "authpro_encrypted.bin" : "authpro_plain.json"; - } else if (g_strcmp0 (action_name, TWOFAS_EXPORT_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, TWOFAS_EXPORT_PLAIN_ACTION_NAME) == 0) { + } else if (g_strcmp0 (action_name, TWOFAS_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, TWOFAS_ENC_ACTION_NAME) == 0) { filename = (encrypted == TRUE) ? "twofas_encrypted_v4.2fas" : "twofas_plain_v4.2fas"; } else { show_message_dialog (app_data->main_window, "Invalid export action.", GTK_MESSAGE_ERROR); @@ -85,15 +85,15 @@ export_data_cb (GSimpleAction *simple, } gchar *ret_msg = NULL; - if (g_strcmp0 (action_name, ANDOTP_EXPORT_ACTION_NAME) == 0 || g_strcmp0 (action_name, ANDOTP_EXPORT_PLAIN_ACTION_NAME) == 0) { + if (g_strcmp0 (action_name, ANDOTP_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, ANDOTP_ENC_ACTION_NAME) == 0) { ret_msg = export_andotp (export_file_abs_path, password, app_data->db_data->json_data); - } else if (g_strcmp0 (action_name, FREEOTPPLUS_EXPORT_ACTION_NAME) == 0) { + } else if (g_strcmp0 (action_name, FREEOTPPLUS_PLAIN_ACTION_NAME) == 0) { ret_msg = export_freeotpplus (export_file_abs_path, app_data->db_data->json_data); - } else if (g_strcmp0 (action_name, AEGIS_EXPORT_ACTION_NAME) == 0 || g_strcmp0 (action_name, AEGIS_EXPORT_PLAIN_ACTION_NAME) == 0) { + } else if (g_strcmp0 (action_name, AEGIS_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, AEGIS_ENC_ACTION_NAME) == 0) { ret_msg = export_aegis (export_file_abs_path, password, app_data->db_data->json_data); - } else if (g_strcmp0 (action_name, AUTHPRO_EXPORT_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, AUTHPRO_EXPORT_PLAIN_ACTION_NAME) == 0) { + } else if (g_strcmp0 (action_name, AUTHPRO_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, AUTHPRO_ENC_ACTION_NAME) == 0) { ret_msg = export_authpro (export_file_abs_path, password, app_data->db_data->json_data); - } else if (g_strcmp0 (action_name, TWOFAS_EXPORT_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, TWOFAS_EXPORT_PLAIN_ACTION_NAME) == 0) { + } else if (g_strcmp0 (action_name, TWOFAS_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, TWOFAS_ENC_ACTION_NAME) == 0) { ret_msg = export_twofas (export_file_abs_path, password, app_data->db_data->json_data); } else { show_message_dialog (app_data->main_window, "Invalid export action.", GTK_MESSAGE_ERROR); diff --git a/src/gui/gui-common.c b/src/gui/gui-misc.c similarity index 89% rename from src/gui/gui-common.c rename to src/gui/gui-misc.c index e480eda3..e7ee84aa 100644 --- a/src/gui/gui-common.c +++ b/src/gui/gui-misc.c @@ -3,7 +3,7 @@ #include #include "message-dialogs.h" #include "add-common.h" -#include "gui-common.h" +#include "gui-misc.h" #include "../common/common.h" #include "google-migration.pb-c.h" @@ -31,36 +31,6 @@ get_row_number_from_iter (GtkListStore *list_store, } -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; -} - - void send_ok_cb (GtkWidget *entry, gpointer user_data __attribute__((unused))) diff --git a/src/gui/gui-common.h b/src/gui/gui-misc.h similarity index 71% rename from src/gui/gui-common.h rename to src/gui/gui-misc.h index b7414869..f309d8a4 100644 --- a/src/gui/gui-common.h +++ b/src/gui/gui-misc.h @@ -14,15 +14,6 @@ void icon_press_cb (GtkEntry *entry, guint get_row_number_from_iter (GtkListStore *list_store, GtkTreeIter iter); -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); - void send_ok_cb (GtkWidget *entry, gpointer user_data); @@ -42,4 +33,15 @@ guchar *g_base64_decode_secure (const gchar *text, GKeyFile *get_kf_ptr (void); +void import_data_cb (GSimpleAction *simple, + GVariant *parameter, + gpointer user_data); + +void export_data_cb (GSimpleAction *simple, + GVariant *parameter, + gpointer user_data); + +gchar *update_db_from_otps (GSList *otps, + AppData *app_data); + G_END_DECLS diff --git a/src/gui/imports.c b/src/gui/import-from-gui.c similarity index 54% rename from src/gui/imports.c rename to src/gui/import-from-gui.c index d8c9dc00..993a1df6 100644 --- a/src/gui/imports.c +++ b/src/gui/import-from-gui.c @@ -1,13 +1,12 @@ #include #include #include -#include "imports.h" #include "password-cb.h" #include "message-dialogs.h" +#include "../common/import-export.h" #include "../common/gquarks.h" -#include "gui-common.h" +#include "gui-misc.h" #include "db-misc.h" -#include "../common/get-providers-data.h" static gboolean parse_data_and_update_db (AppData *app_data, @@ -16,7 +15,7 @@ static gboolean parse_data_and_update_db (AppData *app_data, void -select_file_cb (GSimpleAction *simple, +import_data_cb (GSimpleAction *simple, GVariant *parameter __attribute__((unused)), gpointer user_data) { @@ -45,19 +44,7 @@ select_file_cb (GSimpleAction *simple, gchar * update_db_from_otps (GSList *otps, AppData *app_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 (app_data->db_data->objects_hash, GUINT_TO_POINTER(hash), check_duplicate) == NULL) { - app_data->db_data->objects_hash = g_slist_append (app_data->db_data->objects_hash, g_memdup2 (&hash, sizeof (guint))); - app_data->db_data->data_to_add = g_slist_append (app_data->db_data->data_to_add, obj); - } else { - g_print ("[INFO] Duplicate element not added\n"); - } - } + add_otps_to_db (otps, app_data->db_data); GError *err = NULL; update_db (app_data->db_data, &err); @@ -74,52 +61,23 @@ update_db_from_otps (GSList *otps, AppData *app_data) } -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); -} - - static gboolean parse_data_and_update_db (AppData *app_data, const gchar *filename, const gchar *action_name) { GError *err = NULL; - GSList *content = NULL; - gchar *pwd = NULL; - if (g_strcmp0 (action_name, ANDOTP_IMPORT_ACTION_NAME) == 0 || g_strcmp0 (action_name, AEGIS_IMPORT_ENC_ACTION_NAME) == 0 || - g_strcmp0 (action_name, AUTHPRO_IMPORT_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, TWOFAS_IMPORT_ENC_ACTION_NAME) == 0) { + + if (g_strcmp0 (action_name, ANDOTP_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, AEGIS_ENC_ACTION_NAME) == 0 || + g_strcmp0 (action_name, AUTHPRO_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, TWOFAS_ENC_ACTION_NAME) == 0) { pwd = prompt_for_password (app_data, NULL, action_name, FALSE); if (pwd == NULL) { return FALSE; } } - if (g_strcmp0 (action_name, ANDOTP_IMPORT_ACTION_NAME) == 0 || g_strcmp0 (action_name, ANDOTP_IMPORT_PLAIN_ACTION_NAME) == 0) { - content = get_andotp_data (filename, pwd, app_data->db_data->max_file_size_from_memlock, &err); - } else if (g_strcmp0 (action_name, FREEOTPPLUS_IMPORT_ACTION_NAME) == 0) { - content = get_freeotpplus_data (filename, app_data->db_data->max_file_size_from_memlock, &err); - } else if (g_strcmp0 (action_name, AEGIS_IMPORT_ACTION_NAME) == 0 || g_strcmp0 (action_name, AEGIS_IMPORT_ENC_ACTION_NAME) == 0) { - content = get_aegis_data (filename, pwd, app_data->db_data->max_file_size_from_memlock, &err); - } else if (g_strcmp0 (action_name, AUTHPRO_IMPORT_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, AUTHPRO_IMPORT_PLAIN_ACTION_NAME) == 0) { - content = get_authpro_data (filename, pwd, app_data->db_data->max_file_size_from_memlock, &err); - } else if (g_strcmp0 (action_name, TWOFAS_IMPORT_ENC_ACTION_NAME) == 0 || g_strcmp0 (action_name, TWOFAS_IMPORT_PLAIN_ACTION_NAME) == 0) { - content = get_twofas_data (filename, pwd, app_data->db_data->max_file_size_from_memlock, &err); - } - + GSList *content = get_data_from_provider (action_name, filename, pwd, app_data->db_data->max_file_size_from_memlock, &err); if (content == NULL) { const gchar *msg = "An error occurred while importing, so nothing has been added to the database."; gchar *msg_with_err = NULL; diff --git a/src/gui/imports.h b/src/gui/imports.h deleted file mode 100644 index 40b02def..00000000 --- a/src/gui/imports.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include "data.h" - -G_BEGIN_DECLS - -#define ANDOTP_IMPORT_ACTION_NAME "import_andotp" -#define ANDOTP_IMPORT_PLAIN_ACTION_NAME "import_andotp_plain" -#define FREEOTPPLUS_IMPORT_ACTION_NAME "import_freeotpplus" -#define AEGIS_IMPORT_ACTION_NAME "import_aegis" -#define AEGIS_IMPORT_ENC_ACTION_NAME "import_aegis_enc" -#define AUTHPRO_IMPORT_ENC_ACTION_NAME "import_authpro_enc" -#define AUTHPRO_IMPORT_PLAIN_ACTION_NAME "import_authpro_plain" -#define TWOFAS_IMPORT_ENC_ACTION_NAME "import_twofas_enc" -#define TWOFAS_IMPORT_PLAIN_ACTION_NAME "import_twofas_plain" -#define GOOGLE_MIGRATION_FILE_ACTION_NAME "import_google_qr_file" -#define GOOGLE_MIGRATION_WEBCAM_ACTION_NAME "import_google_qr_webcam" - -void select_file_cb (GSimpleAction *simple, - GVariant *parameter, - gpointer user_data); - -gchar *update_db_from_otps (GSList *otps, - AppData *app_data); - -void free_otps_gslist (GSList *otps, - guint list_len); - -G_END_DECLS diff --git a/src/gui/lock-app.c b/src/gui/lock-app.c index 547974a2..e5b358cf 100644 --- a/src/gui/lock-app.c +++ b/src/gui/lock-app.c @@ -3,7 +3,7 @@ #include #include "data.h" #include "get-builder.h" -#include "gui-common.h" +#include "gui-misc.h" #include "message-dialogs.h" #include "otpclient.h" #include "lock-app.h" diff --git a/src/gui/manual-add-cb.c b/src/gui/manual-add-cb.c index a363f20b..b7041d11 100644 --- a/src/gui/manual-add-cb.c +++ b/src/gui/manual-add-cb.c @@ -1,6 +1,6 @@ #include #include "db-misc.h" -#include "gui-common.h" +#include "gui-misc.h" #include "manual-add-cb.h" #include "../common/gquarks.h" #include "message-dialogs.h" diff --git a/src/gui/parse-data.c b/src/gui/parse-data.c index b0c23180..d5f9052b 100644 --- a/src/gui/parse-data.c +++ b/src/gui/parse-data.c @@ -7,7 +7,7 @@ #include "manual-add-cb.h" #include "../common/gquarks.h" #include "message-dialogs.h" -#include "gui-common.h" +#include "gui-misc.h" static gboolean is_input_valid (GtkWidget *dialog, diff --git a/src/gui/password-cb.c b/src/gui/password-cb.c index 66ddc388..8ba5f89a 100644 --- a/src/gui/password-cb.c +++ b/src/gui/password-cb.c @@ -1,6 +1,6 @@ #include #include -#include "gui-common.h" +#include "gui-misc.h" #include "message-dialogs.h" #include "get-builder.h" diff --git a/src/gui/qrcode-parser.c b/src/gui/qrcode-parser.c index f2dcc826..0b149f71 100644 --- a/src/gui/qrcode-parser.c +++ b/src/gui/qrcode-parser.c @@ -4,7 +4,7 @@ #include #include #include "../common/common.h" -#include "gui-common.h" +#include "gui-misc.h" typedef struct image_data_t { gulong width; diff --git a/src/gui/settings-cb.c b/src/gui/settings-cb.c index 9729baf7..8d4f8415 100644 --- a/src/gui/settings-cb.c +++ b/src/gui/settings-cb.c @@ -4,7 +4,7 @@ #include "message-dialogs.h" #include "get-builder.h" #include "../common/secret-schema.h" -#include "gui-common.h" +#include "gui-misc.h" typedef struct settings_data_t { GtkWidget *dss_switch; diff --git a/src/gui/show-qr-cb.c b/src/gui/show-qr-cb.c index 3ff7c665..357e536b 100644 --- a/src/gui/show-qr-cb.c +++ b/src/gui/show-qr-cb.c @@ -7,7 +7,7 @@ #include "../common/parse-uri.h" #include "get-builder.h" #include "message-dialogs.h" -#include "gui-common.h" +#include "gui-misc.h" #define INCHES_PER_METER (100.0/2.54) #define SIZE 3 diff --git a/src/gui/ui/settings_popover.ui b/src/gui/ui/settings_popover.ui index df98adc0..e573a0c4 100644 --- a/src/gui/ui/settings_popover.ui +++ b/src/gui/ui/settings_popover.ui @@ -30,7 +30,7 @@ True True True - settings_menu.import_andotp + import_menu.andotp_encrypted andOTP (encrypted) @@ -44,7 +44,7 @@ True True True - settings_menu.import_andotp_plain + import_menu.andotp_plain andOTP (plain) @@ -58,7 +58,7 @@ True True True - settings_menu.import_freeotpplus + import_menu.freeotpplus_plain FreeOTP+ (key URI) @@ -72,7 +72,7 @@ True True True - settings_menu.import_aegis_enc + import_menu.aegis_encrypted Aegis (encrypted json) @@ -86,7 +86,7 @@ True True True - settings_menu.import_aegis + import_menu.aegis_plain Aegis (plain json) @@ -100,7 +100,7 @@ True True True - settings_menu.import_authpro_enc + import_menu.authpro_encrypted Authenticator Pro (encrypted) @@ -114,7 +114,7 @@ True True True - settings_menu.import_authpro_plain + import_menu.authpro_plain Authenticator Pro (plain json) @@ -128,7 +128,7 @@ True True True - settings_menu.import_twofas_enc + import_menu.twofas_encrypted 2FAS (encrypted json) @@ -142,7 +142,7 @@ True True True - settings_menu.import_twofas_plain + import_menu.twofas_plain 2FAS (plain json) @@ -197,7 +197,7 @@ True True True - settings_menu.export_andotp + export_menu.andotp_encrypted andOTP (encrypted) @@ -211,7 +211,7 @@ True True True - settings_menu.export_andotp_plain + export_menu.andotp_plain andOTP (plain) @@ -225,7 +225,7 @@ True True True - settings_menu.export_freeotpplus + export_menu.freeotpplus_plain FreeOTP+ (key URI) @@ -239,7 +239,7 @@ True True True - settings_menu.export_aegis + export_menu.aegis_encrypted Aegis (encrypted json) @@ -253,7 +253,7 @@ True True True - settings_menu.export_aegis_plain + export_menu.aegis_plain Aegis (plain json) @@ -267,7 +267,7 @@ True True True - settings_menu.export_authpro_enc + export_menu.authpro_encrypted Authenticator Pro (encrypted) @@ -281,7 +281,7 @@ True True True - settings_menu.export_authpro_plain + export_menu.authpro_plain Authenticator Pro (plain json) @@ -295,7 +295,7 @@ True True True - settings_menu.export_twofas_enc + export_menu.twofas_encrypted 2FAS (encrypted json) @@ -309,7 +309,7 @@ True True True - settings_menu.export_twofas_plain + export_menu.twofas_plain 2FAS (plain json) @@ -597,7 +597,7 @@ True True True - settings_menu.import_google_qr_file + import_menu.import_google_qr_file From file @@ -611,7 +611,7 @@ True True True - settings_menu.import_google_qr_webcam + import_menu.import_google_qr_webcam From webcam diff --git a/src/gui/webcam-add-cb.c b/src/gui/webcam-add-cb.c index b4ea0d20..a7d8cec8 100644 --- a/src/gui/webcam-add-cb.c +++ b/src/gui/webcam-add-cb.c @@ -1,10 +1,10 @@ #include #include #include -#include "imports.h" #include "message-dialogs.h" #include "get-builder.h" -#include "gui-common.h" +#include "gui-misc.h" +#include "../common/import-export.h" typedef struct config_data_t { @@ -27,7 +27,7 @@ webcam_add_cb (GSimpleAction *simple, gpointer user_data) { const gchar *action_name = g_action_get_name (G_ACTION(simple)); - gboolean google_migration = (g_strcmp0 (action_name, GOOGLE_MIGRATION_WEBCAM_ACTION_NAME) == 0) ? TRUE : FALSE; + gboolean google_migration = (g_strcmp0 (action_name, GOOGLE_WEBCAM_ACTION_NAME) == 0) ? TRUE : FALSE; AppData *app_data = (AppData *)user_data; From 9d94a7c7b390d5924153308281676729ce482817 Mon Sep 17 00:00:00 2001 From: Paolo Stivanin Date: Mon, 11 Mar 2024 11:28:02 +0100 Subject: [PATCH 5/6] Update appdata and security.md --- SECURITY.md | 3 ++- data/com.github.paolostivanin.OTPClient.appdata.xml | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index 15164b57..0eafdf8f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,7 +6,8 @@ The following list describes whether a version is eligible or not for security u | Version | Supported | EOL | |---------|--------------------|-------------| -| 3.5.x | :white_check_mark: | - | +| 3.6.x | :white_check_mark: | - | +| 3.5.x | :white_check_mark: | 31-Mar-2024 | | 3.4.1 | :white_check_mark: | 31-May-2024 | | 3.4.0 | :x: | 29-Feb-2024 | | 3.3.x | :x: | 29-Feb-2024 | diff --git a/data/com.github.paolostivanin.OTPClient.appdata.xml b/data/com.github.paolostivanin.OTPClient.appdata.xml index 6b0f566e..daa3fe05 100644 --- a/data/com.github.paolostivanin.OTPClient.appdata.xml +++ b/data/com.github.paolostivanin.OTPClient.appdata.xml @@ -89,6 +89,17 @@ + + +

OTPClient 3.6.0 brings a new feature and internal improvements:

+
    +
  • NEW: add possibility to import plain/encrypted backups using the CLI
  • +
  • FIX: make GUI and CLI independent, so that CLI only can be built and installed without GTK.
  • +
  • FIX: check file size against memlock before importing a backup
  • +
  • FIX: code cleanup and internal refactoring
  • +
+
+

OTPClient 3.5.2 brings some small improvements:

From 91b8f339c50e00ec6f4984828e8c5164540ae98d Mon Sep 17 00:00:00 2001 From: Paolo Stivanin Date: Mon, 11 Mar 2024 15:21:32 +0100 Subject: [PATCH 6/6] clean up code --- src/common/common.c | 7 ++++++ src/common/common.h | 2 ++ src/gui/change-db-cb.c | 2 +- src/gui/change-pwd-cb.c | 1 - src/gui/db-misc.c | 46 --------------------------------------- src/gui/db-misc.h | 17 --------------- src/gui/edit-row-cb.c | 3 +-- src/gui/export-from-gui.c | 38 +++++++------------------------- src/gui/gui-misc.c | 35 +++++++++++++++++++++++++++++ src/gui/gui-misc.h | 3 +++ src/gui/import-from-gui.c | 21 ------------------ src/gui/liststore-misc.c | 1 - src/gui/liststore-misc.h | 2 -- src/gui/manual-add-cb.c | 2 +- src/gui/manual-add-cb.h | 2 ++ src/gui/new-db-cb.c | 4 ++-- src/gui/parse-data.c | 1 - src/gui/treeview.c | 9 ++++++++ src/gui/treeview.h | 2 ++ 19 files changed, 73 insertions(+), 125 deletions(-) delete mode 100644 src/gui/db-misc.c delete mode 100644 src/gui/db-misc.h diff --git a/src/common/common.c b/src/common/common.c index bc4ca4a7..d6ab3e4f 100644 --- a/src/common/common.c +++ b/src/common/common.c @@ -422,4 +422,11 @@ build_json_obj (const gchar *type, } return obj; +} + + +void +json_free (gpointer data) +{ + json_decref (data); } \ No newline at end of file diff --git a/src/common/common.h b/src/common/common.h index d6a7ab24..4eb4ec6e 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -86,4 +86,6 @@ json_t *build_json_obj (const gchar *type, guint period, guint64 ctr); +void json_free (gpointer data); + G_END_DECLS diff --git a/src/gui/change-db-cb.c b/src/gui/change-db-cb.c index 4cdfb0d5..256b6849 100644 --- a/src/gui/change-db-cb.c +++ b/src/gui/change-db-cb.c @@ -3,7 +3,7 @@ #include #include "data.h" #include "message-dialogs.h" -#include "db-misc.h" +#include "gui-misc.h" #include "password-cb.h" #include "db-actions.h" #include "../common/secret-schema.h" diff --git a/src/gui/change-pwd-cb.c b/src/gui/change-pwd-cb.c index 6039960b..5daebda1 100644 --- a/src/gui/change-pwd-cb.c +++ b/src/gui/change-pwd-cb.c @@ -3,7 +3,6 @@ #include #include "data.h" #include "message-dialogs.h" -#include "db-misc.h" #include "password-cb.h" #include "../common/secret-schema.h" #include "otpclient.h" diff --git a/src/gui/db-misc.c b/src/gui/db-misc.c deleted file mode 100644 index 076aba95..00000000 --- a/src/gui/db-misc.c +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include "db-misc.h" -#include "otpclient.h" -#include "../common/gquarks.h" -#include "gui-misc.h" - -static void json_free (gpointer data); - - -void -regenerate_model (AppData *app_data) -{ - update_model (app_data); - g_slist_free_full (app_data->db_data->data_to_add, json_free); - app_data->db_data->data_to_add = NULL; -} - - -void -load_new_db (AppData *app_data, - GError **err) -{ - reload_db (app_data->db_data, err); - if (*err != NULL) { - return; - } - - update_model (app_data); - g_slist_free_full (app_data->db_data->data_to_add, json_free); - app_data->db_data->data_to_add = NULL; -} - - -void -write_db_to_disk (DatabaseData *db_data, - GError **err) -{ - update_db (db_data, err); -} - - -static void -json_free (gpointer data) -{ - json_decref (data); -} \ No newline at end of file diff --git a/src/gui/db-misc.h b/src/gui/db-misc.h deleted file mode 100644 index 8b419d09..00000000 --- a/src/gui/db-misc.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include "../common/db-common.h" -#include "data.h" - -G_BEGIN_DECLS - -void load_new_db (AppData *app_data, - GError **err); - -void regenerate_model (AppData *app_data); - -void write_db_to_disk (DatabaseData *db_data, - GError **err); - -G_END_DECLS diff --git a/src/gui/edit-row-cb.c b/src/gui/edit-row-cb.c index 609d2d32..a8ba1332 100644 --- a/src/gui/edit-row-cb.c +++ b/src/gui/edit-row-cb.c @@ -1,10 +1,9 @@ #include #include #include "treeview.h" -#include "db-misc.h" +#include "gui-misc.h" #include "get-builder.h" #include "message-dialogs.h" -#include "gui-misc.h" #include "../common/gquarks.h" typedef struct edit_data_t { diff --git a/src/gui/export-from-gui.c b/src/gui/export-from-gui.c index c5bb38ff..f3c8fda5 100644 --- a/src/gui/export-from-gui.c +++ b/src/gui/export-from-gui.c @@ -5,10 +5,6 @@ #include "message-dialogs.h" #include "../common/import-export.h" -static void show_ret_msg_dialog (GtkWidget *mainwin, - const gchar *fpath, - const gchar *ret_msg); - void export_data_cb (GSimpleAction *simple, @@ -69,7 +65,7 @@ export_data_cb (GSimpleAction *simple, gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(fl_diag), filename); - gchar *export_file_abs_path = NULL; + g_autofree gchar *export_file_abs_path = NULL; gint native_diag_res = gtk_native_dialog_run (GTK_NATIVE_DIALOG(fl_diag)); if (native_diag_res == GTK_RESPONSE_ACCEPT) { export_file_abs_path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(fl_diag)); @@ -84,7 +80,7 @@ export_data_cb (GSimpleAction *simple, return; } - gchar *ret_msg = NULL; + g_autofree gchar *ret_msg = NULL; if (g_strcmp0 (action_name, ANDOTP_PLAIN_ACTION_NAME) == 0 || g_strcmp0 (action_name, ANDOTP_ENC_ACTION_NAME) == 0) { ret_msg = export_andotp (export_file_abs_path, password, app_data->db_data->json_data); } else if (g_strcmp0 (action_name, FREEOTPPLUS_PLAIN_ACTION_NAME) == 0) { @@ -99,30 +95,12 @@ export_data_cb (GSimpleAction *simple, show_message_dialog (app_data->main_window, "Invalid export action.", GTK_MESSAGE_ERROR); return; } - show_ret_msg_dialog (app_data->main_window, export_file_abs_path, ret_msg); - g_free (ret_msg); - g_free (export_file_abs_path); + + g_autofree gchar *message = (ret_msg != NULL) ? g_strconcat ("Error while exporting data: ", ret_msg, NULL) + : g_strconcat ("Data successfully exported to ", export_file_abs_path, NULL); + GtkMessageType msg_type = (ret_msg != NULL) ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO; + show_message_dialog (app_data->main_window, message, msg_type); if (encrypted == TRUE) { gcry_free (password); } -} - - -static void -show_ret_msg_dialog (GtkWidget *mainwin, - const gchar *fpath, - const gchar *ret_msg) -{ - GtkMessageType msg_type; - gchar *message = NULL; - - if (ret_msg != NULL) { - message = g_strconcat ("Error while exporting data: ", ret_msg, NULL); - msg_type = GTK_MESSAGE_ERROR; - } else { - message = g_strconcat ("Data successfully exported to ", fpath, NULL); - msg_type = GTK_MESSAGE_INFO; - } - show_message_dialog (mainwin, message, msg_type); - g_free (message); -} +} \ No newline at end of file diff --git a/src/gui/gui-misc.c b/src/gui/gui-misc.c index e7ee84aa..0bacbbce 100644 --- a/src/gui/gui-misc.c +++ b/src/gui/gui-misc.c @@ -6,6 +6,8 @@ #include "gui-misc.h" #include "../common/common.h" #include "google-migration.pb-c.h" +#include "../common/gquarks.h" +#include "treeview.h" void @@ -268,6 +270,39 @@ decode_migration_data (const gchar *encoded_uri) } +gchar * +update_db_from_otps (GSList *otps, AppData *app_data) +{ + add_otps_to_db (otps, app_data->db_data); + + GError *err = NULL; + update_db (app_data->db_data, &err); + if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) { + return g_strdup (err->message); + } + reload_db (app_data->db_data, &err); + if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) { + return g_strdup (err->message); + } + regenerate_model (app_data); + + return NULL; +} + + +void +load_new_db (AppData *app_data, + GError **err) +{ + reload_db (app_data->db_data, err); + if (*err != NULL) { + return; + } + + update_model (app_data); + g_slist_free_full (app_data->db_data->data_to_add, json_free); + app_data->db_data->data_to_add = NULL; +} GKeyFile * diff --git a/src/gui/gui-misc.h b/src/gui/gui-misc.h index f309d8a4..a01ccb91 100644 --- a/src/gui/gui-misc.h +++ b/src/gui/gui-misc.h @@ -44,4 +44,7 @@ void export_data_cb (GSimpleAction *simple, gchar *update_db_from_otps (GSList *otps, AppData *app_data); +void load_new_db (AppData *app_data, + GError **err); + G_END_DECLS diff --git a/src/gui/import-from-gui.c b/src/gui/import-from-gui.c index 993a1df6..e6ffd3ff 100644 --- a/src/gui/import-from-gui.c +++ b/src/gui/import-from-gui.c @@ -6,7 +6,6 @@ #include "../common/import-export.h" #include "../common/gquarks.h" #include "gui-misc.h" -#include "db-misc.h" static gboolean parse_data_and_update_db (AppData *app_data, @@ -41,26 +40,6 @@ import_data_cb (GSimpleAction *simple, } -gchar * -update_db_from_otps (GSList *otps, AppData *app_data) -{ - add_otps_to_db (otps, app_data->db_data); - - GError *err = NULL; - update_db (app_data->db_data, &err); - if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) { - return g_strdup (err->message); - } - reload_db (app_data->db_data, &err); - if (err != NULL && !g_error_matches (err, missing_file_gquark (), MISSING_FILE_CODE)) { - return g_strdup (err->message); - } - regenerate_model (app_data); - - return NULL; -} - - static gboolean parse_data_and_update_db (AppData *app_data, const gchar *filename, diff --git a/src/gui/liststore-misc.c b/src/gui/liststore-misc.c index d073091e..39502848 100644 --- a/src/gui/liststore-misc.c +++ b/src/gui/liststore-misc.c @@ -4,7 +4,6 @@ #include "treeview.h" #include "liststore-misc.h" #include "../common/gquarks.h" -#include "../common/common.h" typedef struct otp_data_t { diff --git a/src/gui/liststore-misc.h b/src/gui/liststore-misc.h index db4f77b8..52b07f8b 100644 --- a/src/gui/liststore-misc.h +++ b/src/gui/liststore-misc.h @@ -2,8 +2,6 @@ G_BEGIN_DECLS -#include "db-misc.h" - gboolean traverse_liststore (gpointer user_data); void set_otp (GtkListStore *list_store, diff --git a/src/gui/manual-add-cb.c b/src/gui/manual-add-cb.c index b7041d11..ea69e869 100644 --- a/src/gui/manual-add-cb.c +++ b/src/gui/manual-add-cb.c @@ -1,10 +1,10 @@ #include -#include "db-misc.h" #include "gui-misc.h" #include "manual-add-cb.h" #include "../common/gquarks.h" #include "message-dialogs.h" #include "get-builder.h" +#include "treeview.h" static void changed_otp_cb (GtkWidget *cb, gpointer user_data); diff --git a/src/gui/manual-add-cb.h b/src/gui/manual-add-cb.h index eef992d5..03e2c7f7 100644 --- a/src/gui/manual-add-cb.h +++ b/src/gui/manual-add-cb.h @@ -1,5 +1,7 @@ #pragma once +#include "../common/db-common.h" + G_BEGIN_DECLS typedef struct widgets_t { diff --git a/src/gui/new-db-cb.c b/src/gui/new-db-cb.c index 28a181bd..eca8c589 100644 --- a/src/gui/new-db-cb.c +++ b/src/gui/new-db-cb.c @@ -2,7 +2,7 @@ #include #include #include "data.h" -#include "db-misc.h" +#include "gui-misc.h" #include "message-dialogs.h" #include "password-cb.h" #include "db-actions.h" @@ -48,7 +48,7 @@ new_db (AppData *app_data) } secret_password_store (OTPCLIENT_SCHEMA, SECRET_COLLECTION_DEFAULT, "main_pwd", app_data->db_data->key, NULL, on_password_stored, NULL, "string", "main_pwd", NULL); GError *err = NULL; - write_db_to_disk (app_data->db_data, &err); + update_db (app_data->db_data, &err); if (err != NULL) { show_message_dialog (app_data->main_window, err->message, GTK_MESSAGE_ERROR); g_clear_error (&err); diff --git a/src/gui/parse-data.c b/src/gui/parse-data.c index d5f9052b..4f579949 100644 --- a/src/gui/parse-data.c +++ b/src/gui/parse-data.c @@ -3,7 +3,6 @@ #include #include #include -#include "db-misc.h" #include "manual-add-cb.h" #include "../common/gquarks.h" #include "message-dialogs.h" diff --git a/src/gui/treeview.c b/src/gui/treeview.c index a1539de5..20165473 100644 --- a/src/gui/treeview.c +++ b/src/gui/treeview.c @@ -261,6 +261,15 @@ reorder_db (AppData *app_data) } +void +regenerate_model (AppData *app_data) +{ + update_model (app_data); + g_slist_free_full (app_data->db_data->data_to_add, json_free); + app_data->db_data->data_to_add = NULL; +} + + static void hide_all_otps_cb (GtkTreeView *tree_view, gpointer user_data) diff --git a/src/gui/treeview.h b/src/gui/treeview.h index 2e88456d..61957563 100644 --- a/src/gui/treeview.h +++ b/src/gui/treeview.h @@ -33,4 +33,6 @@ void row_selected_cb (GtkTreeView *tree_view, void reorder_db (AppData *app_data); +void regenerate_model (AppData *app_data); + G_END_DECLS