[balsa] Autocrypt improvements



commit da4d141549b0fbcc8741f82688daa02327cc8645
Author: Albrecht Dreß <albrecht dress arcor de>
Date:   Sun Jan 31 17:49:10 2021 +0100

    Autocrypt improvements
    
    This change adds the following improvements to Autocrypt support:
    1. add a context menu to the Autocrypt database dialogue, with options
       to view the key details (replacing the double-click), and to delete a
       key.  The context menu can be opened as usual (right click, Sh-F10);
    2. enable sorting by address or time stamps in the database dialogue;
    3. display a warning if a message contains a broken Autocrypt header, or
       if the Autocrypt header contains a different key than the one used
       for signing the message.
    
    Changes:
    - libbalsa/autocrypt.c:(global) add SQLite query for deleting a key; add
      list store columns for raw time stamps; autocrypt_db_dialog_run():
      drop row-activated signal connection, connect gesture pressed and
      popup-menu signals, make columns sortable; remove obsolete function
      row_activated_cb(); implement callbacks for pressed, popup-menu, key
      details display and key removal; extract_ac_keydata(): remove unused
      parameter
    - src/balsa-mime-widget-crypto.c: balsa_mime_widget_signature_widget():
      implement #3
    
    Signed-off-by: Albrecht Dreß <albrecht dress arcor de>

 libbalsa/autocrypt.c           | 231 ++++++++++++++++++++++++++++++++++-------
 src/balsa-mime-widget-crypto.c |   4 +
 2 files changed, 197 insertions(+), 38 deletions(-)
---
diff --git a/libbalsa/autocrypt.c b/libbalsa/autocrypt.c
index 4dacc2cfb..c5d2ee1c4 100644
--- a/libbalsa/autocrypt.c
+++ b/libbalsa/autocrypt.c
@@ -65,7 +65,7 @@
                "prefer_encrypt BOOLEAN DEFAULT 0);"
 
 
