Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package otpclient for openSUSE:Factory 
checked in at 2024-02-12 18:52:55
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/otpclient (Old)
 and      /work/SRC/openSUSE:Factory/.otpclient.new.1815 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "otpclient"

Mon Feb 12 18:52:55 2024 rev:32 rq:1146089 version:3.4.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/otpclient/otpclient.changes      2024-02-07 
18:52:57.964579634 +0100
+++ /work/SRC/openSUSE:Factory/.otpclient.new.1815/otpclient.changes    
2024-02-12 18:55:23.365387860 +0100
@@ -1,0 +2,18 @@
+Mon Feb 12 07:53:22 UTC 2024 - Paolo Stivanin <i...@paolostivanin.com>
+
+- Update to 3.4.1:
+  * FIX: FreeOTP+ export (thanks @hubnut)
+
+-------------------------------------------------------------------
+Sat Feb 10 15:07:04 UTC 2024 - Paolo Stivanin <i...@paolostivanin.com>
+
+- Update to 3.4.0:
+  * NEW: CLI was completely refactored, and the options have changed.
+         Be sure to check the new options using the help command (-h)
+  * NEW: you can now specify a database when calling the CLI (#340)
+  * FIX: handling errors when path and/or password is incorrect (#336)
+  * FIX: prompt for file again, if needed (#335)
+  * FIX: prevent about dialog from hiding
+  * FIX: use system RNG as source of entropy
+
+-------------------------------------------------------------------

Old:
----
  v3.3.0.tar.gz
  v3.3.0.tar.gz.asc

New:
----
  v3.4.1.tar.gz
  v3.4.1.tar.gz.asc

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ otpclient.spec ++++++
--- /var/tmp/diff_new_pack.kkujo8/_old  2024-02-12 18:55:23.881406496 +0100
+++ /var/tmp/diff_new_pack.kkujo8/_new  2024-02-12 18:55:23.881406496 +0100
@@ -18,7 +18,7 @@
 
 %define uclname OTPClient
 Name:           otpclient
-Version:        3.3.0
+Version:        3.4.1
 Release:        0
 Summary:        Simple GTK+ client for managing TOTP and HOTP
 License:        GPL-3.0-or-later


++++++ v3.3.0.tar.gz -> v3.4.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/CMakeLists.txt 
new/OTPClient-3.4.1/CMakeLists.txt
--- old/OTPClient-3.3.0/CMakeLists.txt  2024-01-08 12:16:54.000000000 +0100
+++ new/OTPClient-3.4.1/CMakeLists.txt  2024-02-12 08:48:17.000000000 +0100
@@ -1,5 +1,5 @@
 cmake_minimum_required(VERSION 3.16)
-project(OTPClient VERSION "3.3.0" LANGUAGES "C")
+project(OTPClient VERSION "3.4.1" LANGUAGES "C")
 include(GNUInstallDirs)
 
 configure_file("src/common/version.h.in" "version.h")
@@ -88,7 +88,8 @@
         src/shortcuts-cb.h
         src/webcam-add-cb.h
         src/edit-row-cb.h
-        src/show-qr-cb.h src/dbinfo-cb.h)
+        src/show-qr-cb.h src/dbinfo-cb.h
+        src/change-file-cb.h)
 
 set(GUI_SOURCE_FILES
         src/common/common.c
@@ -128,10 +129,10 @@
         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/change-pwd-cb.c
+        src/dbinfo-cb.c)
 
 set(CLI_HEADER_FILES
-        src/cli/help.h
         src/cli/get-data.h
         src/common/common.h
         src/db-misc.h
@@ -141,15 +142,17 @@
         src/parse-uri.h
         src/common/get-providers-data.h
         src/google-migration.pb-c.h
-        src/secret-schema.h)
+        src/secret-schema.h
+        src/cli/main.h
+)
 
 set(CLI_SOURCE_FILES
         src/cli/main.c
-        src/cli/help.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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/SECURITY.md 
new/OTPClient-3.4.1/SECURITY.md
--- old/OTPClient-3.3.0/SECURITY.md     2024-01-08 12:16:54.000000000 +0100
+++ new/OTPClient-3.4.1/SECURITY.md     2024-02-12 08:48:17.000000000 +0100
@@ -7,7 +7,7 @@
 | Version | Supported          | EOL         |
 |---------|--------------------|-------------|
 | 3.3.x   | :white_check_mark: | -           |
-| 3.2.x   | :white_check_mark: | 31-Jan-2024 |
+| 3.2.x   | :x:                | 31-Jan-2024 |
 | 3.1.x   | :x:                | 30-Nov-2023 |
 | 3.0.x   | :x:                | 31-Dec-2022 |
 | 2.6.x   | :x:                | 15-Jan-2023 |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/OTPClient-3.3.0/data/com.github.paolostivanin.OTPClient.appdata.xml 
new/OTPClient-3.4.1/data/com.github.paolostivanin.OTPClient.appdata.xml
--- old/OTPClient-3.3.0/data/com.github.paolostivanin.OTPClient.appdata.xml     
2024-01-08 12:16:54.000000000 +0100
+++ new/OTPClient-3.4.1/data/com.github.paolostivanin.OTPClient.appdata.xml     
2024-02-12 08:48:17.000000000 +0100
@@ -14,6 +14,8 @@
     <keyword>2fa</keyword>
     <keyword>2factor</keyword>
     <keyword>2fa-client</keyword>
+    <keyword>2step</keyword>
+    <keyword>twostep</keyword>
   </keywords>
 
   <description>
@@ -87,6 +89,26 @@
   </content_rating>
 
   <releases>
+    <release version="3.4.1" date="2024-02-12">
+      <description>
+        <p>OTPClient 3.4.1 brings a single fix::</p>
+        <ul>
+          <li>FIX: fix FreeOTP+ export (thanks @hubnut)</li>
+        </ul>
+      </description>
+    </release>
+    <release version="3.4.0" date="2024-02-09">
+      <description>
+        <p>OTPClient 3.4.0 brings the following changes:</p>
+        <ul>
+          <li>NEW: you can now specify a database when calling the CLI 
(#340)</li>
+          <li>FIX: handling errors when path and/or password is incorrect 
(#336)</li>
+          <li>FIX: prompt for file again, if needed (#335)</li>
+          <li>FIX: prevent about dialog from hiding</li>
+          <li>FIX: use system RNG as source of entropy</li>
+        </ul>
+      </description>
+    </release>
     <release version="3.3.0" date="2024-01-08">
       <description>
         <p>OTPClient 3.3.0 brings the following changes:</p>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/about_diag_cb.c 
new/OTPClient-3.4.1/src/about_diag_cb.c
--- old/OTPClient-3.3.0/src/about_diag_cb.c     2024-01-08 12:16:54.000000000 
+0100
+++ new/OTPClient-3.4.1/src/about_diag_cb.c     2024-02-12 08:48:17.000000000 
+0100
@@ -16,11 +16,11 @@
     gchar *icon_abs_path = g_strconcat (INSTALL_PREFIX, "/", partial_path, 
NULL);
 
     GtkWidget *ab_diag = gtk_about_dialog_new ();
-    gtk_window_set_transient_for (GTK_WINDOW(app_data->main_window), 
GTK_WINDOW(ab_diag));
+    gtk_window_set_transient_for (GTK_WINDOW(ab_diag), 
GTK_WINDOW(app_data->main_window));
 
     gtk_about_dialog_set_program_name (GTK_ABOUT_DIALOG(ab_diag), 
PROJECT_NAME);
     gtk_about_dialog_set_version (GTK_ABOUT_DIALOG(ab_diag), PROJECT_VER);
-    gtk_about_dialog_set_copyright (GTK_ABOUT_DIALOG(ab_diag), "2017-2022");
+    gtk_about_dialog_set_copyright (GTK_ABOUT_DIALOG(ab_diag), "2017-2024");
     gtk_about_dialog_set_comments (GTK_ABOUT_DIALOG(ab_diag), _("Highly secure 
and easy to use GTK+ software for two-factor authentication that supports both 
Time-based One-time Passwords (TOTP) and HMAC-Based One-Time Passwords 
(HOTP)."));
     gtk_about_dialog_set_license_type (GTK_ABOUT_DIALOG(ab_diag), 
GTK_LICENSE_GPL_3_0);
     gtk_about_dialog_set_website (GTK_ABOUT_DIALOG(ab_diag), 
"https://github.com/paolostivanin/OTPClient";);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/app.c 
new/OTPClient-3.4.1/src/app.c
--- old/OTPClient-3.3.0/src/app.c       2024-01-08 12:16:54.000000000 +0100
+++ new/OTPClient-3.4.1/src/app.c       2024-02-12 08:48:17.000000000 +0100
@@ -24,6 +24,7 @@
 #include "edit-row-cb.h"
 #include "show-qr-cb.h"
 #include "dbinfo-cb.h"
+#include "change-file-cb.h"
 
 #ifndef USE_FLATPAK_APP_FOLDER
 static gchar     *get_db_path               (AppData            *app_data);
@@ -208,11 +209,14 @@
         retry:
         app_data->db_data->key = prompt_for_password (app_data, NULL, NULL, 
FALSE);
         if (app_data->db_data->key == NULL) {
-            if (change_file (app_data) == FALSE) {
+            retry_change_file:
+            if (change_file (app_data) == QUIT_APP) {
                 g_free (app_data->db_data);
                 g_free (app_data);
                 g_application_quit (G_APPLICATION(app));
                 return;
+            } else {
+                goto retry_change_file;
             }
         }
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/change-db-cb.c 
new/OTPClient-3.4.1/src/change-db-cb.c
--- old/OTPClient-3.3.0/src/change-db-cb.c      2024-01-08 12:16:54.000000000 
+0100
+++ new/OTPClient-3.4.1/src/change-db-cb.c      2024-02-12 08:48:17.000000000 
+0100
@@ -7,15 +7,11 @@
 #include "password-cb.h"
 #include "db-actions.h"
 #include "secret-schema.h"
+#include "change-file-cb.h"
 
-
-void
-change_db_cb (GSimpleAction *simple    __attribute__((unused)),
-              GVariant      *parameter __attribute__((unused)),
-              gpointer       user_data)
+int
+change_db (AppData *app_data)
 {
-    AppData *app_data = (AppData *)user_data;
-
     GtkWidget *changedb_diag = GTK_WIDGET(gtk_builder_get_object 
(app_data->builder, "changedb_diag_id"));
     GtkWidget *old_changedb_entry = GTK_WIDGET(gtk_builder_get_object 
(app_data->builder, "changedb_olddb_entry_id"));
     GtkWidget *new_changedb_entry = GTK_WIDGET(gtk_builder_get_object 
(app_data->builder, "changedb_entry_id"));
@@ -29,29 +25,60 @@
     gint result = gtk_dialog_run (GTK_DIALOG (changedb_diag));
     switch (result) {
         case GTK_RESPONSE_OK:
+            if (gtk_entry_get_text_length (GTK_ENTRY(new_changedb_entry)) == 
0) {
+                show_message_dialog (app_data->main_window, "Input path cannot 
be empty.", GTK_MESSAGE_ERROR);
+                gtk_widget_hide (changedb_diag);
+                return RETRY_CHANGE;
+            }
             new_db_path = gtk_entry_get_text (GTK_ENTRY(new_changedb_entry));
             if (!g_file_test (new_db_path, G_FILE_TEST_IS_REGULAR) || 
g_file_test (new_db_path,G_FILE_TEST_IS_SYMLINK)){
                 show_message_dialog (app_data->main_window, "Selected file is 
either a symlink or a non regular file.\nPlease choose another file.", 
GTK_MESSAGE_ERROR);
-            } else {
-                g_free (app_data->db_data->db_path);
-                app_data->db_data->db_path = g_strdup (new_db_path);
-                update_cfg_file (app_data);
-                gcry_free (app_data->db_data->key);
-                app_data->db_data->key = prompt_for_password (app_data, NULL, 
NULL, FALSE);
-                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;
-                load_new_db (app_data, &err);
-                if (err != NULL) {
-                    show_message_dialog (app_data->main_window, err->message, 
GTK_MESSAGE_ERROR);
-                    g_clear_error (&err);
-                }
+                gtk_widget_hide (changedb_diag);
+                return RETRY_CHANGE;
+            }
+            gchar *old_db_path = g_strdup (app_data->db_data->db_path);
+            g_free (app_data->db_data->db_path);
+            app_data->db_data->db_path = g_strdup (new_db_path);
+            update_cfg_file (app_data);
+            gcry_free (app_data->db_data->key);
+            app_data->db_data->key = prompt_for_password (app_data, NULL, 
NULL, FALSE);
+            if (app_data->db_data->key == NULL) {
+                gtk_widget_hide (changedb_diag);
+                revert_db_path (app_data, old_db_path);
+                return RETRY_CHANGE;
             }
+            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;
+            load_new_db (app_data, &err);
+            if (err != NULL) {
+                show_message_dialog (app_data->main_window, err->message, 
GTK_MESSAGE_ERROR);
+                g_clear_error (&err);
+                gtk_widget_hide (changedb_diag);
+                revert_db_path (app_data, old_db_path);
+                return RETRY_CHANGE;
+            }
+            g_free (old_db_path);
             break;
         case GTK_RESPONSE_CANCEL:
+            gtk_widget_destroy (changedb_diag);
+            return QUIT_APP;
         default:
             break;
     }
     gtk_widget_destroy (changedb_diag);
+
+    return CHANGE_OK;
+}
+
+
+void
+change_db_cb (GSimpleAction *action_name __attribute__((unused)),
+              GVariant      *parameter __attribute__((unused)),
+              gpointer       user_data)
+{
+    AppData *app_data = (AppData *)user_data;
+
+    change_db (app_data);
 }
 
 
@@ -60,4 +87,4 @@
                        gpointer   user_data)
 {
     change_db_cb (NULL, NULL, user_data);
-}
+}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/change-db-cb.h 
new/OTPClient-3.4.1/src/change-db-cb.h
--- old/OTPClient-3.3.0/src/change-db-cb.h      2024-01-08 12:16:54.000000000 
+0100
+++ new/OTPClient-3.4.1/src/change-db-cb.h      2024-02-12 08:48:17.000000000 
+0100
@@ -4,6 +4,8 @@
 
 G_BEGIN_DECLS
 
+int  change_db             (AppData *app_data);
+
 void change_db_cb          (GSimpleAction *simple,
                             GVariant      *parameter,
                             gpointer       user_data);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/change-file-cb.c 
new/OTPClient-3.4.1/src/change-file-cb.c
--- old/OTPClient-3.3.0/src/change-file-cb.c    2024-01-08 12:16:54.000000000 
+0100
+++ new/OTPClient-3.4.1/src/change-file-cb.c    2024-02-12 08:48:17.000000000 
+0100
@@ -1,10 +1,10 @@
 #include <gtk/gtk.h>
 #include "new-db-cb.h"
 #include "change-db-cb.h"
-#include "db-misc.h"
+#include "change-file-cb.h"
 #include "message-dialogs.h"
 
-gboolean
+int
 change_file (AppData *app_data)
 {
     GtkWidget *label = GTK_WIDGET(gtk_builder_get_object (app_data->builder, 
"diag_changefile_label_id"));
@@ -21,18 +21,18 @@
     switch (result) {
         case GTK_RESPONSE_ACCEPT:
             // select an existing DB.
-            change_db_cb (NULL, NULL, app_data);
-            res = TRUE;
+            res = change_db (app_data);
             break;
         case GTK_RESPONSE_OK:
             // create a new db.
-            new_db_cb (NULL, NULL, app_data);
-            res = TRUE;
+            res =  new_db (app_data);
             break;
-        case GTK_RESPONSE_CANCEL:
+        case GTK_RESPONSE_CLOSE:
+            res = QUIT_APP;
         default:
             break;
     }
+
     gtk_widget_hide (diag_changefile);
 
     return res;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/change-file-cb.h 
new/OTPClient-3.4.1/src/change-file-cb.h
--- old/OTPClient-3.3.0/src/change-file-cb.h    1970-01-01 01:00:00.000000000 
+0100
+++ new/OTPClient-3.4.1/src/change-file-cb.h    2024-02-12 08:48:17.000000000 
+0100
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "data.h"
+
+G_BEGIN_DECLS
+
+#define QUIT_APP     50
+#define RETRY_CHANGE 51
+#define CHANGE_OK    52
+
+int  change_file (AppData *app_data);
+
+G_END_DECLS
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/cli/exec-action.c 
new/OTPClient-3.4.1/src/cli/exec-action.c
--- old/OTPClient-3.3.0/src/cli/exec-action.c   1970-01-01 01:00:00.000000000 
+0100
+++ new/OTPClient-3.4.1/src/cli/exec-action.c   2024-02-12 08:48:17.000000000 
+0100
@@ -0,0 +1,251 @@
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gcrypt.h>
+#include <termios.h>
+#include <libsecret/secret.h>
+#include "main.h"
+#include "../secret-schema.h"
+#include "../common/common.h"
+#include "../db-misc.h"
+#include "get-data.h"
+#include "../common/exports.h"
+
+#ifndef USE_FLATPAK_APP_FOLDER
+static gchar    *get_db_path           (void);
+#endif
+
+static gchar    *get_pwd               (const gchar *pwd_msg);
+
+static gboolean  get_use_secretservice (void);
+
+
+gboolean exec_action (CmdlineOpts  *cmdline_opts,
+                      DatabaseData *db_data)
+{
+#ifdef USE_FLATPAK_APP_FOLDER
+    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);
+    if (!g_file_test (cfg_file_path, G_FILE_TEST_EXISTS)) {
+        g_file_set_contents (cfg_file_path, "[config]", -1, NULL);
+    }
+    g_free (cfg_file_path);
+#else
+    db_data->db_path = (cmdline_opts->database != NULL) ? g_strdup 
(cmdline_opts->database) : get_db_path ();
+    if (db_data->db_path == NULL) {
+        g_free (db_data);
+        return FALSE;
+    }
+#endif
+
+    gboolean use_secret_service = get_use_secretservice ();
+    if (use_secret_service == TRUE) {
+        gchar *pwd = secret_password_lookup_sync (OTPCLIENT_SCHEMA, NULL, 
NULL, "string", "main_pwd", NULL);
+        if (pwd == NULL) {
+            goto get_pwd;
+        } else {
+            db_data->key_stored = TRUE;
+            db_data->key= secure_strdup (pwd);
+            secret_password_free (pwd);
+        }
+    } else {
+        get_pwd:
+        db_data->key = get_pwd (_("Type the DB decryption password: "));
+        if (db_data->key == NULL) {
+            g_print ("Password was NULL, exiting...\n");
+            g_free (db_data);
+            return FALSE;
+        }
+    }
+
+    GError *err = NULL;
+    load_db (db_data, &err);
+    if (err != NULL) {
+        gchar *msg = g_strconcat (_("Error while loading the database: "), 
err->message, NULL);
+        g_printerr ("%s\n", msg);
+        g_free (msg);
+        free_dbdata (db_data);
+        return FALSE;
+    }
+
+    if (use_secret_service == TRUE && db_data->key_stored == FALSE) {
+        secret_password_store (OTPCLIENT_SCHEMA, SECRET_COLLECTION_DEFAULT, 
"main_pwd", db_data->key, NULL, on_password_stored, NULL, "string", "main_pwd", 
NULL);
+    }
+
+    if (cmdline_opts->show) {
+        show_token (db_data, cmdline_opts->account, cmdline_opts->issuer, 
cmdline_opts->match_exact, cmdline_opts->show_next);
+    }
+
+    if (cmdline_opts->list) {
+        list_all_acc_iss (db_data);
+    }
+
+    if (cmdline_opts->export) {
+        gchar *export_directory;
+#ifdef USE_FLATPAK_APP_FOLDER
+        export_directory = g_get_user_data_dir ();
+#else
+        export_directory = (cmdline_opts->export_dir != NULL) ? 
cmdline_opts->export_dir : (gchar *)g_get_home_dir ();
+        if (!g_file_test (export_directory, G_FILE_TEST_IS_DIR)) {
+            g_printerr (_("%s is not a directory or the folder doesn't exist. 
The output will be saved into the HOME directory.\n"), export_directory);
+            export_directory = (gchar *)g_get_home_dir ();
+        }
+#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) {
+                export_pwd = get_pwd (_("Type the export encryption password: 
"));
+                if (export_pwd == NULL) {
+                    free_dbdata (db_data);
+                    return FALSE;
+                }
+            }
+            exported_file_path = g_build_filename (export_directory, 
export_pwd != NULL ? "andotp_exports.json.aes" : "andotp_exports.json", NULL);
+            ret_msg = export_andotp (exported_file_path, export_pwd, 
db_data->json_data);
+            gcry_free (export_pwd);
+            exported = TRUE;
+        }
+        if (g_ascii_strcasecmp (cmdline_opts->export_type, "freeotpplus") == 
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) {
+                export_pwd = get_pwd (_("Type the export encryption password: 
"));
+                if (export_pwd == NULL) {
+                    free_dbdata (db_data);
+                    return FALSE;
+                }
+            }
+            exported_file_path = g_build_filename (export_directory, 
export_pwd != NULL ? "aegis_exports.json.aes" : "aegis_exports.json", NULL);
+            ret_msg = export_aegis (exported_file_path, db_data->json_data, 
export_pwd);
+            gcry_free (export_pwd);
+            exported = TRUE;
+        }
+        if (ret_msg != NULL) {
+            g_printerr (_("An error occurred while exporting the data: %s\n"), 
ret_msg);
+            g_free (ret_msg);
+        } else {
+            if (exported) {
+                g_print (_("Data successfully exported to: %s\n"), 
exported_file_path);
+            } else {
+                gchar *msg = g_strconcat ("Option not recognized: ", 
cmdline_opts->export_type, NULL);
+                g_print ("%s\n", msg);
+                g_free (msg);
+                return FALSE;
+            }
+        }
+        g_free (exported_file_path);
+    }
+    return TRUE;
+}
+
+#ifndef USE_FLATPAK_APP_FOLDER
+static gchar *
+get_db_path (void)
+{
+    gchar *db_path = NULL;
+    GError *err = NULL;
+    GKeyFile *kf = g_key_file_new ();
+    gchar *cfg_file_path = g_build_filename (g_get_user_config_dir (), 
"otpclient.cfg", NULL);
+    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_printerr ("%s\n", err->message);
+            g_key_file_free (kf);
+            g_clear_error (&err);
+            return NULL;
+        }
+        db_path = g_key_file_get_string (kf, "config", "db_path", NULL);
+        if (db_path == NULL) {
+            goto type_db_path;
+        }
+        if (!g_file_test (db_path, G_FILE_TEST_EXISTS)) {
+            gchar *msg = g_strconcat ("Database file/location (", db_path, ") 
does not exist.\n", NULL);
+            g_printerr ("%s\n", msg);
+            g_free (msg);
+            goto type_db_path;
+        }
+        goto end;
+    }
+    type_db_path: ; // empty statement workaround
+    g_print ("%s", _("Type the absolute path to the database: "));
+    db_path = g_malloc0 (MAX_ABS_PATH_LEN);
+    if (fgets (db_path, MAX_ABS_PATH_LEN, stdin) == NULL) {
+        g_printerr ("%s\n", _("Couldn't get db path from stdin"));
+        g_free (cfg_file_path);
+        g_free (db_path);
+        return NULL;
+    } else {
+        // remove the newline char
+        db_path[g_utf8_strlen (db_path, -1) - 1] = '\0';
+        if (!g_file_test (db_path, G_FILE_TEST_EXISTS)) {
+            g_printerr (_("File '%s' does not exist\n"), db_path);
+            g_free (cfg_file_path);
+            g_free (db_path);
+            return NULL;
+        }
+    }
+
+    end:
+    g_free (cfg_file_path);
+
+    return db_path;
+}
+#endif
+
+
+static gchar *
+get_pwd (const gchar *pwd_msg)
+{
+    gchar *pwd = gcry_calloc_secure (256, 1);
+    g_print ("%s", pwd_msg);
+
+    struct termios old, new;
+    if (tcgetattr (STDIN_FILENO, &old) != 0) {
+        g_printerr ("%s\n", _("Couldn't get termios info"));
+        gcry_free (pwd);
+        return NULL;
+    }
+    new = old;
+    new.c_lflag &= ~ECHO;
+    if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &new) != 0) {
+        g_printerr ("%s\n", _("Couldn't turn echoing off"));
+        gcry_free (pwd);
+        return NULL;
+    }
+    if (fgets (pwd, 256, stdin) == NULL) {
+        g_printerr ("%s\n", _("Couldn't read password from stdin"));
+        gcry_free (pwd);
+        return NULL;
+    }
+    g_print ("\n");
+    tcsetattr (STDIN_FILENO, TCSAFLUSH, &old);
+
+    pwd[g_utf8_strlen (pwd, -1) - 1] = '\0';
+
+    gchar *realloc_pwd = gcry_realloc (pwd, g_utf8_strlen (pwd, -1) + 1);
+
+    return realloc_pwd;
+}
+
+
+static gboolean
+get_use_secretservice (void)
+{
+    gboolean use_secret_service = TRUE; // by default, we enable it
+    GError *err = NULL;
+    GKeyFile *kf = g_key_file_new ();
+    gchar *cfg_file_path = g_build_filename (g_get_user_config_dir (), 
"otpclient.cfg", NULL);
+    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_printerr ("%s\n", err->message);
+            g_key_file_free (kf);
+            g_clear_error (&err);
+            return FALSE;
+        }
+        use_secret_service = g_key_file_get_boolean (kf, "config", 
"use_secret_service", NULL);
+    }
+    return use_secret_service;
+}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/cli/help.c 
new/OTPClient-3.4.1/src/cli/help.c
--- old/OTPClient-3.3.0/src/cli/help.c  2024-01-08 12:16:54.000000000 +0100
+++ new/OTPClient-3.4.1/src/cli/help.c  1970-01-01 01:00:00.000000000 +0100
@@ -1,121 +0,0 @@
-#include <glib.h>
-#include <glib/gi18n.h>
-#include "version.h"
-#include "../common/common.h"
-
-static void print_main_help   (const gchar *prg_name);
-
-static void print_show_help   (const gchar *prg_name);
-
-static void print_export_help (const gchar *prg_name);
-
-
-gboolean show_help (const gchar *prg_name,
-                    const gchar *help_command)
-{
-    gboolean help_displayed = FALSE;
-    if (g_strcmp0 (help_command, "-h") == 0 || g_strcmp0 (help_command, 
"--help") == 0 || g_strcmp0 (help_command, "help") == 0 ||
-            help_command == NULL || g_utf8_strlen (help_command, -1) < 2) {
-        print_main_help (prg_name);
-        help_displayed = TRUE;
-    } else if (g_strcmp0 (help_command, "-v") == 0 || g_strcmp0 (help_command, 
"--version") == 0) {
-        g_print ("%s v%s\n", PROJECT_NAME, PROJECT_VER);
-        help_displayed = TRUE;
-    } else if (g_strcmp0 (help_command, "--help-show") == 0 || g_strcmp0 
(help_command, "help-show") == 0) {
-        print_show_help (prg_name);
-        help_displayed = TRUE;
-    } else if (g_strcmp0 (help_command, "--help-export") == 0 || g_strcmp0 
(help_command, "help-export") == 0) {
-        print_export_help (prg_name);
-        help_displayed = TRUE;
-    }
-
-    return help_displayed;
-}
-
-
-static void
-print_main_help (const gchar *prg_name)
-{
-    GString *msg = g_string_new (_("Usage:\n  %s <main option> [option 1] 
[option 2] ..."));
-#if GLIB_CHECK_VERSION(2, 68, 0)
-    g_string_replace (msg, "%s", prg_name, 0);
-#else
-    g_string_replace_backported (msg, "%s", prg_name, 0);
-#endif
-    g_print ("%s\n\n", msg->str);
-    g_string_free (msg, TRUE);
-
-    // Translators: please do not translate 'help'
-    g_print ("%s\n", _("help command options:"));
-
-    // Translators: please do not translate '-h, --help'
-    g_print ("%s\n", _("  -h, --help\t\tShow this help"));
-
-    // Translators: please do not translate '--help-show'
-    g_print ("%s\n", _("  --help-show\t\tShow options"));
-
-    // Translators: please do not translate '--help-export'
-    g_print ("%s\n\n", _("  --help-export\t\tExport options"));
-    g_print ("%s\n", _("Main options:"));
-
-    // Translators: please do not translate '-v, --version'
-    g_print ("%s\n", _("  -v, --version\t\t\t\tShow program version"));
-
-    // Translators: please do not translate 'show <-a ..> [-i ..] [-m] [-n]'
-    g_print ("%s\n", _("  show <-a ..> [-i ..] [-m] [-n]\tShow a token"));
-
-    // Translators: please do not translate 'list'
-    g_print ("%s\n", _("  list\t\t\t\t\tList all pairs of account and 
issuer"));
-
-    // Translators: please do not translate 'export <-t ..> [-d ..]'
-    g_print ("%s\n\n", _("  export <-t ..> [-d ..]\t\tExport data"));
-}
-
-
-static void
-print_show_help (const gchar *prg_name)
-{
-    // Translators: please do not translate '%s show'
-    GString *msg = g_string_new (_("Usage:\n  %s show <-a ..> [-i ..] [-m]"));
-#if GLIB_CHECK_VERSION(2, 68, 0)
-    g_string_replace (msg, "%s", prg_name, 0);
-#else
-    g_string_replace_backported (msg, "%s", prg_name, 0);
-#endif
-    g_print ("%s\n\n", msg->str);
-    g_string_free (msg, TRUE);
-
-    // Translators: please do not translate 'show'
-    g_print ("%s\n", _("show command options:"));
-    // Translators: please do not translate '-a, --account'
-    g_print ("%s\n", _("  -a, --account\t\tThe account name (mandatory)"));
-    // Translators: please do not translate '-i, --issuer'
-    g_print ("%s\n", _("  -i, --issuer\t\tThe issuer name (optional)"));
-    // Translators: please do not translate '-m, --match-exactly'
-    g_print ("%s\n", _("  -m, --match-exactly\tShow the token only if it 
matches exactly the account and/or the issuer (optional)"));
-    // Translators: please do not translate '-n, --next'
-    g_print ("%s\n\n", _("  -n, --next\tShow also the next token, not only the 
current one (optional, valid only for TOTP)"));
-}
-
-
-static void
-print_export_help (const gchar *prg_name)
-{
-    // Translators: please do not translate '%s export'
-    GString *msg = g_string_new (_("Usage:\n  %s export <-t> <andotp | 
freeotpplus | aegis> [-d ..]"));
-#if GLIB_CHECK_VERSION(2, 68, 0)
-    g_string_replace (msg, "%s", prg_name, 0);
-#else
-    g_string_replace_backported (msg, "%s", prg_name, 0);
-#endif
-    g_print ("%s\n\n", msg->str);
-    g_string_free (msg, TRUE);
-
-    // Translators: please do not translate 'export'
-    g_print ("%s\n", _("export command options:"));
-    // Translators: please do not translate '-t, --type'
-    g_print ("%s\n", _("  -t, --type\t\tExport format. Must be either one of: 
andotp_plain, andotp_encrypted, freeotpplus, aegis"));
-    // Translators: please do not translate '-d, --directory'
-    g_print ("%s\n", _("  -d, --directory\tThe output directory where the 
exported file will be saved."));
-    g_print ("%s\n\n", _("\t\t\tIf nothing is specified OR flatpak is being 
used, the output folder will be the user's HOME directory."));
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/cli/help.h 
new/OTPClient-3.4.1/src/cli/help.h
--- old/OTPClient-3.3.0/src/cli/help.h  2024-01-08 12:16:54.000000000 +0100
+++ new/OTPClient-3.4.1/src/cli/help.h  1970-01-01 01:00:00.000000000 +0100
@@ -1,10 +0,0 @@
-#pragma once
-
-#include <glib.h>
-
-G_BEGIN_DECLS
-
-gboolean show_help  (const gchar *prg_name,
-                     const gchar *help_command);
-
-G_END_DECLS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/cli/main.c 
new/OTPClient-3.4.1/src/cli/main.c
--- old/OTPClient-3.3.0/src/cli/main.c  2024-01-08 12:16:54.000000000 +0100
+++ new/OTPClient-3.4.1/src/cli/main.c  2024-02-12 08:48:17.000000000 +0100
@@ -1,303 +1,181 @@
 #include <glib.h>
-#include <string.h>
-#include <gcrypt.h>
-#include <termios.h>
-#include <libsecret/secret.h>
-#include <glib/gi18n.h>
-#include "help.h"
-#include "get-data.h"
+#include <gio/gio.h>
+#include "version.h"
+#include "main.h"
 #include "../common/common.h"
-#include "../common/exports.h"
-#include "../secret-schema.h"
 
-#define MAX_ABS_PATH_LEN 256
+static gint      handle_local_options  (GApplication            *application,
+                                        GVariantDict            *options,
+                                        gpointer                 user_data);
+
+static int       command_line          (GApplication            *application,
+                                        GApplicationCommandLine *cmdline,
+                                        gpointer                 user_data);
 
-#ifndef USE_FLATPAK_APP_FOLDER
-static gchar    *get_db_path           (void);
-#endif
-
-static gchar    *get_pwd               (const gchar *pwd_msg);
+static gboolean  parse_options         (GApplicationCommandLine *cmdline,
+                                        CmdlineOpts             *cmdline_opts);
 
-static gboolean  get_use_secretservice (void);
+static void      g_free_cmdline_opts   (CmdlineOpts             *co);
 
 
 gint
 main (gint    argc,
       gchar **argv)
 {
-    if (show_help (argv[0], argv[1])) {
-        return 0;
-    }
+    GOptionEntry entries[] =
+            {
+#ifndef USE_FLATPAK_APP_FOLDER
+                    { "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 },
+                    { "account", 'a', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, 
NULL, "Account name (to be used with --show, mandatory)", NULL},
+                    { "issuer", 'i', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, 
NULL, "Issuer (to be used with --show, optional)", NULL},
+                    { "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, aegis_encrypted (to be used with 
--export, mandatory)", NULL },
+#ifndef USE_FLATPAK_APP_FOLDER
+                    { "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 },
+                    { NULL }
+            };
 
-    DatabaseData *db_data = g_new0 (DatabaseData, 1);
-    db_data->key_stored = FALSE;
 
-    db_data->max_file_size_from_memlock = get_max_file_size_from_memlock ();
-    gchar *init_msg = init_libs (db_data->max_file_size_from_memlock);
-    if (init_msg != NULL) {
-        g_printerr ("%s\n", init_msg);
-        g_free (init_msg);
-        g_free (db_data);
-        return -1;
-    }
+    const gchar *ctx_text = "- Highly secure and easy to use OTP client that 
supports both TOTP and HOTP";
 
-#ifdef USE_FLATPAK_APP_FOLDER
-    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);
-    if (!g_file_test (cfg_file_path, G_FILE_TEST_EXISTS)) {
-        g_file_set_contents (cfg_file_path, "[config]", -1, NULL);
-    }
-    g_free (cfg_file_path);
-#else
-    db_data->db_path = get_db_path ();
-    if (db_data->db_path == NULL) {
-        g_free (db_data);
-        return -1;
-    }
-#endif
+    GApplication *app = g_application_new 
("com.github.paolostivanin.OTPClient", G_APPLICATION_HANDLES_COMMAND_LINE);
 
-    gboolean use_secret_service = get_use_secretservice ();
-    if (use_secret_service == TRUE) {
-        gchar *pwd = secret_password_lookup_sync (OTPCLIENT_SCHEMA, NULL, 
NULL, "string", "main_pwd", NULL);
-        if (pwd == NULL) {
-            goto get_pwd;
-        } else {
-            db_data->key_stored = TRUE;
-            db_data->key= secure_strdup (pwd);
-            secret_password_free (pwd);
-        }
-    } else {
-        get_pwd:
-        db_data->key = get_pwd (_("Type the DB decryption password: "));
-        if (db_data->key == NULL) {
-            g_free (db_data);
-            return -1;
-        }
-    }
+    g_application_add_main_option_entries (app, entries);
+    g_application_set_option_context_parameter_string (app, ctx_text);
 
-    db_data->objects_hash = NULL;
+    g_signal_connect (app, "handle-local-options", G_CALLBACK 
(handle_local_options), NULL);
+    g_signal_connect (app, "command-line", G_CALLBACK(command_line), NULL);
 
-    GError *err = NULL;
-    load_db (db_data, &err);
-    if (err != NULL) {
-        const gchar *tmp_msg = _("Error while loading the database:");
-        gchar *msg = g_strconcat (tmp_msg, " %s\n", err->message, NULL);
-        g_printerr ("%s\n", msg);
-        gcry_free (db_data->key);
-        g_free (db_data);
-        g_free (msg);
-        return -1;
-    }
+    int status = g_application_run (app, argc, argv);
 
-    if (use_secret_service == TRUE && db_data->key_stored == FALSE) {
-        secret_password_store (OTPCLIENT_SCHEMA, SECRET_COLLECTION_DEFAULT, 
"main_pwd", db_data->key, NULL, on_password_stored, NULL, "string", "main_pwd", 
NULL);
-    }
+    g_object_unref (app);
 
-    gchar *account = NULL, *issuer = NULL;
-    gboolean show_next_token = FALSE, match_exactly = FALSE;
+    return status;
+}
 
-    if (g_strcmp0 (argv[1], "show") == 0) {
-        if (argc < 4 || argc > 8) {
-            // Translators: please do not translate '%s --help-show'
-            g_printerr (_("Wrong argument(s). Please type '%s --help-show' to 
see the available options.\n"), argv[0]);
-            g_free (db_data);
-            return -1;
-        }
-        for (gint i = 2; i < argc; i++) {
-            if (g_strcmp0 (argv[i], "-a") == 0) {
-                account = argv[i + 1];
-            } else if (g_strcmp0 (argv[i], "-i") == 0) {
-                issuer = argv[i + 1];
-            } else if (g_strcmp0 (argv[i], "-m") == 0) {
-                match_exactly = TRUE;
-            } else if (g_strcmp0 (argv[i], "-n") == 0) {
-                show_next_token = TRUE;
-            }
-        }
-        if (account == NULL) {
-            // Translators: please do not translate 'account'
-            g_printerr ("%s\n", _("[ERROR]: The account option (-a) must be 
specified and can not be empty."));
-            goto end;
-        }
-        show_token (db_data, account, issuer, match_exactly, show_next_token);
-    } else if (g_strcmp0 (argv[1], "list") == 0) {
-        list_all_acc_iss (db_data);
-    } else if (g_strcmp0 (argv[1], "export") == 0) {
-        if (g_ascii_strcasecmp (argv[3], "andotp_plain") != 0 && 
g_ascii_strcasecmp (argv[3], "andotp_encrypted") != 0 &&
-            g_ascii_strcasecmp (argv[3], "freeotpplus") != 0 && 
g_ascii_strcasecmp (argv[3], "aegis") != 0) {
-                // Translators: please do not translate '%s --help-export'
-                g_printerr (_("Wrong argument(s). Please type '%s 
--help-export' to see the available options.\n"), argv[0]);
-                g_free (db_data);
-                return -1;
-        }
-        const gchar *base_dir = NULL;
-#ifndef USE_FLATPAK_APP_FOLDER
-        if (argv[4] == NULL) {
-            base_dir = g_get_home_dir ();
-        } else {
-            if (g_ascii_strcasecmp (argv[4], "-d") == 0 && argv[5] != NULL) {
-                if (!g_file_test (argv[5], G_FILE_TEST_IS_DIR)) {
-                    g_printerr (_("%s is not a directory or the folder doesn't 
exist. The output will be saved into the HOME directory.\n"), argv[5]);
-                    base_dir = g_get_home_dir ();
-                } else {
-                    base_dir = argv[5];
-                }
-            } else {
-                g_printerr ("%s\n", _("Incorrect parameters used for setting 
the output folder. Therefore, the exported file will be saved into the HOME 
directory."));
-                base_dir = g_get_home_dir ();
-            }
-        }
-#else
-        base_dir = g_get_user_data_dir ();
-#endif
-        gchar *andotp_export_pwd = NULL, *exported_file_path = NULL, *ret_msg 
= NULL;
-        if (g_ascii_strcasecmp (argv[3], "andotp_plain") == 0 || 
g_ascii_strcasecmp (argv[3], "andotp_encrypted") == 0) {
-            if (g_ascii_strcasecmp (argv[3], "andotp_encrypted")) {
-                andotp_export_pwd = get_pwd (_("Type the export encryption 
password: "));
-                if (andotp_export_pwd == NULL) {
-                    goto end;
-                }
-            }
-            exported_file_path = g_build_filename (base_dir, andotp_export_pwd 
!= NULL ? "andotp_exports.json.aes" : "andotp_exports.json", NULL);
-            ret_msg = export_andotp (exported_file_path, andotp_export_pwd, 
db_data->json_data);
-            gcry_free (andotp_export_pwd);
-        }
-        if (g_ascii_strcasecmp (argv[3], "freeotpplus") == 0) {
-            exported_file_path = g_build_filename (base_dir, 
"freeotpplus-exports.txt", NULL);
-            ret_msg = export_freeotpplus (exported_file_path, 
db_data->json_data);
-        }
-        if (g_ascii_strcasecmp (argv[3], "aegis") == 0) {
-            exported_file_path = g_build_filename (base_dir, 
"aegis_export_plain.json", NULL);
-            ret_msg = export_aegis (exported_file_path, db_data->json_data, 
NULL);
-        }
-        if (ret_msg != NULL) {
-            g_printerr (_("An error occurred while exporting the data: %s\n"), 
ret_msg);
-            g_free (ret_msg);
-        } else {
-            g_print (_("Data successfully exported to: %s\n"), 
exported_file_path);
-        }
-        g_free (exported_file_path);
-    } else {
-        show_help (argv[0], "help");
-        return -1;
-    }
 
-    end:
+void
+free_dbdata (DatabaseData *db_data)
+{
     gcry_free (db_data->key);
     g_free (db_data->db_path);
     g_slist_free_full (db_data->objects_hash, g_free);
     json_decref (db_data->json_data);
     g_free (db_data);
-
-    return 0;
 }
 
 
-#ifndef USE_FLATPAK_APP_FOLDER
-static gchar *
-get_db_path (void)
+static gint
+handle_local_options (GApplication      *application __attribute__((unused)),
+                      GVariantDict      *options,
+                      gpointer           user_data   __attribute__((unused)))
 {
-    gchar *db_path = NULL;
-    GError *err = NULL;
-    GKeyFile *kf = g_key_file_new ();
-    gchar *cfg_file_path = g_build_filename (g_get_user_config_dir (), 
"otpclient.cfg", NULL);
-    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_printerr ("%s\n", err->message);
-            g_key_file_free (kf);
-            g_clear_error (&err);
-            return NULL;
-        }
-        db_path = g_key_file_get_string (kf, "config", "db_path", NULL);
-        if (db_path == NULL) {
-            goto type_db_path;
-        }
-        if (!g_file_test (db_path, G_FILE_TEST_EXISTS)) {
-            gchar *msg = g_strconcat ("Database file/location (", db_path, ") 
does not exist.\n", NULL);
-            g_printerr ("%s\n", msg);
-            g_free (msg);
-            goto type_db_path;
-        }
-        goto end;
-    }
-    type_db_path: ; // empty statement workaround
-    g_print ("%s", _("Type the absolute path to the database: "));
-    db_path = g_malloc0 (MAX_ABS_PATH_LEN);
-    if (fgets (db_path, MAX_ABS_PATH_LEN, stdin) == NULL) {
-        g_printerr ("%s\n", _("Couldn't get db path from stdin"));
-        g_free (cfg_file_path);
-        g_free (db_path);
-        return NULL;
-    } else {
-        // remove the newline char
-        db_path[g_utf8_strlen (db_path, -1) - 1] = '\0';
-        if (!g_file_test (db_path, G_FILE_TEST_EXISTS)) {
-            g_printerr (_("File '%s' does not exist\n"), db_path);
-            g_free (cfg_file_path);
-            g_free (db_path);
-            return NULL;
-        }
+    guint32 count;
+    if (g_variant_dict_lookup (options, "version", "b", &count)) {
+        gchar *msg = g_strconcat ("OTPClient version ", PROJECT_VER, NULL);
+        g_print ("%s\n", msg);
+        g_free (msg);
+        return 0;
     }
-
-    end:
-    g_free (cfg_file_path);
-
-    return db_path;
+    return -1;
 }
-#endif
 
 
-static gchar *
-get_pwd (const gchar *pwd_msg)
+static int
+command_line (GApplication                *application __attribute__((unused)),
+              GApplicationCommandLine     *cmdline,
+              gpointer                     user_data   __attribute__((unused)))
 {
-    gchar *pwd = gcry_calloc_secure (256, 1);
-    g_print ("%s", pwd_msg);
+    DatabaseData *db_data = g_new0 (DatabaseData, 1);
+    db_data->key_stored = FALSE;
+    db_data->objects_hash = NULL;
 
-    struct termios old, new;
-    if (tcgetattr (STDIN_FILENO, &old) != 0) {
-        g_printerr ("%s\n", _("Couldn't get termios info"));
-        gcry_free (pwd);
-        return NULL;
-    }
-    new = old;
-    new.c_lflag &= ~ECHO;
-    if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &new) != 0) {
-        g_printerr ("%s\n", _("Couldn't turn echoing off"));
-        gcry_free (pwd);
-        return NULL;
+    db_data->max_file_size_from_memlock = get_max_file_size_from_memlock ();
+    gchar *init_msg = init_libs (db_data->max_file_size_from_memlock);
+    if (init_msg != NULL) {
+        g_application_command_line_printerr(cmdline, "Error while initializing 
GCrypt: %s\n", init_msg);
+        g_free (init_msg);
+        g_free (db_data);
+        return -1;
     }
-    if (fgets (pwd, 256, stdin) == NULL) {
-        g_printerr ("%s\n", _("Couldn't read password from stdin"));
-        gcry_free (pwd);
-        return NULL;
+
+    CmdlineOpts *cmdline_opts = g_new0 (CmdlineOpts, 1);
+    cmdline_opts->database = NULL;
+    cmdline_opts->show = FALSE;
+    cmdline_opts->account = NULL;
+    cmdline_opts->issuer = NULL;
+    cmdline_opts->match_exact = FALSE;
+    cmdline_opts->show_next = FALSE;
+    cmdline_opts->list = FALSE;
+    cmdline_opts->export = FALSE;
+    cmdline_opts->export_type = NULL;
+    cmdline_opts->export_dir = NULL;
+
+    if (!parse_options (cmdline, cmdline_opts)) {
+        g_free (db_data);
+        g_free_cmdline_opts (cmdline_opts);
+        return -1;
     }
-    g_print ("\n");
-    tcsetattr (STDIN_FILENO, TCSAFLUSH, &old);
 
-    pwd[g_utf8_strlen (pwd, -1) - 1] = '\0';
+    if (!exec_action (cmdline_opts, db_data)) {
+        g_free_cmdline_opts (cmdline_opts);
+        return -1;
+    }
 
-    gchar *realloc_pwd = gcry_realloc (pwd, g_utf8_strlen (pwd, -1) + 1);
+    free_dbdata (db_data);
+    g_free_cmdline_opts (cmdline_opts);
 
-    return realloc_pwd;
+    return 0;
 }
 
 
 static gboolean
-get_use_secretservice (void)
+parse_options (GApplicationCommandLine *cmdline,
+               CmdlineOpts             *cmdline_opts)
 {
-    gboolean use_secret_service = TRUE; // by default, we enable it
-    GError *err = NULL;
-    GKeyFile *kf = g_key_file_new ();
-    gchar *cfg_file_path = g_build_filename (g_get_user_config_dir (), 
"otpclient.cfg", NULL);
-    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_printerr ("%s\n", err->message);
-            g_key_file_free (kf);
-            g_clear_error (&err);
+    GVariantDict *options = g_application_command_line_get_options_dict 
(cmdline);
+
+    g_variant_dict_lookup (options, "database", "s", &cmdline_opts->database);
+
+    if (g_variant_dict_lookup (options, "show", "b", &cmdline_opts->show)) {
+        if (!g_variant_dict_lookup (options, "account", "s", 
&cmdline_opts->account)) {
+            g_application_command_line_print (cmdline, "Please provide at 
least the account option.\n");
             return FALSE;
         }
-        use_secret_service = g_key_file_get_boolean (kf, "config", 
"use_secret_service", NULL);
+        g_variant_dict_lookup (options, "issuer", "s", &cmdline_opts->issuer);
+        g_variant_dict_lookup (options, "match-exact", "b", 
&cmdline_opts->match_exact);
+        g_variant_dict_lookup (options, "show-next", "b", 
&cmdline_opts->show_next);
     }
-    return use_secret_service;
+
+    g_variant_dict_lookup (options, "list", "b", &cmdline_opts->list);
+
+    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");
+            return FALSE;
+        }
+#ifndef USE_FLATPAK_APP_FOLDER
+        g_variant_dict_lookup (options, "output-dir", "s", 
&cmdline_opts->export_dir);
+#endif
+    }
+    return TRUE;
+}
+
+
+static void
+g_free_cmdline_opts (CmdlineOpts *co)
+{
+    g_free (co->database);
+    g_free (co->account);
+    g_free (co->issuer);
+    g_free (co->export_type);
+    g_free (co->export_dir);
+    g_free (co);
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/cli/main.h 
new/OTPClient-3.4.1/src/cli/main.h
--- old/OTPClient-3.3.0/src/cli/main.h  1970-01-01 01:00:00.000000000 +0100
+++ new/OTPClient-3.4.1/src/cli/main.h  2024-02-12 08:48:17.000000000 +0100
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <glib.h>
+#include "../data.h"
+
+G_BEGIN_DECLS
+
+#define MAX_ABS_PATH_LEN 256
+
+typedef struct _cmdline_opts {
+    gchar *database;
+    gboolean show;
+    gchar *account;
+    gchar *issuer;
+    gboolean match_exact;
+    gboolean show_next;
+    gboolean list;
+    gboolean export;
+    gchar *export_type;
+    gchar *export_dir;
+} CmdlineOpts;
+
+gboolean exec_action (CmdlineOpts  *cmdline_opts,
+                      DatabaseData *db_data);
+
+void     free_dbdata (DatabaseData *db_data);
+
+G_END_DECLS
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/common/common.c 
new/OTPClient-3.4.1/src/common/common.c
--- old/OTPClient-3.3.0/src/common/common.c     2024-01-08 12:16:54.000000000 
+0100
+++ new/OTPClient-3.4.1/src/common/common.c     2024-02-12 08:48:17.000000000 
+0100
@@ -34,6 +34,7 @@
 gchar *
 init_libs (gint32 max_file_size)
 {
+    gcry_control(GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
     if (!gcry_check_version ("1.8.0")) {
         return g_strdup ("The required version of GCrypt is 1.8.0 or 
greater.");
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/common/freeotp.c 
new/OTPClient-3.4.1/src/common/freeotp.c
--- old/OTPClient-3.3.0/src/common/freeotp.c    2024-01-08 12:16:54.000000000 
+0100
+++ new/OTPClient-3.4.1/src/common/freeotp.c    2024-02-12 08:48:17.000000000 
+0100
@@ -45,7 +45,7 @@
     if (err == NULL) {
         json_array_foreach (json_db_data, index, db_obj) {
             gchar *uri = get_otpauth_uri (NULL, db_obj);
-            if (g_output_stream_write (G_OUTPUT_STREAM(out_stream), uri, 
g_utf8_strlen (uri, -1) + 1, NULL, &err) == -1) {
+            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");
             }
             g_free (uri);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/db-actions.c 
new/OTPClient-3.4.1/src/db-actions.c
--- old/OTPClient-3.3.0/src/db-actions.c        2024-01-08 12:16:54.000000000 
+0100
+++ new/OTPClient-3.4.1/src/db-actions.c        2024-02-12 08:48:17.000000000 
+0100
@@ -67,3 +67,14 @@
     g_free (cfg_file_path);
     g_key_file_free (kf);
 }
+
+
+void
+revert_db_path (AppData *app_data,
+                gchar   *old_db_path)
+{
+    g_free (app_data->db_data->db_path);
+    app_data->db_data->db_path = g_strdup (old_db_path);
+    update_cfg_file (app_data);
+    g_free (old_db_path);
+}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/db-actions.h 
new/OTPClient-3.4.1/src/db-actions.h
--- old/OTPClient-3.3.0/src/db-actions.h        2024-01-08 12:16:54.000000000 
+0100
+++ new/OTPClient-3.4.1/src/db-actions.h        2024-02-12 08:48:17.000000000 
+0100
@@ -8,11 +8,14 @@
 #define ACTION_OPEN 5
 #define ACTION_SAVE 10
 
-void select_file_icon_pressed_cb (GtkEntry         *entry,
-                                  gint              position,
-                                  GdkEventButton   *event,
-                                  gpointer          data);
+void select_file_icon_pressed_cb (GtkEntry       *entry,
+                                  gint            position,
+                                  GdkEventButton *event,
+                                  gpointer        data);
 
-void update_cfg_file             (AppData *app_data);
+void update_cfg_file             (AppData        *app_data);
+
+void revert_db_path              (AppData        *app_data,
+                                  gchar          *old_db_path);
 
 G_END_DECLS
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/new-db-cb.c 
new/OTPClient-3.4.1/src/new-db-cb.c
--- old/OTPClient-3.3.0/src/new-db-cb.c 2024-01-08 12:16:54.000000000 +0100
+++ new/OTPClient-3.4.1/src/new-db-cb.c 2024-02-12 08:48:17.000000000 +0100
@@ -7,13 +7,11 @@
 #include "password-cb.h"
 #include "db-actions.h"
 #include "secret-schema.h"
+#include "change-file-cb.h"
 
-void
-new_db_cb (GSimpleAction *simple    __attribute__((unused)),
-           GVariant      *parameter __attribute__((unused)),
-           gpointer       user_data)
+int
+new_db (AppData *app_data)
 {
-    AppData *app_data = (AppData *)user_data;
     GtkWidget *newdb_diag = GTK_WIDGET(gtk_builder_get_object 
(app_data->builder, "newdb_diag_id"));
     GtkWidget *newdb_entry = GTK_WIDGET(gtk_builder_get_object 
(app_data->builder, "newdb_entry_id"));
 
@@ -24,35 +22,69 @@
     gint result = gtk_dialog_run (GTK_DIALOG (newdb_diag));
     switch (result) {
         case GTK_RESPONSE_OK:
+            if (gtk_entry_get_text_length (GTK_ENTRY(newdb_entry)) == 0) {
+                show_message_dialog (app_data->main_window, "Input cannot be 
empty.", GTK_MESSAGE_ERROR);
+                gtk_widget_hide (newdb_diag);
+                return RETRY_CHANGE;
+            }
             new_db_path_with_suffix = g_string_new (gtk_entry_get_text 
(GTK_ENTRY(newdb_entry)));
             g_string_append (new_db_path_with_suffix, ".enc");
             if (g_file_test (new_db_path_with_suffix->str, 
G_FILE_TEST_IS_REGULAR) || g_file_test (new_db_path_with_suffix->str, 
G_FILE_TEST_IS_SYMLINK)) {
                 show_message_dialog (app_data->main_window, "Selected file 
already exists, please choose another filename.", GTK_MESSAGE_ERROR);
+                g_string_free (new_db_path_with_suffix, TRUE);
+                return RETRY_CHANGE;
             } else {
+                gchar *old_db_path = g_strdup (app_data->db_data->db_path);
                 g_free (app_data->db_data->db_path);
                 app_data->db_data->db_path = g_strdup 
(new_db_path_with_suffix->str);
                 update_cfg_file (app_data);
                 gcry_free (app_data->db_data->key);
                 app_data->db_data->key = prompt_for_password (app_data, NULL, 
NULL, FALSE);
+                if (app_data->db_data->key == NULL) {
+                    gtk_widget_hide (newdb_diag);
+                    revert_db_path (app_data, old_db_path);
+                    return RETRY_CHANGE;
+                }
                 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);
                 if (err != NULL) {
                     show_message_dialog (app_data->main_window, err->message, 
GTK_MESSAGE_ERROR);
                     g_clear_error (&err);
-                } else {
-                    load_new_db (app_data, &err);
-                    if (err != NULL) {
-                        show_message_dialog (app_data->main_window, 
err->message, GTK_MESSAGE_ERROR);
-                        g_clear_error (&err);
-                    }
+                    gtk_widget_hide (newdb_diag);
+                    revert_db_path (app_data, old_db_path);
+                    return RETRY_CHANGE;
+                }
+                load_new_db (app_data, &err);
+                if (err != NULL) {
+                    show_message_dialog (app_data->main_window, err->message, 
GTK_MESSAGE_ERROR);
+                    g_clear_error (&err);
+                    gtk_widget_hide (newdb_diag);
+                    revert_db_path (app_data, old_db_path);
+                    return RETRY_CHANGE;
                 }
+                g_free (old_db_path);
             }
             g_string_free (new_db_path_with_suffix, TRUE);
             break;
         case GTK_RESPONSE_CANCEL:
+            gtk_widget_destroy (newdb_diag);
+            return QUIT_APP;
         default:
             break;
     }
     gtk_widget_destroy (newdb_diag);
+
+    return CHANGE_OK;
+}
+
+
+void
+new_db_cb (GSimpleAction *simple    __attribute__((unused)),
+           GVariant      *parameter __attribute__((unused)),
+           gpointer       user_data)
+{
+    AppData *app_data = (AppData *)user_data;
+
+    new_db (app_data);
 }
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/new-db-cb.h 
new/OTPClient-3.4.1/src/new-db-cb.h
--- old/OTPClient-3.3.0/src/new-db-cb.h 2024-01-08 12:16:54.000000000 +0100
+++ new/OTPClient-3.4.1/src/new-db-cb.h 2024-02-12 08:48:17.000000000 +0100
@@ -1,11 +1,14 @@
 #pragma once
 
 #include <gtk/gtk.h>
+#include "data.h"
 
 G_BEGIN_DECLS
 
-void new_db_cb (GSimpleAction *simple,
-                GVariant      *parameter,
-                gpointer       user_data);
+gboolean new_db     (AppData       *app_data);
+
+void     new_db_cb  (GSimpleAction *simple,
+                     GVariant      *parameter,
+                     gpointer       user_data);
 
 G_END_DECLS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/OTPClient-3.3.0/src/otpclient.h 
new/OTPClient-3.4.1/src/otpclient.h
--- old/OTPClient-3.3.0/src/otpclient.h 2024-01-08 12:16:54.000000000 +0100
+++ new/OTPClient-3.4.1/src/otpclient.h 2024-02-12 08:48:17.000000000 +0100
@@ -11,8 +11,6 @@
 void     activate              (GtkApplication *app,
                                 gpointer        user_data);
 
-gboolean change_file           (AppData *app_data);
-
 void     add_qr_from_file      (GSimpleAction  *simple,
                                 GVariant       *parameter,
                                 gpointer        user_data);

Reply via email to