Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package otpclient for openSUSE:Factory checked in at 2022-07-06 15:41:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/otpclient (Old) and /work/SRC/openSUSE:Factory/.otpclient.new.1548 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "otpclient" Wed Jul 6 15:41:51 2022 rev:16 rq:986970 version:2.6.2 Changes: -------- --- /work/SRC/openSUSE:Factory/otpclient/otpclient.changes 2022-07-02 15:34:47.639040417 +0200 +++ /work/SRC/openSUSE:Factory/.otpclient.new.1548/otpclient.changes 2022-07-06 15:42:02.982521661 +0200 @@ -1,0 +2,13 @@ +Wed Jul 6 06:53:14 UTC 2022 - Paolo Stivanin <i...@paolostivanin.com> + +- Update to 2.6.2: + * Add info message about the new secret service behavior. + +------------------------------------------------------------------- +Tue Jul 5 13:43:05 UTC 2022 - Paolo Stivanin <i...@paolostivanin.com> + +- Update to 2.6.1: + * Add ability to import google migration QR also via webcam. + * Avoid double free on error while importing encrypted aegis. + +------------------------------------------------------------------- Old: ---- v2.6.0.tar.gz v2.6.0.tar.gz.asc New: ---- v2.6.2.tar.gz v2.6.2.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ otpclient.spec ++++++ --- /var/tmp/diff_new_pack.PXlFTY/_old 2022-07-06 15:42:04.666524142 +0200 +++ /var/tmp/diff_new_pack.PXlFTY/_new 2022-07-06 15:42:04.670524149 +0200 @@ -18,7 +18,7 @@ %define uclname OTPClient Name: otpclient -Version: 2.6.0 +Version: 2.6.2 Release: 0 Summary: Simple GTK+ client for managing TOTP and HOTP License: GPL-3.0-or-later ++++++ v2.6.0.tar.gz -> v2.6.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/CMakeLists.txt new/OTPClient-2.6.2/CMakeLists.txt --- old/OTPClient-2.6.0/CMakeLists.txt 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/CMakeLists.txt 2022-07-06 08:51:34.000000000 +0200 @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(OTPClient VERSION "2.6.0" LANGUAGES "C") +project(OTPClient VERSION "2.6.2" LANGUAGES "C") configure_file("src/common/version.h.in" "version.h") include_directories(${PROJECT_BINARY_DIR}) @@ -127,6 +127,7 @@ src/common/exports.h src/parse-uri.h src/common/get-providers-data.h + src/google-migration.pb-c.h src/secret-schema.h) set(CLI_SOURCE_FILES @@ -141,19 +142,29 @@ src/common/andotp.c src/common/aegis.c src/common/freeotp.c - src/secret-schema.c) + src/secret-schema.c + src/google-migration.pb-c.c) 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/authplus.c + src/password-cb.c + src/get-builder.c src/message-dialogs.c) 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() @@ -191,6 +202,7 @@ if(BUILD_CLI) include_directories(${GTK3_INCLUDE_DIRS} ${GCRYPT_INCLUDE_DIRS} + ${BASEENCODE_INCLUDE_DIRS} ${COTP_INCLUDE_DIRS} ${BASEENCODE_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS} @@ -212,6 +224,7 @@ ${GLIB2_LIBRARIES} ${GIO_LIBRARIES} ${GCRYPT_LIBRARIES} + ${BASEENCODE_LIBRARIES} ${COTP_LIBRARIES} ${JANSSON_LIBRARIES} ${UUID_LIBRARIES} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/README.md new/OTPClient-2.6.2/README.md --- old/OTPClient-2.6.0/README.md 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/README.md 2022-07-06 08:51:34.000000000 +0200 @@ -10,19 +10,23 @@ |----------------------------------------------------|-------------| | GTK+ | 3.20 | | Glib | 2.48.0 | -| jansson | 2.6.0 | +| jansson | 2.100 | | libgcrypt | 1.6.0 | | libzip | 1.0.0 | | libpng | 1.2.0 | -| [libcotp](https://github.com/paolostivanin/libcotp) | 1.2.1 |-| +| [libbaseencode](https://github.com/paolostivanin/libbaseencode) | 1.0.12 | +| [libcotp](https://github.com/paolostivanin/libcotp) | 1.2.1 | | zbar | 0.20 | | protobuf-c | 1.30 | | protobuf | 3.6 | +| uuid | 2.34 | +| libsecret | 0.20 | :warning: Please note that the memlock value should be `>= 4 MB`. Any value less than this may cause issues when dealing with tens of tokens (especially when importing from third parties backups). See this [wiki section](https://github.com/paolostivanin/OTPClient/wiki/Secure-Memory-Limitations) for info on how to check the current value and set, if needed, a higher one. ## Features +- integration with the OS' secret service provider via libsecret - support both TOTP and HOTP - support setting custom digits (between 4 and 10 inclusive) - support setting a custom period (between 10 and 120 seconds inclusive) @@ -32,6 +36,7 @@ - import and export encrypted/plain [andOTP](https://github.com/flocke/andOTP) backup - import and export encrypted/plain [Aegis](https://github.com/beemdevelopment/Aegis) backup - import and export plain [FreeOTPPlus](https://github.com/helloworld1/FreeOTPPlus) backup (key URI format only) +- import of Google's migration QR codes - local database is encrypted using AES256-GCM - key is derived using PBKDF2 with SHA512 and 100k iterations - decrypted file is never saved (and hopefully never swapped) to disk. While the app is running, the decrypted content resides in a "secure memory" buffer allocated by Gcrypt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/data/com.github.paolostivanin.OTPClient.appdata.xml new/OTPClient-2.6.2/data/com.github.paolostivanin.OTPClient.appdata.xml --- old/OTPClient-2.6.0/data/com.github.paolostivanin.OTPClient.appdata.xml 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/data/com.github.paolostivanin.OTPClient.appdata.xml 2022-07-06 08:51:34.000000000 +0200 @@ -83,6 +83,23 @@ </content_rating> <releases> + <release version="2.6.2" date="2022-07-06"> + <description> + <p>OTPClient 2.6.2 add an upgrade message</p> + <ul> + <li>warn user about new secret service behavior</li> + </ul> + </description> + </release> + <release version="2.6.1" date="2022-07-05"> + <description> + <p>OTPClient 2.6.1 some fixes and a new feature</p> + <ul> + <li>add ability to import Google migration QR also via webcam</li> + <li>fix double free in case of error</li> + </ul> + </description> + </release> <release version="2.6.0" date="2022-07-01"> <description> <p>OTPClient 2.6.0 brings lots of new features</p> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/src/add-from-qr.c new/OTPClient-2.6.2/src/add-from-qr.c --- old/OTPClient-2.6.0/src/add-from-qr.c 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/src/add-from-qr.c 2022-07-06 08:51:34.000000000 +0200 @@ -1,13 +1,14 @@ #include <gtk/gtk.h> #include <gcrypt.h> #include <glib/gstdio.h> -#include <baseencode.h> #include "imports.h" #include "qrcode-parser.h" #include "message-dialogs.h" #include "add-common.h" #include "get-builder.h" -#include "google-migration.pb-c.h" +#include "common/common.h" +#include "gui-common.h" + typedef struct gtimeout_data_t { GtkWidget *diag; @@ -32,8 +33,6 @@ GdkPixbuf *pixbuf, gpointer user_data); -static GSList *decode_migration_data (const gchar *uri); - void add_qr_from_file (GSimpleAction *simple, @@ -42,7 +41,7 @@ { const gchar *action_name = g_action_get_name (G_ACTION(simple)); gboolean google_migration; - (g_strcmp0 (action_name, GOOGLE_MIGRATION_ACTION_NAME) == 0) ? google_migration = TRUE : FALSE; + (g_strcmp0 (action_name, GOOGLE_MIGRATION_FILE_ACTION_NAME) == 0) ? google_migration = TRUE : FALSE; AppData *app_data = (AppData *)user_data; @@ -136,34 +135,12 @@ return; } - GSList *otpauth_decoded_uris = NULL; - if (google_migration == TRUE) { - gint failed = 0; - otpauth_decoded_uris = decode_migration_data (otpauth_uri); - for (gint i = 0; i < g_slist_length (otpauth_decoded_uris); i++) { - gchar *uri = g_slist_nth_data (otpauth_decoded_uris, i); - err_msg = add_data_to_db (uri, app_data); - if (err_msg != NULL) { - failed++; - g_free (err_msg); - } - } - if (failed > 0) { - GString *e_msg = g_string_new (NULL); - g_string_printf (e_msg, "Failed to add all OTPs. Only %u out of %u were successfully added.", g_slist_length (otpauth_decoded_uris) - failed, - g_slist_length (otpauth_decoded_uris)); - show_message_dialog (app_data->main_window, e_msg->str, GTK_MESSAGE_ERROR); - g_string_free (e_msg, TRUE); - } - g_slist_free_full (otpauth_decoded_uris, g_free); + err_msg = parse_uris_migration (app_data, otpauth_uri, google_migration); + if (err_msg != NULL) { + show_message_dialog (app_data->main_window, err_msg, GTK_MESSAGE_ERROR); + g_free (err_msg); } else { - err_msg = add_data_to_db (otpauth_uri, app_data); - if (err_msg != NULL) { - show_message_dialog (app_data->main_window, err_msg, GTK_MESSAGE_ERROR); - g_free (err_msg); - } else { - show_message_dialog (app_data->main_window, "QRCode successfully imported.", GTK_MESSAGE_INFO); - } + show_message_dialog (app_data->main_window, "QRCode successfully scanned", GTK_MESSAGE_INFO); } gcry_free (otpauth_uri); @@ -225,78 +202,4 @@ } else { show_message_dialog (app_data->main_window, "Couldn't get QR code image from clipboard", GTK_MESSAGE_ERROR); } -} - - -static 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; - guchar *data = g_base64_decode (g_uri_unescape_string ((encoded_uri_copy), NULL), &out_len); - - GSList *uris = NULL; - gchar *uri = NULL; - MigrationPayload *msg = migration_payload__unpack (NULL, out_len, data); - for (gint i = 0; i < msg->n_otp_parameters; i++) { - uri = g_strconcat ("otpauth://", NULL); - if (msg->otp_parameters[i]->type == 1) { - uri = g_strconcat (uri, "hotp/", NULL); - } else if (msg->otp_parameters[i]->type == 2) { - uri = g_strconcat (uri, "totp/", NULL); - } else { - g_printerr ("OTP type not recognized, skipping %s\n", msg->otp_parameters[i]->name); - goto end; - } - - uri = g_strconcat (uri, msg->otp_parameters[i]->name, "?", NULL); - - if (msg->otp_parameters[i]->algorithm == 1) { - uri = g_strconcat (uri, "algorithm=SHA1&", NULL); - } else if (msg->otp_parameters[i]->algorithm == 2) { - uri = g_strconcat (uri, "algorithm=SHA256&", NULL); - } else if (msg->otp_parameters[i]->algorithm == 3) { - uri = g_strconcat (uri, "algorithm=SHA512&", NULL); - } else { - g_printerr ("Algorithm type not supported, skipping %s\n", msg->otp_parameters[i]->name); - goto end; - } - - if (msg->otp_parameters[i]->digits == 1) { - uri = g_strconcat (uri, "digits=6&", NULL); - } else if (msg->otp_parameters[i]->digits == 2) { - uri = g_strconcat (uri, "digits=8&", NULL); - } else { - g_printerr ("Algorithm type not supported, skipping %s\n", msg->otp_parameters[i]->name); - goto end; - } - - if (msg->otp_parameters[i]->issuer != NULL) { - uri = g_strconcat (uri, "issuer=", msg->otp_parameters[i]->issuer, "&", NULL); - } - - if (msg->otp_parameters[i]->type == 1) { - uri = g_strconcat (uri, "counter=", msg->otp_parameters[i]->counter, "&", NULL); - } - - baseencode_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; - } - - uri = g_strconcat (uri, "secret=", b32_encoded_secret, NULL); - - uris = g_slist_append (uris, g_strdup (uri)); - - end: - g_free (uri); - } - - return uris; } \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/src/app.c new/OTPClient-2.6.2/src/app.c --- old/OTPClient-2.6.0/src/app.c 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/src/app.c 2022-07-06 08:51:34.000000000 +0200 @@ -36,6 +36,15 @@ gint height, AppData *app_data); +static gboolean show_upgrade_msg (void); + +static void set_info_bar (AppData *app_data, + const gchar *msg); + +static void on_bar_response (GtkInfoBar *ib, + gint response_id, + gpointer user_data); + static gboolean set_action_group (GtkBuilder *builder, AppData *app_data); @@ -272,6 +281,13 @@ app_data->source_id_last_activity = g_timeout_add_seconds (1, check_inactivity, app_data); gtk_widget_show_all (app_data->main_window); + + app_data->info_bar = GTK_WIDGET(gtk_builder_get_object (app_data->builder, "info_bar_id")); + if (show_upgrade_msg ()) { + set_info_bar (app_data, "Release <b>2.6.0</b>: please check the 'Secret Service Integration' new feature <a href=\"https://github.com/paolostivanin/OTPClient/wiki/How-to-use-OTPClient#secret-service-integration\">HERE</a>"); + } else { + gtk_widget_hide (app_data->info_bar); + } } @@ -432,6 +448,68 @@ static gboolean +show_upgrade_msg () +{ + gboolean show_msg = TRUE; + GKeyFile *kf = get_kf_ptr (); + if (kf != NULL) { + gchar *up_msg = g_key_file_get_string (kf, "config", "upgrade_msg", NULL); + if (up_msg == NULL) { + show_msg = TRUE; + } else { + show_msg = (g_strcmp0 (up_msg, "v2_6") == 0) ? FALSE : TRUE; + } + } + + g_key_file_free (kf); + + return show_msg; +} + + +static void +set_info_bar (AppData *app_data, + const gchar *msg) +{ + GtkWidget *label = GTK_WIDGET(gtk_builder_get_object (app_data->builder, "info_bar_label_id")); + + g_signal_connect (app_data->info_bar, "response", G_CALLBACK(on_bar_response), NULL); + + gtk_label_set_markup (GTK_LABEL(label), msg); + gtk_info_bar_set_message_type (GTK_INFO_BAR(app_data->info_bar), GTK_MESSAGE_INFO); + gtk_widget_show (app_data->info_bar); +} + + +static void +on_bar_response (GtkInfoBar *ib, + gint response_id __attribute__((unused)), + gpointer user_data __attribute__((unused))) +{ + GError *err = NULL; + GKeyFile *kf = get_kf_ptr (); + if (kf != NULL) { + g_key_file_set_string (kf, "config", "upgrade_msg", "v2_6"); + gchar *cfg_file_path; +#ifndef USE_FLATPAK_APP_FOLDER + 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 + g_key_file_save_to_file (kf, cfg_file_path, &err); + if (err != NULL) { + g_printerr ("%s\n", err->message); + } + g_free (cfg_file_path); + } + + g_key_file_free (kf); + + gtk_widget_hide (GTK_WIDGET(ib)); +} + + +static gboolean set_action_group (GtkBuilder *builder, AppData *app_data) { @@ -447,7 +525,8 @@ { .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 = GOOGLE_MIGRATION_ACTION_NAME, .activate = add_qr_from_file }, + { .name = GOOGLE_MIGRATION_FILE_ACTION_NAME, .activate = add_qr_from_file }, + { .name = GOOGLE_MIGRATION_WEBCAM_ACTION_NAME, .activate = webcam_cb }, { .name = "create_newdb", .activate = new_db_cb }, { .name = "change_db", .activate = change_db_cb }, { .name = "change_pwd", .activate = change_password_cb }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/src/common/aegis.c new/OTPClient-2.6.2/src/common/aegis.c --- old/OTPClient-2.6.0/src/common/aegis.c 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/src/common/aegis.c 2022-07-06 08:51:34.000000000 +0200 @@ -135,8 +135,6 @@ 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_free (key_nonce); - g_free (key_tag); gcry_cipher_close (hd); gcry_free (master_key); return NULL; @@ -146,8 +144,6 @@ gpg_err = gcry_cipher_checktag(hd, tag, TAG_SIZE); if (gpg_err != 0) { g_set_error (err, bad_tag_gquark (), BAD_TAG_ERRCODE, "Invalid TAG (database). Either the password is wrong or the file is corrupted."); - g_free (key_nonce); - g_free (key_tag); gcry_cipher_close (hd); gcry_free (master_key); g_free (decrypted_db); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/src/common/common.c new/OTPClient-2.6.2/src/common/common.c --- old/OTPClient-2.6.0/src/common/common.c 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/src/common/common.c 2022-07-06 08:51:34.000000000 +0200 @@ -1,8 +1,10 @@ #include <glib.h> #include <sys/resource.h> #include <cotp.h> +#include <baseencode.h> #include "gcrypt.h" #include "jansson.h" +#include "../google-migration.pb-c.h" gint32 get_max_file_size_from_memlock (void) @@ -165,3 +167,77 @@ return result; } + + +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; + guchar *data = g_base64_decode (g_uri_unescape_string ((encoded_uri_copy), NULL), &out_len); + + GSList *uris = NULL; + gchar *uri = NULL; + MigrationPayload *msg = migration_payload__unpack (NULL, out_len, data); + for (gint i = 0; i < msg->n_otp_parameters; i++) { + uri = g_strconcat ("otpauth://", NULL); + if (msg->otp_parameters[i]->type == 1) { + uri = g_strconcat (uri, "hotp/", NULL); + } else if (msg->otp_parameters[i]->type == 2) { + uri = g_strconcat (uri, "totp/", NULL); + } else { + g_printerr ("OTP type not recognized, skipping %s\n", msg->otp_parameters[i]->name); + goto end; + } + + uri = g_strconcat (uri, msg->otp_parameters[i]->name, "?", NULL); + + if (msg->otp_parameters[i]->algorithm == 1) { + uri = g_strconcat (uri, "algorithm=SHA1&", NULL); + } else if (msg->otp_parameters[i]->algorithm == 2) { + uri = g_strconcat (uri, "algorithm=SHA256&", NULL); + } else if (msg->otp_parameters[i]->algorithm == 3) { + uri = g_strconcat (uri, "algorithm=SHA512&", NULL); + } else { + g_printerr ("Algorithm type not supported, skipping %s\n", msg->otp_parameters[i]->name); + goto end; + } + + if (msg->otp_parameters[i]->digits == 1) { + uri = g_strconcat (uri, "digits=6&", NULL); + } else if (msg->otp_parameters[i]->digits == 2) { + uri = g_strconcat (uri, "digits=8&", NULL); + } else { + g_printerr ("Algorithm type not supported, skipping %s\n", msg->otp_parameters[i]->name); + goto end; + } + + if (msg->otp_parameters[i]->issuer != NULL) { + uri = g_strconcat (uri, "issuer=", msg->otp_parameters[i]->issuer, "&", NULL); + } + + if (msg->otp_parameters[i]->type == 1) { + uri = g_strconcat (uri, "counter=", msg->otp_parameters[i]->counter, "&", NULL); + } + + baseencode_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; + } + + uri = g_strconcat (uri, "secret=", b32_encoded_secret, NULL); + + uris = g_slist_append (uris, g_strdup (uri)); + + end: + g_free (uri); + } + + return uris; +} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/src/common/common.h new/OTPClient-2.6.2/src/common/common.h --- old/OTPClient-2.6.0/src/common/common.h 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/src/common/common.h 2022-07-06 08:51:34.000000000 +0200 @@ -31,4 +31,6 @@ gchar *bytes_to_hexstr (const guchar *data, size_t datalen); +GSList *decode_migration_data (const gchar *encoded_uri); + G_END_DECLS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/src/data.h new/OTPClient-2.6.2/src/data.h --- old/OTPClient-2.6.0/src/data.h 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/src/data.h 2022-07-06 08:51:34.000000000 +0200 @@ -31,6 +31,7 @@ GtkBuilder *builder; GtkWidget *main_window; + GtkWidget *info_bar; GtkTreeView *tree_view; GtkClipboard *clipboard; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/src/gui-common.c new/OTPClient-2.6.2/src/gui-common.c --- old/OTPClient-2.6.0/src/gui-common.c 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/src/gui-common.c 2022-07-06 08:51:34.000000000 +0200 @@ -1,5 +1,8 @@ #include <gtk/gtk.h> #include <jansson.h> +#include "message-dialogs.h" +#include "add-common.h" +#include "common/common.h" void @@ -61,3 +64,37 @@ { gtk_dialog_response (GTK_DIALOG(gtk_widget_get_toplevel (entry)), GTK_RESPONSE_OK); } + + +gchar * +parse_uris_migration (AppData *app_data, + const gchar *user_uri, + gboolean google_migration) +{ + gchar *return_err_msg = NULL; + GSList *otpauth_decoded_uris = NULL; + if (google_migration == TRUE) { + gint failed = 0; + otpauth_decoded_uris = decode_migration_data (user_uri); + for (gint i = 0; i < g_slist_length (otpauth_decoded_uris); i++) { + gchar *uri = g_slist_nth_data (otpauth_decoded_uris, i); + gchar *err_msg = add_data_to_db (uri, app_data); + if (err_msg != NULL) { + failed++; + g_free (err_msg); + } + } + if (failed > 0) { + GString *e_msg = g_string_new (NULL); + g_string_printf (e_msg, "Failed to add all OTPs. Only %u out of %u were successfully added.", g_slist_length (otpauth_decoded_uris) - failed, + g_slist_length (otpauth_decoded_uris)); + return_err_msg = g_strdup (e_msg->str); + g_string_free (e_msg, TRUE); + } + g_slist_free_full (otpauth_decoded_uris, g_free); + } else { + return_err_msg = add_data_to_db (user_uri, app_data); + } + + return return_err_msg; +} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/src/gui-common.h new/OTPClient-2.6.2/src/gui-common.h --- old/OTPClient-2.6.0/src/gui-common.h 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/src/gui-common.h 2022-07-06 08:51:34.000000000 +0200 @@ -2,27 +2,32 @@ #include <gtk/gtk.h> #include <jansson.h> +#include "data.h" 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); +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); G_END_DECLS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/src/imports.h new/OTPClient-2.6.2/src/imports.h --- old/OTPClient-2.6.0/src/imports.h 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/src/imports.h 2022-07-06 08:51:34.000000000 +0200 @@ -11,7 +11,8 @@ #define FREEOTPPLUS_IMPORT_ACTION_NAME "import_freeotpplus" #define AEGIS_IMPORT_ACTION_NAME "import_aegis" #define AEGIS_IMPORT_ENC_ACTION_NAME "import_aegis_enc" -#define GOOGLE_MIGRATION_ACTION_NAME "import_qr_google_migration_file" +#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; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/src/ui/otpclient.ui new/OTPClient-2.6.2/src/ui/otpclient.ui --- old/OTPClient-2.6.0/src/ui/otpclient.ui 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/src/ui/otpclient.ui 2022-07-06 08:51:34.000000000 +0200 @@ -1863,12 +1863,13 @@ </packing> </child> <child> - <object class="GtkModelButton" id="import_google_migration_qr_btn_id"> + <object class="GtkModelButton" id="import_google_qr_model_btn_id"> <property name="visible">True</property> <property name="can-focus">True</property> <property name="receives-default">True</property> - <property name="action-name">settings_menu.import_qr_google_migration_file</property> <property name="text" translatable="yes">Google Migration QR</property> + <property name="menu-name">import_google_qr_menu</property> + <accelerator key="g" signal="activate" modifiers="GDK_CONTROL_MASK"/> </object> <packing> <property name="expand">False</property> @@ -2092,6 +2093,45 @@ <property name="position">3</property> </packing> </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkModelButton" id="import_google_qr_file_btn_id"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="action-name">settings_menu.import_google_qr_file</property> + <property name="text" translatable="yes">From file</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkModelButton" id="import_google_qr_webcam_btn_id"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="action-name">settings_menu.import_google_qr_webcam</property> + <property name="text" translatable="yes">From webcam</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="submenu">import_google_qr_menu</property> + <property name="position">4</property> + </packing> + </child> </object> <object class="GtkApplicationWindow" id="appwindow_id"> <property name="can-focus">False</property> @@ -2100,23 +2140,93 @@ <property name="default-height">350</property> <property name="gravity">center</property> <child> - <object class="GtkScrolledWindow" id="scrolledwin_id"> + <object class="GtkBox"> <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - <property name="shadow-type">etched-in</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkInfoBar" id="info_bar_id"> + <property name="visible">False</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <child internal-child="action_area"> + <object class="GtkButtonBox"> + <property name="can-focus">False</property> + <property name="spacing">6</property> + <property name="layout-style">end</property> + <child> + <object class="GtkButton" id="info_bar_ok_btn_id"> + <property name="label" translatable="yes">OK</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child internal-child="content_area"> + <object class="GtkBox"> + <property name="can-focus">False</property> + <property name="spacing">16</property> + <child> + <object class="GtkLabel" id="info_bar_label_id"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">label</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <action-widgets> + <action-widget response="-5">info_bar_ok_btn_id</action-widget> + </action-widgets> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> <child> - <object class="GtkTreeView" id="treeview_id"> + <object class="GtkScrolledWindow" id="scrolledwin_id"> <property name="visible">True</property> <property name="can-focus">True</property> <property name="hexpand">True</property> <property name="vexpand">True</property> - <property name="headers-clickable">False</property> - <property name="enable-grid-lines">horizontal</property> - <property name="activate-on-single-click">True</property> - <child internal-child="selection"> - <object class="GtkTreeSelection"/> + <property name="shadow-type">etched-in</property> + <child> + <object class="GtkTreeView" id="treeview_id"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="headers-clickable">False</property> + <property name="enable-grid-lines">horizontal</property> + <property name="activate-on-single-click">True</property> + <child internal-child="selection"> + <object class="GtkTreeSelection"/> + </child> + </object> </child> </object> </child> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-2.6.0/src/webcam-add-cb.c new/OTPClient-2.6.2/src/webcam-add-cb.c --- old/OTPClient-2.6.0/src/webcam-add-cb.c 2022-07-01 14:43:11.000000000 +0200 +++ new/OTPClient-2.6.2/src/webcam-add-cb.c 2022-07-06 08:51:34.000000000 +0200 @@ -7,6 +7,7 @@ #include "add-common.h" #include "get-builder.h" #include "common/common.h" +#include "gui-common.h" typedef struct config_data_t { @@ -28,6 +29,10 @@ GVariant *parameter __attribute__((unused)), 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) ? google_migration = TRUE : FALSE; + AppData *app_data = (AppData *)user_data; ConfigData *cfg_data = g_new0 (ConfigData, 1); @@ -59,7 +64,7 @@ if (response == GTK_RESPONSE_CANCEL) { if (cfg_data->qrcode_found) { zbar_processor_destroy (proc); - gchar *err_msg = add_data_to_db (cfg_data->otp_uri, app_data); + gchar *err_msg = parse_uris_migration (app_data, cfg_data->otp_uri, google_migration); if (err_msg != NULL) { show_message_dialog (app_data->main_window, err_msg, GTK_MESSAGE_ERROR); g_free (err_msg);