-#define NUM_QUERIES                                                            6U
+#define NUM_QUERIES                                                            7U
 
 
 struct _AutocryptData {
@@ -83,7 +83,9 @@ typedef struct _AutocryptData AutocryptData;
 
 enum {
        AC_ADDRESS_COLUMN = 0,
+       AC_LAST_SEEN_INT_COLUMN,
        AC_LAST_SEEN_COLUMN,
+       AC_TIMESTAMP_INT_COLUMN,
        AC_TIMESTAMP_COLUMN,
        AC_PREFER_ENCRYPT_COLUMN,
        AC_KEY_PTR_COLUMN,
@@ -101,8 +103,7 @@ typedef struct {
 
 static void autocrypt_close(void);
 static gboolean extract_ac_keydata(GMimeAutocryptHeader  *autocrypt_header,
-                                                                  ac_key_data_t         *dest,
-                                                                  GError               **error);
+                                                                  ac_key_data_t         *dest);
 static void add_or_update_user_info(GMimeAutocryptHeader    *autocrypt_header,
                                                                        const ac_key_data_t     *ac_key_data,
                                                                        gboolean                 update,
@@ -118,14 +119,24 @@ static AutocryptRecommend autocrypt_check_ia_list(gpgme_ctx_t           gpgme_ct
                                                                                                  time_t      
          ref_time,
                                                                                                  GList       
        **missing_keys,
                                                                                                  GError      
        **error);
-static void row_activated_cb(GtkTreeView       *tree_view,
-                             GtkTreePath       *path,
-                             GtkTreeViewColumn *column,
-                             gpointer           user_data);
+
+static gboolean popup_menu_cb(GtkWidget *widget,
+                                                         gpointer   user_data);
+static void button_press_cb(GtkGestureMultiPress *multi_press_gesture,
+                                                       gint                  n_press,
+                                                       gdouble               x,
+                                                       gdouble               y,
+                                                       gpointer              user_data);
+static void popup_menu_real(GtkWidget      *widget,
+                                                       const GdkEvent *event);
+static void show_key_details_cb(GtkMenuItem *menuitem,
+                                                               gpointer     user_data);
+static void remove_key_cb(GtkMenuItem *menuitem,
+                                                 gpointer     user_data);
 
 
 static sqlite3 *autocrypt_db = NULL;
-static sqlite3_stmt *query[NUM_QUERIES] = { NULL, NULL, NULL, NULL, NULL, NULL };
+static sqlite3_stmt *query[NUM_QUERIES] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
 G_LOCK_DEFINE_STATIC(db_mutex);
 
 
@@ -140,7 +151,8 @@ autocrypt_init(GError **error)
                " expires = ?5, prefer_encrypt = ?6 WHERE addr = LOWER(?1)",
                "UPDATE autocrypt SET last_seen = ?2 WHERE addr = LOWER(?1) AND last_seen < ?2 AND 
ac_timestamp < ?2",
                "SELECT pubkey FROM autocrypt WHERE fingerprint LIKE ?",
-               "SELECT addr, last_seen, ac_timestamp, prefer_encrypt, pubkey FROM autocrypt ORDER BY addr 
ASC"
+               "SELECT addr, last_seen, ac_timestamp, prefer_encrypt, pubkey FROM autocrypt ORDER BY addr 
ASC",
+               "DELETE FROM autocrypt WHERE addr = LOWER(?1)"
        };
        gboolean result;
 
@@ -237,7 +249,7 @@ autocrypt_from_message(LibBalsaMessage  *message,
 
     /* update the database */
     G_LOCK(db_mutex);
-    if (extract_ac_keydata(headers->autocrypt_hdr, &ac_key_data, error)) {
+    if (extract_ac_keydata(headers->autocrypt_hdr, &ac_key_data)) {
        AutocryptData *db_info;
 
        db_info = autocrypt_user_info(g_mime_autocrypt_header_get_address_as_string(headers->autocrypt_hdr), 
error);
@@ -422,13 +434,13 @@ autocrypt_db_dialog_run(const gchar *date_string, GtkWindow *parent)
 {
        GtkWidget *dialog;
        GtkWidget *vbox;
-    GtkWidget *label;
     GtkWidget *scrolled_window;
     GtkWidget *tree_view;
     GtkListStore *model;
     GtkTreeSelection *selection;
        GtkCellRenderer *renderer;
        GtkTreeViewColumn *column;
+       GtkGesture *gesture;
     GList *keys = NULL;
        int sqlite_res;
 
@@ -438,25 +450,29 @@ autocrypt_db_dialog_run(const gchar *date_string, GtkWindow *parent)
 
     vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
     gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), vbox);
-    gtk_widget_set_vexpand (vbox, TRUE);
-    label = gtk_label_new(_("Double-click key to show details"));
-    gtk_widget_set_halign(label, GTK_ALIGN_START);
-    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
+    gtk_widget_set_vexpand(vbox, TRUE);
 
     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+    gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 12U);
     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), GTK_SHADOW_ETCHED_IN);
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, 
GTK_POLICY_AUTOMATIC);
     gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
 
     model = gtk_list_store_new(AC_DB_VIEW_COLUMNS, G_TYPE_STRING,      /* address */
+       G_TYPE_INT64,                                                                                         
  /* last seen timestamp value (for sorting) */
        G_TYPE_STRING,                                                                                        
  /* formatted last seen timestamp */
+               G_TYPE_INT64,                                                                                 
          /* last Autocrypt message timestamp value (for sorting) */
                G_TYPE_STRING,                                                                                
          /* formatted last Autocrypt message timestamp */
                G_TYPE_BOOLEAN,                                                                               
          /* user prefers encrypted messages */
                G_TYPE_POINTER);                                                                              
          /* key */
 
     tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
 
-    g_signal_connect(tree_view, "row-activated", G_CALLBACK(row_activated_cb), dialog);
+    gesture = gtk_gesture_multi_press_new(tree_view);
+    gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), 0);
+    g_signal_connect(gesture, "pressed", G_CALLBACK(button_press_cb), NULL);
+    gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(gesture), GTK_PHASE_CAPTURE);
+    g_signal_connect(tree_view, "popup-menu", G_CALLBACK(popup_menu_cb), NULL);
 
     gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view);
     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
@@ -465,20 +481,26 @@ autocrypt_db_dialog_run(const gchar *date_string, GtkWindow *parent)
     /* add the keys */
     sqlite_res = sqlite3_step(query[5]);
     while (sqlite_res == SQLITE_ROW) {
+       gint64 last_seen_val;
        gchar *last_seen_buf;
+       gint64 last_ac_val;
        gchar *last_ac_buf;
        GBytes *key;
         GtkTreeIter iter;
 
-       last_seen_buf = libbalsa_date_to_utf8(sqlite3_column_int64(query[5], 1), date_string);
-       last_ac_buf = libbalsa_date_to_utf8(sqlite3_column_int64(query[5], 2), date_string);
+        last_seen_val = sqlite3_column_int64(query[5], 1);
+       last_seen_buf = libbalsa_date_to_utf8(last_seen_val, date_string);
+       last_ac_val = sqlite3_column_int64(query[5], 2);
+       last_ac_buf = libbalsa_date_to_utf8(last_ac_val, date_string);
        key = g_bytes_new(sqlite3_column_blob(query[5], 4), sqlite3_column_bytes(query[5], 4));
        keys = g_list_prepend(keys, key);
 
                gtk_list_store_append(model, &iter);
                gtk_list_store_set(model, &iter,
                        AC_ADDRESS_COLUMN, sqlite3_column_text(query[5], 0),
+                       AC_LAST_SEEN_INT_COLUMN, last_seen_val,
                        AC_LAST_SEEN_COLUMN, last_seen_buf,
+                       AC_TIMESTAMP_INT_COLUMN, last_ac_val,
                        AC_TIMESTAMP_COLUMN, last_ac_buf,
                        AC_PREFER_ENCRYPT_COLUMN, sqlite3_column_int(query[5], 3),
                        AC_KEY_PTR_COLUMN, key,
@@ -491,29 +513,33 @@ autocrypt_db_dialog_run(const gchar *date_string, GtkWindow *parent)
     sqlite3_reset(query[5]);
 
     /* set up the tree view */
-    g_object_unref(model);
-
        renderer = gtk_cell_renderer_text_new();
-       column = gtk_tree_view_column_new_with_attributes(_("Mailbox"), renderer, "text", 0, NULL);
+       column = gtk_tree_view_column_new_with_attributes(_("Mailbox"), renderer, "text", AC_ADDRESS_COLUMN, 
NULL);
+       gtk_tree_view_column_set_sort_column_id(column, AC_ADDRESS_COLUMN);
        gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
        gtk_tree_view_column_set_resizable(column, TRUE);
 
        renderer = gtk_cell_renderer_text_new();
-       column = gtk_tree_view_column_new_with_attributes(_("Last seen"), renderer, "text", 1, NULL);
+       column = gtk_tree_view_column_new_with_attributes(_("Last seen"), renderer, "text", 
AC_LAST_SEEN_COLUMN, NULL);
+       gtk_tree_view_column_set_sort_column_id(column, AC_LAST_SEEN_INT_COLUMN);
        gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
        gtk_tree_view_column_set_resizable(column, TRUE);
 
        renderer = gtk_cell_renderer_text_new();
-       column = gtk_tree_view_column_new_with_attributes(_("Last Autocrypt message"), renderer, "text", 2, 
NULL);
+       column = gtk_tree_view_column_new_with_attributes(_("Last Autocrypt message"), renderer, "text", 
AC_TIMESTAMP_COLUMN, NULL);
+       gtk_tree_view_column_set_sort_column_id(column, AC_TIMESTAMP_INT_COLUMN);
        gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
        gtk_tree_view_column_set_resizable(column, TRUE);
 
        renderer = gtk_cell_renderer_toggle_new();
-       column = gtk_tree_view_column_new_with_attributes(_("Prefer encryption"), renderer, "active", 3, 
NULL);
+       column = gtk_tree_view_column_new_with_attributes(_("Prefer encryption"), renderer, "active", 
AC_PREFER_ENCRYPT_COLUMN, NULL);
        gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
        gtk_tree_view_column_set_resizable(column, TRUE);
        gtk_widget_show_all(vbox);
 
+       gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), AC_ADDRESS_COLUMN, GTK_SORT_ASCENDING);
+    g_object_unref(model);
+
        (void) gtk_dialog_run(GTK_DIALOG(dialog));
        gtk_widget_destroy(dialog);
        g_list_free_full(keys, (GDestroyNotify) g_bytes_unref);
@@ -664,7 +690,7 @@ autocrypt_user_info(const gchar *mailbox, GError **error)
 
 
 static gboolean
-extract_ac_keydata(GMimeAutocryptHeader *autocrypt_header, ac_key_data_t *dest, GError **error)
+extract_ac_keydata(GMimeAutocryptHeader *autocrypt_header, ac_key_data_t *dest)
 {
        GBytes *keydata;
        gboolean success = FALSE;
@@ -773,18 +799,99 @@ update_last_seen(GMimeAutocryptHeader *autocrypt_header, GError **error)
 }
 
 
+/* callback: popup menu key in autocrypt database dialogue activated */
+static gboolean
+popup_menu_cb(GtkWidget *widget, gpointer G_GNUC_UNUSED user_data)
+{
+    GtkTreeView *tree_view = GTK_TREE_VIEW(widget);
+    GtkTreeModel *model;
+    GtkTreeSelection *selection;
+    GtkTreeIter iter;
+
+       selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
+       if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+               GtkTreePath *path;
+
+               path = gtk_tree_model_get_path(model, &iter);
+               gtk_tree_view_scroll_to_cell(tree_view, path, NULL, FALSE, 0.0, 0.0);
+               gtk_tree_path_free(path);
+               popup_menu_real(widget, NULL);
+       }
+
+       return TRUE;
+}
+
+
+/* callback: mouse click in autocrypt database dialogue activated */
+static void
+button_press_cb(GtkGestureMultiPress *multi_press_gesture, gint G_GNUC_UNUSED n_press, gdouble x, gdouble y,
+       gpointer G_GNUC_UNUSED user_data)
+{
+    GtkWidget *widget = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(multi_press_gesture));
+    GtkTreeView *tree_view = GTK_TREE_VIEW(widget);
+    GtkGesture *gesture;
+    GdkEventSequence *sequence;
+    const GdkEvent *event;
+
+    gesture = GTK_GESTURE(multi_press_gesture);
+    sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(multi_press_gesture));
+    event = gtk_gesture_get_last_event(gesture, sequence);
+    if (gdk_event_triggers_context_menu(event) && (gdk_event_get_window(event) == 
gtk_tree_view_get_bin_window(tree_view))) {
+        gint bx;
+        gint by;
+        GtkTreePath *path;
+
+        gtk_tree_view_convert_widget_to_bin_window_coords(tree_view, (gint) x, (gint) y, &bx, &by);
+        if (gtk_tree_view_get_path_at_pos(tree_view, bx, by, &path, NULL, NULL, NULL)) {
+               GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view);
+            GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
+            GtkTreeIter iter;
+
+            gtk_tree_selection_unselect_all(selection);
+            gtk_tree_selection_select_path(selection, path);
+            gtk_tree_view_set_cursor(GTK_TREE_VIEW(tree_view), path, NULL, FALSE);
+            if (gtk_tree_model_get_iter(model, &iter, path)) {
+               popup_menu_real(GTK_WIDGET(tree_view), event);
+            }
+            gtk_tree_path_free(path);
+        }
+    }
+}
+
+
+/* autocrypt database dialogue context menu */
 static void
-row_activated_cb(GtkTreeView       *tree_view,
-                 GtkTreePath       *path,
-                 GtkTreeViewColumn *column,
-                 gpointer           user_data)
+popup_menu_real(GtkWidget *widget, const GdkEvent *event)
+{
+    GtkWidget *popup_menu;
+    GtkWidget* menu_item;
+
+       popup_menu = gtk_menu_new();
+    menu_item = gtk_menu_item_new_with_mnemonic(_("_Show details…"));
+       g_signal_connect(menu_item, "activate", G_CALLBACK(show_key_details_cb), widget);
+    gtk_menu_shell_append(GTK_MENU_SHELL(popup_menu), menu_item);
+    menu_item = gtk_menu_item_new_with_mnemonic(_("_Delete"));
+    g_signal_connect(menu_item, "activate", G_CALLBACK(remove_key_cb), widget);
+    gtk_menu_shell_append(GTK_MENU_SHELL(popup_menu), menu_item);
+    gtk_widget_show_all(popup_menu);
+    if (event != NULL) {
+       gtk_menu_popup_at_pointer(GTK_MENU(popup_menu), event);
+    } else {
+        gtk_menu_popup_at_widget(GTK_MENU(popup_menu), widget, GDK_GRAVITY_CENTER, GDK_GRAVITY_CENTER, NULL);
+    }
+}
+
+
+/* key context menu callback: show key details */
+static void
+show_key_details_cb(GtkMenuItem G_GNUC_UNUSED *menuitem, gpointer user_data)
 {
     GtkTreeModel *model;
+    GtkTreeSelection *selection;
     GtkTreeIter iter;
 
-    /* note: silently ignore all errors below... */
-    model = gtk_tree_view_get_model(tree_view);
-    if (gtk_tree_model_get_iter(model, &iter, path)) {
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(user_data));
+       if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
                gpgme_ctx_t ctx;
 
                ctx = libbalsa_gpgme_new_with_proto(GPGME_PROTOCOL_OpenPGP, NULL, NULL, NULL);
@@ -793,30 +900,78 @@ row_activated_cb(GtkTreeView       *tree_view,
 
                        if (libbalsa_mktempdir(&temp_dir)) {
                                GBytes *key;
+                               gchar *mail_addr;
                                GList *keys = NULL;
                                gboolean success;
 
-                               gtk_tree_model_get(model, &iter, AC_KEY_PTR_COLUMN, &key, -1);
+                               gtk_tree_model_get(model, &iter, AC_KEY_PTR_COLUMN, &key, AC_ADDRESS_COLUMN, 
&mail_addr, -1);
                                success = libbalsa_gpgme_ctx_set_home(ctx, temp_dir, NULL) &&
                                        libbalsa_gpgme_import_bin_key(ctx, key, NULL, NULL) &&
                                        libbalsa_gpgme_list_keys(ctx, &keys, NULL, NULL, FALSE, FALSE, TRUE, 
NULL);
-                               if (success && (keys != NULL)) {
-                               GtkWindow *window = user_data;
+                               if (success) {
+                                       GtkWidget *toplevel;
+                               GtkWindow *window;
                                GtkWidget *dialog;
 
-                               dialog = libbalsa_key_dialog(window, GTK_BUTTONS_CLOSE, (gpgme_key_t) 
keys->data, GPG_SUBKEY_CAP_ALL,
-                                       NULL, NULL);
+                               toplevel = gtk_widget_get_toplevel(GTK_WIDGET(user_data));
+                               window = GTK_IS_WINDOW(toplevel) ? GTK_WINDOW(toplevel) : NULL;
+                               if (keys != NULL) {
+                                       dialog = libbalsa_key_dialog(window, GTK_BUTTONS_CLOSE, (gpgme_key_t) 
keys->data, GPG_SUBKEY_CAP_ALL,
+                                               NULL, NULL);
+                               } else {
+                                       dialog = gtk_message_dialog_new(window, 
GTK_DIALOG_DESTROY_WITH_PARENT | libbalsa_dialog_flags(),
+                                               GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _("The database entry 
for “%s” does not contain a key."),
+                                                       mail_addr);
+                               }
                                (void) gtk_dialog_run(GTK_DIALOG(dialog));
                                gtk_widget_destroy(dialog);
                                g_list_free_full(keys, (GDestroyNotify) gpgme_key_release);
                                }
                                libbalsa_delete_directory_contents(temp_dir);
                                g_rmdir(temp_dir);
+                               g_free(mail_addr);
                        }
 
                        gpgme_release(ctx);
                }
-    }
+       }
 }
 
+
+/* key context menu callback: remove key from database */
+static void
+remove_key_cb(GtkMenuItem G_GNUC_UNUSED *menuitem, gpointer user_data)
+{
+    GtkTreeModel *model;
+    GtkTreeSelection *selection;
+    GtkTreeIter iter;
+
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(user_data));
+       if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+               GtkWidget *toplevel;
+       GtkWindow *window;
+               GtkWidget *dialog;
+               gchar *mail_addr;
+
+       toplevel = gtk_widget_get_toplevel(GTK_WIDGET(user_data));
+       window = GTK_IS_WINDOW(toplevel) ? GTK_WINDOW(toplevel) : NULL;
+       gtk_tree_model_get(model, &iter, AC_ADDRESS_COLUMN, &mail_addr, -1);
+               dialog = gtk_message_dialog_new(window, GTK_DIALOG_DESTROY_WITH_PARENT | 
libbalsa_dialog_flags(),
+                       GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("Delete the Autocrypt key for “%s” from 
the database?"), mail_addr);
+               if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES) {
+                       if ((sqlite3_bind_text(query[6], 1, mail_addr, -1, SQLITE_STATIC) != SQLITE_OK) ||
+                               (sqlite3_step(query[6]) != SQLITE_DONE)) {
+                               g_warning("deleting database entry for \"%s\" failed: %s", mail_addr, 
sqlite3_errmsg(autocrypt_db));
+                       } else {
+                               g_debug("deleted database entry for \"%s\"", mail_addr);
+                       }
+                       sqlite3_reset(query[6]);
+                       gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+               }
+               gtk_widget_destroy(dialog);
+               g_free(mail_addr);
+       }
+}
+
+
 #endif  /* ENABLE_AUTOCRYPT */
diff --git a/src/balsa-mime-widget-crypto.c b/src/balsa-mime-widget-crypto.c
index ce11f0992..71878363a 100644
--- a/src/balsa-mime-widget-crypto.c
+++ b/src/balsa-mime-widget-crypto.c
@@ -143,6 +143,10 @@ balsa_mime_widget_signature_widget(LibBalsaMessageBody * mime_body,
                        g_object_set_data_full(G_OBJECT(button), "autocrypt_key", autocrypt_key, 
(GDestroyNotify) g_bytes_unref);
                        g_signal_connect(button, "clicked", G_CALLBACK(on_key_import_button), NULL);
                        gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+               } else if (libbalsa_message_get_headers(mime_body->message)->autocrypt_hdr != NULL) {
+                       libbalsa_information(LIBBALSA_INFORMATION_WARNING,
+                               _("The message contains an Autocrypt header, but it is either broken "
+                                 "or the signature has been created using a different key."));
                }
 #endif
             button = gtk_button_new_with_mnemonic(_("_Search key server for this key"));


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]